Systemd service tự viết — chạy app Node/Python 24/7

Tài liệu » Quản trị VPS - Server » Systemd service tự viết — chạy app Node/Python 24/7

Vì sao cần Systemd service tự viết ngay?

Nhiều người mới dùng VPS thường chạy app bằng lệnh trực tiếp hoặc dùng screen/tmux. Tuy nhiên, cách này bộc lộ rất nhiều điểm yếu chí mạng khi đưa lên môi trường thực tế. Bạn bắt buộc phải chuyển sang dùng Systemd vì những lý do sau:

  • 🔴 App chết khi đóng SSH: Lệnh chạy trực tiếp gắn liền với session terminal của bạn.
  • 🔴 Không tự khởi động lại: Nếu app crash do lỗi code hoặc hết RAM, nó sẽ chết luôn cho đến khi bạn bật lại.
  • 🔴 Server reboot là sập: Khi VPS khởi động lại do bảo trì, app của bạn không tự chạy lên được.
  • 🟢 Quản lý log tập trung: Systemd tích hợp sẵn journalctl giúp lưu trữ và xoay vòng log tự động.
  • 🟢 Chuẩn mực công nghiệp: Không cần cài thêm PM2 hay Supervisor, tiết kiệm tài nguyên cho VPS nhỏ.

Hiểu về cấu trúc file .service của Systemd

Tóm gọn: File cấu hình của Systemd là một file text đơn giản chia làm 3 phần chính: [Unit], [Service][Install].

Vị trí lưu trữ file service

Trên Ubuntu, các file service do người dùng tự tạo phải được đặt ở một thư mục cụ thể. Thư mục chuẩn nhất để lưu trữ là /etc/systemd/system/. Bạn cần quyền root hoặc sudo để tạo và chỉnh sửa file tại đây.

Cấu trúc 3 phần cơ bản

Một file .service luôn tuân theo định dạng INI. Dưới đây là cấu trúc khung mà bạn sẽ dùng cho mọi ứng dụng.

[Unit]
Description=Mô tả ngắn về ứng dụng của bạn
After=network.target

[Service]
Type=simple
User=tên_user_chạy_app
WorkingDirectory=/đường/dẫn/tuyệt/đối/tới/thư/mục/app
ExecStart=/đường/dẫn/tuyệt/đối/tới/lệnh/chạy/app
Restart=on-failure

[Install]
WantedBy=multi-user.target
  • After=network.target: Đảm bảo mạng internet đã sẵn sàng trước khi bật app.
  • Type=simple: Loại service mặc định, Systemd coi service đã khởi động ngay khi lệnh ExecStart được gọi.
  • Restart=on-failure: Tự động khởi động lại nếu app bị crash (exit code khác 0).
  • WantedBy=multi-user.target: Cho phép service khởi động cùng hệ thống khi VPS boot vào chế độ đa người dùng.

💡 Mẹo: Luôn sử dụng đường dẫn tuyệt đối (Absolute paths) trong file Systemd. Systemd không hiểu các biến môi trường như $HOME hay dấu ngã ~ trừ khi bạn cấu hình đặc biệt.

Thực hành: Viết Systemd Service cho ứng dụng Node.js

Tóm gọn: Để chạy Node.js bằng Systemd, bạn cần xác định chính xác đường dẫn của file thực thi node và đường dẫn tới file code của bạn.

Bước 1: Chuẩn bị ứng dụng Node.js mẫu

Giả sử bạn có một ứng dụng Express.js đặt tại /opt/node-app/. File chạy chính là /opt/node-app/server.js. Trước tiên, hãy tìm đường dẫn tuyệt đối của Node.js trên VPS của bạn.

which node
/usr/bin/node

Bước 2: Tạo và cấu hình file .service

Tạo một file mới tên là myapp.service trong thư mục của Systemd. Sử dụng trình soạn thảo nano hoặc vim.

sudo nano /etc/systemd/system/myapp.service

Dán đoạn cấu hình sau vào file. Chú ý thay đổi User thành user thực tế trên VPS của bạn (ví dụ: ubuntu hoặc root).

[Unit]
Description=Node.js Express Application
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/node-app
ExecStart=/usr/bin/node /opt/node-app/server.js
Restart=always
RestartSec=3
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target
  • Restart=always: Bất kể app tắt vì lý do gì, Systemd cũng sẽ gọi nó dậy.
  • RestartSec=3: Đợi 3 giây trước khi thử khởi động lại để tránh loop vô hạn gây treo CPU.
  • Environment: Khai báo trực tiếp các biến môi trường cần thiết cho app Node.js.

⚠️ Cảnh báo: Tuyệt đối hạn chế dùng User=root cho các ứng dụng web public ra internet. Hãy tạo một user riêng không có quyền sudo để chạy app nhằm bảo mật VPS.

Thực hành: Viết Systemd Service cho ứng dụng Python (FastAPI/Flask)

