Operations

Quản lý dịch vụ với systemd: tạo service và timer thay cron

SSH vào VPS mới, việc đầu tiên mình thường làm là xoá bỏ cron và chuyển toàn bộ tác vụ định kỳ sang systemd timer. Lý do? Vì systemd cung cấp logging chuẩn qua journald, dependency management rõ ràng, và dễ debug khi tác vụ không chạy. Trong bài này, mình sẽ hướng dẫn bạn tạo systemd service và timer trên Ubuntu 24.04 để thay thế cron một cách triệt để.

Tóm tắt nhanh

  • Systemd timer cung cấp logging qua journalctl, kiểm tra lỗi dễ dàng hơn cron syslog.
  • Mỗi tác vụ gồm 2 file: .service (định nghĩa lệnh chạy) và .timer (định nghĩa lịch).
  • Không cần cài thêm gói, systemd có sẵn trên mọi bản Ubuntu/Debian hiện tại.
  • Có thể set dependency: chỉ chạy timer sau khi network online, hoặc sau service khác.

Yêu cầu trước khi bắt đầu

  • VPS chạy Ubuntu 24.04 LTS hoặc Debian 12 (mới hơn cũng được).
  • User sudo non-root (tạo user mới nếu chưa có).
  • Hiểu cơ bản về systemd: systemctl, journalctl.
  • Quyền root/sudo để tạo file trong /etc/systemd/system/.

Vì sao nên dùng systemd timer thay cron?

Cron là công cụ lập lịch Unix kinh điển, nhưng nó có một số nhược điểm với sysadmin hiện đại: logging mặc định gửi về syslog, không có trạng thái rõ ràng (failed/success), và không hỗ trợ dependency. Systemd timer giải quyết những vấn đề này: bạn có thể kiểm tra trạng thái timer bằng systemctl status, xem log lỗi chi tiết bằng journalctl -u, và chỉ chạy tác vụ sau khi network, database, hoặc service khác đã sẵn sàng. Ngoài ra, timer còn hỗ trợ lịch monotonic (kiểu: chạy 30 phút sau khi boot) mà cron không làm được.

Mình từng mất nửa ngày debug một cronjob không chạy vì environment PATH khác. Với systemd, bạn có thể set Environment= rõ ràng trong file service, không còn vụ "cron không thấy lệnh" nữa. Nếu bạn đang thuê VPS Linux và muốn quản lý chuyên nghiệp, chuyển từ cron sang systemd là bước rất đáng làm.

Bước 1 - Tạo script thực thi

Mình sẽ lấy ví dụ một tác vụ backup cơ bản: nén thư mục /var/www/html và ghi vào /backup. Đầu tiên, tạo script và để ở /usr/local/bin.

sudo mkdir -p /usr/local/bin
cat << 'EOF' | sudo tee /usr/local/bin/backup-html.sh
#!/bin/bash
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
BACKUP_DIR="/backup"
SRC_DIR="/var/www/html"

mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/backup-${TIMESTAMP}.tar.gz" -C /var/www html
# Xoá backup cũ hơn 7 ngày
find "$BACKUP_DIR" -name "backup-*.tar.gz" -mtime +7 -delete
EOF

Phân quyền thực thi cho script:

sudo chmod +x /usr/local/bin/backup-html.sh

VERIFY: Chạy thử script một lần để đảm bảo lệnh không lỗi:

sudo /usr/local/bin/backup-html.sh
ls -la /backup/

Nếu bạn thấy file .tar.gz trong /backup, script đã hoạt động.

Bước 2 - Tạo file systemd service

File service sẽ định nghĩa lệnh chạy, loại service (oneshot - chạy 1 lần rồi thoát), và các tham số môi trường. Tạo file /etc/systemd/system/backup-html.service:

sudo nano /etc/systemd/system/backup-html.service

Nội dung file:

[Unit]
Description=Backup /var/www/html to /backup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-html.sh
User=root
Group=root
StandardOutput=journal
StandardError=journal
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
ProtectSystem=full
ReadWritePaths=/backup

[Install]
WantedBy=multi-user.target

Giải thích: - Type=oneshot: service chạy 1 lần rồi dừng, phù hợp với script backup. - After=network-online.target: đảm bảo network hoạt động trước khi chạy. - ProtectSystem=full: bảo vệ các thư mục hệ thống (chỉ ghi được vào ReadWritePaths). - Environment=PATH=...: set PATH rõ ràng, tránh lỗi thiếu lệnh. Tải lại systemd và kiểm tra:

sudo systemctl daemon-reload
sudo systemctl start backup-html.service
sudo systemctl status backup-html.service

Output mong đợi:

● backup-html.service - Backup /var/www/html to /backup
     Loaded: loaded (/etc/systemd/system/backup-html.service; static)
     Active: inactive (dead) since ...

Nếu service hiện active (dead) (vì oneshot chạy xong tự thoát) và không có lỗi, bạn đã thành công.

Bước 3 - Tạo file timer

File timer sẽ định nghĩa lịch chạy. Tạo file /etc/systemd/system/backup-html.timer:

sudo nano /etc/systemd/system/backup-html.timer

Nội dung:

[Unit]
Description=Timer for backup-html service
Requires=backup-html.service

[Timer]
Unit=backup-html.service
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Giải thích: - OnCalendar=daily: chạy mỗi ngày lúc 00:00 theo mặc định. Bạn có thể set chi tiết: OnCalendar=*-*-* 03:00:00 (3h sáng hàng ngày). - Persistent=true: nếu server tắt vào thời điểm chạy, timer sẽ chạy ngay sau khi server khởi động lại. - Requires=backup-html.service: đảm bảo service được load cùng timer. Enable và start timer:

