Tại sao nên chạy dịch vụ dưới ngữ cảnh người dùng?
Trong những ngày đầu làm quản trị hệ thống (sysadmin), tôi có một thói quen nguy hiểm: chạy mọi script chạy nền dưới quyền root. Đó là con đường ít trở ngại nhất vì nó vượt qua mọi rào cản về quyền hạn. Nhưng khi hệ thống lớn dần, tôi nhận ra rủi ro tiềm ẩn. Chỉ cần một lỗi nhỏ trong script chạy quyền root cũng có thể làm tổn hại toàn bộ hệ điều hành. Đó chính là lúc systemd user units phát huy tác dụng.
Hầu hết các quản trị viên Linux sử dụng systemctl cho các dịch vụ toàn hệ thống, nhưng ít ai chạm đến trình quản lý cấp người dùng. Công cụ này cho phép bạn chạy các dịch vụ với quyền hạn của chính tài khoản của mình. Trên một VPS Ubuntu 22.04 tiêu chuẩn, tôi nhận thấy việc chuyển từ các Python worker chạy trên Docker sang systemd user units đã giúp giảm mức sử dụng bộ nhớ nhàn rỗi từ 150MB xuống chỉ còn 12MB. Đây là một giải pháp thay thế nhẹ nhàng, an toàn cho các tác vụ không cần quyền truy cập toàn hệ thống.
So sánh các phương pháp quản lý dịch vụ
Nếu bạn cần một tiến trình luôn chạy ngầm, bạn có một vài lựa chọn. Dưới đây là bảng so sánh trong môi trường production:
| Phương pháp | Tính bền bỉ | Bảo mật | Khả năng kiểm soát |
|---|---|---|---|
| Screen/Tmux | Mất khi reboot | Cao (User) | Chỉ thủ công |
| Crontab @reboot | Bền bỉ | Cao (User) | Không tự khởi động lại |
| System-wide systemd | Bền bỉ | Thấp (Root) | Tuyệt vời |
| systemd User Units | Bền bỉ | Cao (User) | Tuyệt vời |
Đối với các tác vụ không quá quan trọng như web scraper, bot Discord hoặc các API nội bộ, user units mang lại sự cân bằng tốt nhất giữa bảo mật và tính dễ sử dụng.
Những sự đánh đổi
User units không hoàn hảo cho mọi tình huống. Bạn cần biết ưu và nhược điểm của chúng trước khi di chuyển hệ thống của mình.
Lợi ích
- Hạn chế phạm vi ảnh hưởng: Nếu kẻ tấn công khai thác được dịch vụ của bạn, chúng sẽ bị kẹt trong phạm vi quyền hạn của người dùng đó. Chúng không thể chạm vào các file thực thi hệ thống hoặc dữ liệu của người dùng khác.
- Quyền tự chủ của nhà phát triển: Bạn có thể triển khai và quản lý dịch vụ mà không cần yêu cầu quyền sudo từ quản trị viên cấp cao.
- Môi trường sạch sẽ: Các đường dẫn và biến môi trường nằm gọn trong thư mục home của bạn, giúp tránh tình trạng “xung đột phụ thuộc” ở cấp hệ thống.
Nhược điểm
- Giới hạn tài nguyên: Các dịch vụ người dùng chia sẻ chung một phân vùng tài nguyên. Nếu một dịch vụ bị rò rỉ bộ nhớ, nó có thể khiến kernel tắt các tiến trình người dùng khác của bạn.
- Cổng đặc quyền: Bạn không thể liên kết với các cổng dưới 1024 (như 80 hoặc 443) nếu không có cấu hình bổ sung.
- Phụ thuộc vào phiên làm việc: Theo mặc định, các dịch vụ sẽ bị tắt khi bạn đăng xuất. Đây là một vấn đề gây khó chịu thường gặp mà chúng ta sẽ giải quyết bằng “Lingering”.
Triển khai thực tế
Tôi sử dụng user units cho bất kỳ file thực thi Go hoặc ứng dụng Node.js nào không cần truy cập cấp phần cứng. Tôi luôn lưu trữ các file dịch vụ của mình trong ~/.config/systemd/user/. Điều này tuân theo tiêu chuẩn XDG và giữ cho thư mục home luôn gọn gàng.
1. Tạo file Unit
Đầu tiên, tạo thư mục nơi systemd tìm kiếm các cấu hình cấp người dùng.
mkdir -p ~/.config/systemd/user/
Hãy xây dựng một dịch vụ cho một Python worker. Chúng ta muốn nó tự động khởi động lại nếu bị lỗi và ghi log đầu ra vào một thư mục cụ thể.
nano ~/.config/systemd/user/my-worker.service
Dán cấu hình này vào, thay thế các đường dẫn bằng vị trí dự án thực tế của bạn:
[Unit]
Description=Worker chạy nền bằng Python
After=network.target
[Service]
ExecStart=/usr/bin/python3 -u /home/alex/scripts/worker.py
Restart=always
RestartSec=3
StandardOutput=append:/home/alex/logs/worker.log
StandardError=append:/home/alex/logs/worker.err
[Install]
WantedBy=default.target
Lưu ý: Luôn sử dụng đường dẫn tuyệt đối. systemd không biết về biến PATH hoặc các bí danh (alias) trong .bashrc của bạn.
2. Điều khiển dịch vụ
Khi quản lý các dịch vụ này, cờ --user là bắt buộc. Nếu không có nó, systemd sẽ kiểm tra /etc/systemd/system/ và không tìm thấy file của bạn.
# Làm mới trình quản lý
systemctl --user daemon-reload
# Chạy và kích hoạt khởi động cùng hệ thống
systemctl --user start my-worker.service
systemctl --user enable my-worker.service
# Kiểm tra trạng thái hoạt động
systemctl --user status my-worker.service
3. Kích hoạt tính bền bỉ với Lingering
Trên hầu hết các bản phân phối hiện đại, hệ thống sẽ tắt tất cả các tiến trình người dùng ngay khi phiên SSH cuối cùng của bạn kết thúc. Để giữ cho dịch vụ chạy 24/7, bạn phải kích hoạt lingering. Điều này yêu cầu systemd khởi động trình quản lý người dùng ngay khi boot và duy trì nó vô thời hạn.
# Kích hoạt lingering cho người dùng cụ thể
sudo loginctl enable-linger alex
Sau khi được kích hoạt, các dịch vụ của bạn sẽ khởi động ngay khi máy chủ boot, thậm chí trước khi bạn đăng nhập qua SSH.
Xử lý các lỗi thường gặp
Ngay cả những thiết lập đơn giản cũng có thể gặp trục trặc. Đây là hai vấn đề tôi thường xuyên gặp phải.
Lỗi “Failed to connect to bus”
Nếu bạn thấy lỗi Failed to connect to bus: No such file or directory, có khả năng biến XDG_RUNTIME_DIR của bạn bị thiếu. Điều này thường xảy ra nếu bạn chuyển đổi người dùng bằng lệnh su - username. Để khắc phục, hãy chạy lệnh này hoặc thêm nó vào profile của bạn:
export XDG_RUNTIME_DIR=/run/user/$(id -u)
Xem log không cần quyền root
Bạn không cần phải kiểm tra /var/log/syslog. Hãy sử dụng journalctl với cờ user để xem chính xác script của bạn đang làm gì trong thời gian thực.
journalctl --user -u my-worker.service -f
Việc từ bỏ tư duy “chỉ dùng root” giúp máy chủ của bạn bền bỉ hơn. User units và lingering cung cấp một cách chuyên nghiệp, không cần container để quản lý các tác vụ nền với mức tiêu tốn tài nguyên tối thiểu.

