Script sao lưu chạy tay thì tốt, nhưng đến 2 giờ sáng không ai ngồi gõ lệnh. Linux có hai cơ chế hẹn giờ song song nhau — cron đã có từ rất lâu, và systemd timer đi kèm mọi bản phân phối hiện đại. Biết dùng cả hai, và biết khi nào chọn cái nào, là việc của người quản trị.
Hai công cụ, hai hoàn cảnh
cron— một dòng là xong, có mặt gần như mọi nơi kể cả trong container tối giản.systemd timer— dài dòng hơn nhưng log gọn vào journal, chạy bù được khi máy tắt, và thừa hưởng toàn bộ cơ chế giới hạn tài nguyên của unit.
Không có cái nào thay thế hoàn toàn cái nào. Việc nhỏ và đơn giản thì cron nhanh hơn; việc quan trọng cần theo dõi thì timer đáng công sức.
Cú pháp năm trường của cron
# phut gio ngay-thang thang thu-trong-tuan lenh
# 0-59 0-23 1-31 1-12 0-7
Thứ trong tuần nhận cả 0 và 7 cho Chủ nhật. Vài ví dụ thường gặp:
*/10 * * * * /usr/local/bin/kiemtra.sh # 10 phut mot lan
0 2 * * * /usr/local/bin/backup.sh # 2h sang hang ngay
30 3 * * 0 /usr/local/bin/tonghop.sh # 3h30 Chu nhat
0 9 1 * * /usr/local/bin/hoadon.sh # 9h ngay mung 1
0 */6 * * * /usr/local/bin/dongbo.sh # moi 6 tieng
Một cái bẫy nhỏ: khi cả hai trường ngày-trong-tháng và thứ-trong-tuần đều khác *, cron chạy khi một trong hai khớp, chứ không phải cả hai cùng khớp. 0 0 13 * 5 nghĩa là ngày 13 hoặc thứ Sáu, không phải thứ Sáu ngày 13.
Các mốc viết tắt
@reboot /usr/local/bin/khoidong.sh
@daily /usr/local/bin/donlog.sh
@hourly /usr/local/bin/dongbo.sh
@weekly /usr/local/bin/baocao.sh
@monthly /usr/local/bin/quyettoan.sh
crontab của người dùng và crontab hệ thống
crontab -e # sua crontab cua chinh minh
crontab -l # xem
crontab -l -u www-data # xem cua nguoi khac (can quyen root)
crontab -r # XOA SACH, khong hoi lai
crontab -r nằm ngay cạnh -e trên bàn phím và không hỏi xác nhận. Hãy giữ thói quen crontab -l > ~/crontab.bak trước khi sửa.
Ngoài crontab cá nhân còn có /etc/crontab và các file trong /etc/cron.d/. Những file này có thêm một trường thứ sáu chỉ định người dùng chạy lệnh:
# /etc/cron.d/dongbo
# phut gio ngay thang thu NGUOI-DUNG lenh
15 * * * * www-data /usr/local/bin/dongbo.sh
Quên trường người dùng ở đây là lỗi phổ biến nhất, và cron sẽ lặng lẽ bỏ qua cả dòng.
systemd timer: hai file đi cùng nhau
Một timer luôn cần hai unit cùng tên gốc: file .service mô tả việc phải làm, file .timer mô tả lúc nào làm.
# /etc/systemd/system/donlog.service
[Unit]
Description=Don log cu trong /var/log/ungdung
[Service]
Type=oneshot
ExecStart=/usr/local/bin/donlog.sh
User=root
Nice=10
# /etc/systemd/system/donlog.timer
[Unit]
Description=Chay donlog moi ngay luc 2h sang
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
Unit=donlog.service
[Install]
WantedBy=timers.target
Hai tùy chọn đáng chú ý:
Persistent=true— nếu máy đang tắt vào đúng giờ hẹn, việc sẽ chạy bù ngay sau khi khởi động lại. Cron thuần không làm được điều này.RandomizedDelaySec=300— giãn ngẫu nhiên trong 5 phút. Rất hữu ích khi hàng chục máy cùng gọi về một máy chủ vào đúng giây thứ nhất.
Ngoài OnCalendar còn có kiểu hẹn tương đối, tiện cho việc lặp theo chu kỳ thay vì theo đồng hồ:
[Timer]
OnBootSec=5min # 5 phut sau khi may khoi dong
OnUnitActiveSec=30min # roi cu 30 phut ke tu lan chay truoc
Bật và kiểm tra lần chạy kế tiếp
sudo systemctl daemon-reload
sudo systemctl enable --now donlog.timer
systemctl list-timers # cac timer dang bat
systemctl list-timers --all # ke ca timer chua kich hoat
systemctl status donlog.timer
list-timers in ra cột thời điểm chạy kế tiếp và lần chạy gần nhất — đây là thứ cron không có sẵn. Muốn chạy thử ngay mà không chờ tới giờ, gọi thẳng service:
sudo systemctl start donlog.service
journalctl -u donlog.service -n 50
Kiểm tra biểu thức thời gian trước khi dùng
systemd-analyze calendar 'Mon *-*-* 04:00:00'
systemd-analyze calendar '*-*-01 09:00:00'
Lệnh này in ra dạng chuẩn hóa của biểu thức và thời điểm kích hoạt kế tiếp. Dùng nó trước khi lưu file còn hơn phát hiện sai sau ba ngày không thấy báo cáo.
Bẫy 1: PATH trong cron rất hẹp
Cron không đọc ~/.bashrc hay ~/.profile. PATH mặc định của nó chỉ có vài thư mục hệ thống, nên lệnh chạy ngon lành trên terminal lại báo command not found khi vào cron. Có hai cách chữa:
# Cach 1: khai bao PATH ngay dau crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 2 * * * backup.sh
# Cach 2 (chac chan hon): luon dung duong dan tuyet doi
0 2 * * * /usr/local/bin/backup.sh
systemd cũng vậy — ExecStart bắt buộc phải là đường dẫn tuyệt đối, và muốn thêm biến thì khai báo rõ trong unit:
[Service]
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Environment="LANG=vi_VN.UTF-8"
EnvironmentFile=-/etc/default/donlog
Bẫy 2: dấu phần trăm
Trong crontab, ký tự % được hiểu là xuống dòng và phần sau nó bị đẩy vào đầu vào chuẩn của lệnh. Dòng dưới đây không chạy như bạn nghĩ:
# SAI - cron cat dong tai dau %
0 3 * * * tar czf /backup/$(date +%F).tgz /srv
# DUNG - thoat dau phan tram
0 3 * * * tar czf /backup/$(date +\%F).tgz /srv
Trong file unit của systemd thì % lại là ký tự mở đầu cho các mã thay thế, muốn có dấu phần trăm thật phải viết %%. Cách tránh cả hai rắc rối: đẩy logic vào một script riêng và chỉ gọi tên script trong lịch.
Bẫy 3: múi giờ
timedatectl # xem mui gio hien tai cua he thong
sudo timedatectl set-timezone Asia/Ho_Chi_Minh
Cả cron lẫn timer đều tính theo múi giờ hệ thống. Đổi múi giờ của máy là dịch chuyển toàn bộ lịch đã hẹn — và nếu máy chủ để giờ UTC còn bạn nghĩ theo giờ Việt Nam thì mọi thứ lệch bảy tiếng. Một số bản cron cho phép đặt CRON_TZ ở đầu crontab để cố định múi giờ riêng cho lịch đó; hãy kiểm tra trang man của cron trên máy trước khi dựa vào nó.
Bẫy 4: kết quả chạy đi đâu
Mặc định cron gửi mọi thứ lệnh in ra qua thư nội bộ. Trên máy chủ không cấu hình mail, những dòng đó biến mất và bạn không biết việc chạy thành công hay thất bại. Hãy ghi ra file:
[email protected]
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Với systemd timer thì không cần làm gì — mọi thứ service in ra đều vào journal. Kiểm tra lịch sử chạy và mã thoát:
journalctl -u donlog.service --since today
systemctl status donlog.service # ket qua lan chay gan nhat
Còn bản thân dịch vụ cron thì xem bằng journalctl -u cron trên Debian/Ubuntu hoặc journalctl -u crond trên RHEL/Rocky.
Chọn cái nào
- Việc lặt vặt, một dòng, không quan trọng nếu lỡ một lần — dùng cron.
- Việc phải chạy bù khi máy vừa bật lại — dùng timer với
Persistent=true. - Việc cần giới hạn CPU, bộ nhớ, hoặc chạy dưới tài khoản riêng có sandbox — dùng timer.
- Nhiều máy cùng gọi về một đích — dùng timer với
RandomizedDelaySec. - Đừng hẹn cùng một việc ở cả hai nơi. Chọn một, và ghi lại chỗ nào đang giữ lịch.
Hãy mở crontab -l và systemctl list-timers trên máy chủ của bạn ngay hôm nay. Rất thường xuyên sẽ có một lịch cũ vẫn chạy đều mỗi đêm cho một dịch vụ đã gỡ từ lâu — và cũng thường xuyên có một lịch quan trọng đang lặng lẽ thất bại vì không ai đọc log của nó.