sudo systemctl enable backup-html.timer
sudo systemctl start backup-html.timer
sudo systemctl status backup-html.timer

Output mong đợi:

● backup-html.timer - Timer for backup-html service
     Loaded: loaded (/etc/systemd/system/backup-html.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since ...

Dòng active (waiting) nghĩa là timer đang chờ đến thời điểm lịch.

Bước 4 - Kiểm tra danh sách timer và log

Để xem tất cả timer đang chạy:

systemctl list-timers

Output sẽ hiển thị: NEXT LEFT LAST ... Bạn sẽ thấy timer vừa tạo với thời gian chạy tiếp theo.

Kiểm tra log của service sau khi timer kích hoạt:

journalctl -u backup-html.service -n 20 --no-pager

Tìm dòng output từ script (nếu có echo) hoặc thông báo của systemd: Started ..., Exited with status code 0.

Bước 5 - Tuỳ chỉnh lịch OnCalendar chi tiết

Systemd hỗ trợ lịch rất linh hoạt. Dưới đây là một số mẫu hay dùng:

MẫuÝ nghĩa
OnCalendar=minutelyMỗi phút (debug)
OnCalendar=*-*-* *:00:00Mỗi giờ đúng
OnCalendar=*-*-* 03:00:00Mỗi ngày 3h sáng
OnCalendar=Mon..Fri 23:00:00T2-T6 lúc 23h
OnCalendar=*-*-01 00:00:00Ngày mùng 1 mỗi tháng
OnCalendar=*-*-1/3 00:00:00Mỗi 3 ngày (1,4,7...)

Sau khi sửa timer, reload systemd và restart timer:

sudo systemctl daemon-reload
sudo systemctl restart backup-html.timer

Xử lý lỗi thường gặp

Timer không hiện trong list-timers

Chạy kiểm tra xem file timer có lỗi cú pháp không: sudo systemd-analyze verify /etc/systemd/system/backup-html.timer. Lỗi phổ biến: sai định dạng thời gian trong OnCalendar.

Service báo "Unit not found"

Nên kiểm tra tên service trong Unit= của timer phải trùng khớp tên file .service. Dùng systemctl list-units | grep backup để xem có load không. Nếu không, chạy sudo systemctl daemon-reload.

Script chạy không ra đúng output

Dùng journalctl -u backup-html.service -o cat -f để xem log realtime. Thêm exec >> /tmp/debug.log 2>&1 vào đầu script để ghi stdout/stderr ra file tạm, rất dễ debug.

Timer chạy sai giờ (múi giờ)

Mặc định systemd dùng múi giờ hệ thống. Kiểm tra bằng timedatectl. Nếu bạn cần lịch theo UTC, thêm OnCalendar=03:00:00 UTC.

So sánh nhanh: systemd timer vs cron

Tính năngSystemd timerCron
Loggingjournald (tách riêng, filter dễ)syslog (gộp chung, lọc phức tạp)
DependencyAfter, Requires, Wants (có sẵn)Không hỗ trợ
Lịch monotonicOnBootSec, OnUnitActiveSecKhông hỗ trợ trực tiếp
EnvironmentSet rõ trong file servicePhải export trong script
Debugsystemctl status, journalctl -uKiểm tra mail/syslog
Dễ dùng với người mớiHơi cao (2 file + nhớ lệnh)Dễ (1 dòng trong crontab)

Trên một VPS Linux như của thueVPS, việc dùng systemd timer thay cron không chỉ giúp bạn quản lý tác vụ chuyên nghiệp hơn mà còn dễ dàng tích hợp với các service khác. Nếu bạn mới chuyển qua, hãy bắt đầu với 1-2 tác vụ không quan trọng (ví dụ: dọn log) để làm quen, rồi dần dần chuyển toàn bộ cron cũ.

Câu hỏi thường gặp

Làm thế nào để tạo timer chạy mỗi 5 phút?

Dùng OnCalendar=*:0/5 hoặc dạng rõ hơn: OnCalendar=*-*-* *:00:00 cho mỗi giờ. Với chu kỳ sub-minute, cron vẫn là lựa chọn dễ hơn, nhưng systemd hỗ trợ thời gian đến giây, vd OnCalendar=*-*-* *:*:00 (mỗi phút).

Systemd timer có chạy khi server bị tắt đúng lúc không?

Có, nếu bạn set Persistent=true trong [Timer]. Khi server khởi động lại, timer sẽ catch up và chạy service ngay. Đây là ưu điểm lớn so với cron, vốn bỏ qua hoàn toàn nếu server tắt đúng giờ chạy.

Tôi có thể dùng timer để chạy một service khác thay vì script không?

Có. Chỉ cần set Unit=your-service.service trong file timer. Ví dụ: timer để restart Docker, hoặc timer để gọi một systemd service khác.

Timer không chạy đúng lúc, kiểm tra gì trước tiên?

Kiểm tra múi giờ hệ thống: timedatectl. Kiểm tra cú pháp OnCalendar bằng: systemd-analyze calendar "daily" (thay bằng mẫu của bạn). Nếu output báo lỗi, sửa lại ngay. Kiểm tra timer có đang active: systemctl status backup-html.timer.

Tôi muốn chạy timer ngay bây giờ để test?

Chạy sudo systemctl start backup-html.service, lệnh này kích hoạt service độc lập, không cần đợi timer. Hoặc dùng sudo systemctl start backup-html.timer và gọi sudo systemctl daemon-reload để kích hoạt timer lại.

Bài viết liên quan

Lưu ý: Bài viết mang tính tham khảo, tổng hợp kiến thức chung. Mỗi hệ thống, hạ tầng và nhu cầu có đặc thù riêng, nên kiểm thử trong môi trường an toàn và tham vấn kỹ sư trước khi triển khai thực tế.