Lập lịch bằng systemd timer và cron
Thực hiện trên server của bạn, rồi kiểm tra từng tiêu chí.
Vì sao cần học
Backup, xoay vòng log, gia hạn certificate đều là việc định kỳ. Cron làm được nhưng thiếu nhiều thứ: lỗi không ai biết, output thất lạc, máy tắt đúng giờ chạy thì bỏ lỡ luôn. systemd timer giải quyết các điểm đó và dùng cùng công cụ log, giới hạn quyền như service. RHCSA trên RHEL 10 yêu cầu cả cron và timer.
Mục tiêu
- Tách việc cần chạy (service
oneshot) khỏi lịch chạy (timer). - Viết biểu thức
OnCalendarvà kiểm tra bằngsystemd-analyze calendar. - Dùng
Persistent=true,RandomizedDelaySecvàStateDirectory. - Theo dõi lần chạy bằng
systemctl list-timersvàjournalctl.
Điều kiện tiên quyết
- Bài Vận hành web service bằng systemd.
Mental model
| cron | systemd timer | |
|---|---|---|
| Log | Gửi mail hoặc mất | journalctl -u <service> |
| Máy tắt đúng giờ chạy | Bỏ lỡ | Persistent=true chạy bù khi boot |
| Hai lần chạy chồng nhau | Có thể | Không: service đang chạy thì timer không khởi động thêm |
| Chạy thử bằng tay | Copy lệnh ra terminal, môi trường khác | systemctl start <service>, cùng môi trường |
| Giới hạn quyền | Không | Mọi chỉ thị của systemd.exec |
Timer kdc-backup.timer mặc định kích hoạt service cùng tên kdc-backup.service. Service kiểu oneshot chạy xong rồi thoát; trạng thái inactive (dead) sau khi chạy xong là bình thường.
StateDirectory=kdc-backup yêu cầu systemd tạo /var/lib/kdc-backup với owner và quyền bạn khai báo, rồi truyền đường dẫn qua biến $STATE_DIRECTORY.
Thực hành (Guided Lab)
1. Dữ liệu cần backup và script
sudo install -d -m 755 /srv/kdc-notes
echo "Ghi chú tạo lúc $(date)" | sudo tee /srv/kdc-notes/today.md
sudo tee /usr/local/bin/kdc-backup.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
dest="${STATE_DIRECTORY:?chạy script này qua kdc-backup.service}"
stamp="$(date +%Y%m%d-%H%M%S)"
tar -czf "$dest/notes-$stamp.tar.gz" -C /srv/kdc-notes .
find "$dest" -name 'notes-*.tar.gz' -mtime +7 -delete
date --iso-8601=seconds > "$dest/last-success"
echo "Backup xong: notes-$stamp.tar.gz"
EOF
sudo chmod 755 /usr/local/bin/kdc-backup.sh
Giải thích lệnh và tham số
| Thành phần | Ý nghĩa |
|---|---|
${STATE_DIRECTORY:?...} | Dừng và báo lỗi nếu biến chưa có giá trị; systemd cấp biến khi service có StateDirectory=. |
date +%Y%m%d-%H%M%S | In năm-tháng-ngày và giờ-phút-giây để đặt tên backup. Dấu + mở đầu định dạng, không phải phép cộng. |
tar -czf "$dest/...tar.gz" | -c tạo archive, -z nén gzip, -f chọn file archive theo sau. |
-C /srv/kdc-notes . của tar | Đổi thư mục nguồn trước khi lấy nội dung . (thư mục đó), để archive không chứa đường dẫn tuyệt đối dài. |
find "$dest" -name 'notes-*.tar.gz' | Tìm các tên khớp mẫu trong thư mục backup; nháy đơn giữ * để find xử lý. |
-mtime +7 -delete | Chọn file có tuổi sửa đổi theo ngày đã làm tròn lớn hơn 7 (ít nhất 8 khoảng 24 giờ), rồi xóa. -delete thực sự xóa file. |
date --iso-8601=seconds | In thời điểm dạng ISO với độ chính xác giây và múi giờ. |
Chạy find với điều kiện tương tự không có -delete nếu cần xem danh sách trước khi xóa. Lệnh tar này tạo backup, chưa kiểm tra restore.
set -euo pipefail khiến script dừng ngay khi một lệnh lỗi, nên last-success chỉ được ghi khi backup thật sự thành công.
2. Service và timer
sudo tee /etc/systemd/system/kdc-backup.service <<'EOF'
[Unit]
Description=Backup /srv/kdc-notes
[Service]
Type=oneshot
StateDirectory=kdc-backup
StateDirectoryMode=0700
ExecStart=/usr/local/bin/kdc-backup.sh
EOF
sudo tee /etc/systemd/system/kdc-backup.timer <<'EOF'
[Unit]
Description=Backup kdc-notes mỗi 15 phút
[Timer]
OnCalendar=*:0/15
Persistent=true
RandomizedDelaySec=60
[Install]
WantedBy=timers.target
EOF
systemd-analyze calendar '*:0/15'
sudo systemctl daemon-reload
Giải thích lệnh và tham số
systemd-analyze calendar '*:0/15' phân tích biểu thức lịch và in thời điểm chạy tiếp theo. Nháy đơn ngăn shell mở rộng dấu *. Trong cấu hình, OnCalendar chọn lịch, Persistent=true chạy bù một lần khi đã bỏ lỡ lịch, RandomizedDelaySec=60 thêm trễ ngẫu nhiên tối đa 60 giây. Đây là thuộc tính cấu hình, không phải tùy chọn shell.
Timer có [Install], service thì không: ta enable timer, không enable service.
3. Chạy thử, rồi bật lịch
sudo systemctl start kdc-backup.service
journalctl -u kdc-backup.service --no-pager -n 5
sudo ls -l /var/lib/kdc-backup
sudo systemctl enable --now kdc-backup.timer
systemctl list-timers kdc-backup.timer
Giải thích lệnh và tham số
systemctl start kdc-backup.service chạy backup ngay một lần. systemctl enable --now kdc-backup.timer bật timer để lập lịch, không enable service oneshot. list-timers <timer> in lịch với cột NEXT (lần tới), LAST (lần trước). journalctl -n 5 giới hạn ở 5 dòng log.
list-timers cho biết lần chạy kế tiếp (NEXT) và lần gần nhất (LAST).
4. Cron cho việc đơn giản
Cron vẫn có mặt trên hầu hết server và có trong đề RHCSA. Một dòng cron gồm năm trường thời gian (phút, giờ, ngày, tháng, thứ) rồi tới lệnh. File trong /etc/cron.d/ có thêm trường user trước lệnh.
sudo dnf install -y cronie
sudo systemctl enable --now crond
echo '30 2 * * * root find /srv/kdc-notes -name "*.tmp" -mtime +1 -delete' | sudo tee /etc/cron.d/kdc-cleanup
sudo chmod 644 /etc/cron.d/kdc-cleanup
Giải thích lệnh và tham số
Dòng trong /etc/cron.d/kdc-cleanup có 5 trường lịch: 30 phút, 2 giờ, * mọi ngày trong tháng, * mọi tháng, * mọi thứ trong tuần; root là user chạy lệnh. Phần sau mới là lệnh find. -name "*.tmp" chọn file tạm theo tên, -mtime +1 chọn file đã sửa ít nhất 2 khoảng 24 giờ trước, -delete xóa chúng. Với crontab -e, -e mở trình sửa; crontab -l liệt kê lịch của user và không có trường root trong mỗi dòng.
Job chạy lúc 02:30 hằng ngày, xóa file .tmp cũ hơn một ngày. Crontab của từng user sửa bằng crontab -e và xem bằng crontab -l; lịch sử chạy nằm trong journalctl -u crond.
Lịch backup ở trên viết bằng cron sẽ là */15 * * * * root /usr/local/bin/kdc-backup.sh. Hãy nghĩ xem $STATE_DIRECTORY có tồn tại khi cron chạy script không, và lỗi sẽ được ghi vào đâu.
Tự kiểm tra (Verification)
systemctl is-active kdc-backup.timer
systemctl is-enabled kdc-backup.timer
sudo stat -c '%A %U %n' /var/lib/kdc-backup
sudo cat /var/lib/kdc-backup/last-success
cat /etc/cron.d/kdc-cleanup
systemctl is-active crond
Giải thích lệnh và tham số
stat -c '%A %U %n' in quyền dạng chữ, owner và đường dẫn; thư mục backup cần có quyền riêng cho root. Muốn xem danh sách file trong archive, tar -tzf <file.tar.gz> dùng -t để liệt kê (thay cho -c tạo), -z đọc gzip, -f chọn archive. Ví dụ dùng ls -t là sắp xếp file theo thời gian sửa mới nhất trước; head -1 chọn bản mới nhất.
Chọn server và bấm Kiểm tra kết quả.
Lỗi thường gặp
- Enable service thay vì timer:
systemctl enablebáo unit không có[Install]. - Sửa
OnCalendarmà quêndaemon-reload:list-timersvẫn hiện lịch cũ. - Script chạy tay được nhưng lỗi trong service: môi trường khác nhau (
PATH, thư mục hiện tại, biến môi trường). Đọcjournalctl -u kdc-backup.service. OnCalendar=15m: không hợp lệ. Khoảng thời gian tương đối dùngOnUnitActiveSec=15min.
Ghi chú production
- Backup chỉ có giá trị khi đã thử restore. Thử liệt kê bản mới nhất:
sudo sh -c 'tar -tzf "$(ls -t /var/lib/kdc-backup/notes-*.tar.gz | head -1)"'. Cầnsh -cvì user thường không mở được thư mục quyền 700 để mở rộng dấu*. - Backup cùng ổ với dữ liệu không bảo vệ được khi hỏng ổ. Module 04 và 19 sẽ đưa backup ra nơi khác.
- Theo dõi tuổi của
last-successđể cảnh báo khi backup ngừng chạy; Module 17 sẽ biến nó thành alert.
Ghi chú Ubuntu
Cron có sẵn trong package cron, dịch vụ tên cron.service nên tiêu chí crond.service chỉ đạt trên RHEL-family. Phần systemd timer giống hệt.
Dọn dẹp
sudo systemctl disable --now kdc-backup.timer
sudo rm /etc/systemd/system/kdc-backup.{service,timer} /usr/local/bin/kdc-backup.sh /etc/cron.d/kdc-cleanup
sudo systemctl daemon-reload
sudo rm -rf /var/lib/kdc-backup /srv/kdc-notes
Giải thích lệnh và tham số
kdc-backup.{service,timer} là brace expansion của Bash: shell mở rộng thành hai tên kdc-backup.service và kdc-backup.timer trước khi gọi rm. Không phải cú pháp do rm tự hiểu.