Tóm gọn: Chạy ứng dụng Python qua Systemd đòi hỏi bạn phải trỏ đường dẫn ExecStart trực tiếp vào file thực thi trong Virtual Environment (venv).

Bước 1: Setup môi trường Python

Giả sử app Python của bạn nằm ở /opt/python-app/ và bạn đã tạo một môi trường ảo tại /opt/python-app/venv. Bạn đang dùng Uvicorn để chạy FastAPI.

# Đường dẫn tới uvicorn trong venv sẽ trông như thế này
ls /opt/python-app/venv/bin/uvicorn

Bước 2: Viết file service cho Python

Tạo file service mới mang tên fastapi-app.service.

sudo nano /etc/systemd/system/fastapi-app.service

Cấu hình cho Python sẽ hơi khác một chút ở phần ExecStart. Bạn không cần kích hoạt (activate) venv, chỉ cần gọi thẳng file chạy bên trong venv.

[Unit]
Description=FastAPI Application with Uvicorn
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/opt/python-app
Environment="PATH=/opt/python-app/venv/bin"
ExecStart=/opt/python-app/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
Restart=always

[Install]
WantedBy=multi-user.target
  • Group=www-data: Gán group phổ biến của web server, hữu ích nếu bạn có Nginx đứng trước làm Reverse Proxy.
  • Environment="PATH=...": Ép Systemd nhận diện thư mục bin của venv, giúp các thư viện C-extensions hoạt động trơn tru.
  • ExecStart: Trỏ thẳng tới uvicorn trong venv, khai báo luôn số lượng worker.

💡 Mẹo: Đối với ứng dụng Python Production, đừng dùng lệnh python app.py. Hãy luôn dùng WSGI/ASGI server như Gunicorn hoặc Uvicorn để xử lý đa luồng tốt hơn.

Quản lý vòng đời của Service với systemctl

Tóm gọn: Lệnh systemctl là công cụ quyền lực nhất để bạn tương tác với các service vừa tạo, từ việc khởi động đến cấu hình chạy cùng hệ thống.

Reload lại Systemd Daemon

Mỗi khi bạn tạo mới hoặc chỉnh sửa bất kỳ file .service nào trong /etc/systemd/system/, bạn bắt buộc phải báo cho Systemd biết để cập nhật lại cấu hình. Nếu bỏ qua bước này, các lệnh tiếp theo sẽ báo lỗi.

sudo systemctl daemon-reload

Các lệnh quản lý cơ bản

Sau khi reload, bạn có thể bắt đầu sử dụng service. Dưới đây là chuỗi lệnh tiêu chuẩn khi deploy app.

# 1. Khởi động ứng dụng
sudo systemctl start myapp

# 2. Cho phép ứng dụng tự chạy khi VPS khởi động lại
sudo systemctl enable myapp

# 3. Kiểm tra trạng thái ứng dụng
sudo systemctl status myapp

Nếu cấu hình đúng, output của lệnh status sẽ hiển thị màu xanh lá cây rực rỡ báo hiệu thành công.

● myapp.service - Node.js Express Application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-10-25 10:00:00 UTC; 5min ago
   Main PID: 12345 (node)
      Tasks: 11 (limit: 4500)
     Memory: 45.2M
        CPU: 150ms
     CGroup: /system.slice/myapp.service
             └─12345 /usr/bin/node /opt/node-app/server.js

⚠️ Cảnh báo: Lệnh start chỉ chạy app ở phiên làm việc hiện tại. Lệnh enable mới là thứ tạo ra symlink để app tự boot cùng VPS. Luôn nhớ chạy cả hai lệnh này.

Đọc log chuẩn xác với journalctl

Tóm gọn: Systemd tự động thu thập mọi thứ mà ứng dụng của bạn in ra màn hình (stdout/stderr) và lưu vào một hệ thống log tập trung tên là Journal.

Xem log theo thời gian thực

Bạn không cần cấu hình file log thủ công trong Node.js hay Python nữa. Để xem log của service, sử dụng lệnh journalctl kèm cờ -u (unit).

# Xem log và cuộn liên tục (giống lệnh tail -f)
sudo journalctl -u myapp.service -f
Oct 25 10:00:00 vps-server node[12345]: Server is running on port 3000
Oct 25 10:05:12 vps-server node[12345]: GET /api/users - 200 OK
Oct 25 10:06:45 vps-server node[12345]: Error: Database connection timeout

Lọc log nâng cao

Khi ứng dụng chạy lâu ngày, lượng log sinh ra rất lớn. journalctl cung cấp các cờ cực kỳ tiện lợi để lọc log theo thời gian hoặc số dòng.

# Chỉ xem 50 dòng log mới nhất
sudo journalctl -u myapp.service -n 50 --no-pager

# Xem log từ ngày hôm qua đến hôm nay
sudo journalctl -u myapp.service --since yesterday --until today
  • -n 50: Chỉ định số dòng cuối cùng muốn xem.
  • --no-pager: In thẳng log ra màn hình terminal thay vì mở trình đọc cuộn trang, rất tiện khi copy/paste lỗi.

💡 Mẹo: Log của systemd lưu trên RAM hoặc ổ cứng tùy cấu hình VPS. Để tránh đầy ổ cứng, hãy giới hạn dung lượng log bằng lệnh: sudo journalctl --vacuum-size=100M.

Pitfalls & lỗi thường gặp

Dù cấu hình có vẻ đơn giản, người mới thường vấp phải những lỗi rất đặc thù của Systemd. Dưới đây là các case thực tế và cách xử lý.

  • Lỗi status=203/EXEC:
    • Nguyên nhân: Systemd không tìm thấy file thực thi hoặc file không có quyền chạy. Thường do bạn viết sai đường dẫn trong ExecStart hoặc dùng đường dẫn tương đối (như ./app.js).
    • Cách fix: Kiểm tra lại đường dẫn tuyệt đối. Chạy chmod +x /đường/dẫn/file nếu đó là một bash script.
  • Lỗi status=217/USER:
    • Nguyên nhân: Bạn khai báo User=tên_user trong file service nhưng user này không tồn tại trên VPS.
    • Cách fix: Kiểm tra danh sách user bằng lệnh cat /etc/passwd. Tạo user mới hoặc đổi tên cho đúng.
  • App chạy nhưng báo thiếu thư viện (Python) / Module not found (Node.js):
    • Nguyên nhân: Sai WorkingDirectory. Khi chạy qua Systemd, app mặc định chạy ở thư mục gốc / nếu không được chỉ định, dẫn đến không tìm thấy file package.json hoặc requirements.txt.
    • Cách fix: Luôn đảm bảo WorkingDirectory=/đường/dẫn/tuyệt/đối/tới/thư/mục/code được khai báo chính xác.
  • Sửa file .service xong nhưng app chạy cấu hình cũ:
    • Nguyên nhân: Bạn quên nạp lại daemon.
    • Cách fix: Chạy ngay sudo systemctl daemon-reload sau đó sudo systemctl restart myapp.

Key takeaways

  • Systemd là giải pháp native, ổn định và nhẹ nhất để chạy ứng dụng 24/7 trên Linux mà không cần cài thêm tool ngoài.
  • Luôn lưu file cấu hình .service tại thư mục /etc/systemd/system/.
  • Đường dẫn tuyệt đối là quy tắc sống còn trong Systemd. Không dùng đường dẫn tương đối.
  • Mỗi khi chỉnh sửa file .service, bắt buộc phải chạy sudo systemctl daemon-reload.
  • Sử dụng journalctl -u tên_service -f để theo dõi log ứng dụng theo thời gian thực một cách chuyên nghiệp.

FAQ

Dùng PM2 cho Node.js có tốt hơn Systemd không?

PM2 rất tiện lợi cho môi trường development hoặc khi bạn cần quản lý cluster Node.js nhanh chóng. Tuy nhiên, PM2 tiêu tốn một lượng RAM nhất định để chạy daemon riêng của nó. Nếu VPS của bạn ít RAM, hoặc bạn muốn quản lý mọi app (Node, Python, Go) chung một chuẩn, Systemd là lựa chọn tối ưu hơn.

Tôi có thể chạy nhiều instance của cùng một app bằng Systemd không?

Hoàn toàn được. Systemd hỗ trợ tính năng “Template unit” (file có đuôi @.service). Bạn có thể tạo [email protected] và khởi động nhiều instance bằng lệnh systemctl start myapp@1, systemctl start myapp@2 chạy trên các port khác nhau.

Làm sao để nạp file .env vào Systemd?

Thay vì dùng nhiều dòng Environment=, bạn có thể dùng chỉ thị EnvironmentFile=. Tạo một file text chứa các biến môi trường, sau đó thêm EnvironmentFile=/opt/node-app/.env vào phần [Service] của file cấu hình.

Docker và Systemd có xung đột không?

Không. Thực tế, bản thân Docker daemon cũng được quản lý bởi Systemd. Nếu bạn đã đóng gói app bằng Docker, bạn có thể thiết lập chính sách restart: always trong Docker Compose, không cần viết file Systemd riêng cho app đó nữa.

Cần VPS chạy Systemd service mượt mà 24/7?

Việc cấu hình Systemd đòi hỏi một môi trường Linux sạch, ổn định và toàn quyền Root. Nếu bạn đang tìm kiếm một máy chủ để thực hành deploy các ứng dụng Node.js, Python hay xây dựng hệ thống backend chuyên nghiệp, hãy tham khảo các gói dịch vụ tại VSIS.

Truy cập ngay vsis.net/vps để chọn cho mình một cấu hình VPS tốc độ cao, tối ưu tuyệt đối cho developer Việt Nam!

Lên đầu trang