คำตอบสั้น ๆ: สร้าง systemd Timer สำหรับงานตามเวลา พร้อมข้อดีที่ Cron ไม่มี ทั้ง Log ในตัว การกันงานทับซ้อน และการรันงานที่พลาดไปตอนเครื่องดับ
คู่มือนี้อธิบาย systemd Timer ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
Cron ทำงานได้ดีมาหลายสิบปี แต่เมื่อมีงานตามเวลาหลายตัวบนเซิร์ฟเวอร์เดียว ข้อจำกัดจะเริ่มโผล่ ได้แก่ Log ที่ต้อง redirect เอง งานที่รันทับกันเมื่อรอบก่อนยังไม่จบ และงานที่หายไปเงียบ ๆ เมื่อเครื่องดับในช่วงเวลานั้นพอดี
systemd Timer แก้ทั้งสามข้อนี้ในตัว คู่มือนี้สร้าง Timer สำหรับงานสำรองข้อมูลเป็นตัวอย่าง แล้วอธิบายรูปแบบเวลา การดู Log และการย้ายงานเดิมจาก Cron ใช้ได้กับ Ubuntu, Debian, AlmaLinux และ Rocky Linux ทุกรุ่นที่ใช้ systemd
สิ่งที่ต้องเตรียม (systemd Timer)
- Linux Server ที่ใช้ systemd และสิทธิ์
sudo - สคริปต์หรือคำสั่งที่ต้องการให้ทำงานตามเวลา ทดสอบรันด้วยมือจนผ่านแล้ว
ก่อนเริ่ม ให้ทดสอบสคริปต์ด้วยมือก่อนเสมอ ปัญหาส่วนใหญ่ของงานตามเวลาไม่ได้อยู่ที่ตัวตั้งเวลา แต่อยู่ที่สคริปต์ที่พึ่งพาตัวแปรสภาพแวดล้อมซึ่งมีเฉพาะตอนล็อกอิน
ขั้นตอนที่ 1: เขียนสคริปต์ที่จะให้ทำงาน (systemd Timer)
sudo nano /usr/local/bin/backup-db.sh
#!/bin/bash
set -euo pipefail
DEST=/var/backups/mysql
STAMP=$(date +%F-%H%M)
mkdir -p "$DEST"
mysqldump --single-transaction --all-databases | gzip > "$DEST/all-$STAMP.sql.gz"
find "$DEST" -name 'all-*.sql.gz' -mtime +14 -delete
echo "backup written: $DEST/all-$STAMP.sql.gz"
ให้สิทธิ์รันและทดสอบ
sudo chmod +x /usr/local/bin/backup-db.sh
sudo /usr/local/bin/backup-db.sh
บรรทัด set -euo pipefail สำคัญมาก มันทำให้สคริปต์หยุดทันทีเมื่อคำสั่งใดล้มเหลว แทนที่จะทำงานต่อจนได้ไฟล์สำรองที่ว่างเปล่าโดยไม่มีใครรู้
ขั้นตอนที่ 2: สร้างไฟล์ .service
Timer ไม่ได้รันคำสั่งเอง แต่ไปสั่ง service ให้ทำงาน จึงต้องมีสองไฟล์เสมอ ชื่อไฟล์ต้องตรงกันยกเว้นนามสกุล
sudo nano /etc/systemd/system/backup-db.service
[Unit]
Description=สำรองฐานข้อมูล MySQL ทั้งหมด
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-db.sh
User=root
# ให้หยุดเองหากงานค้างเกินหนึ่งชั่วโมง
TimeoutStartSec=3600
Type=oneshot บอก systemd ว่างานนี้ทำครั้งเดียวแล้วจบ ไม่ใช่โปรแกรมที่ต้องรันค้างไว้ ทดสอบสั่งด้วยมือได้เลย
sudo systemctl daemon-reload
sudo systemctl start backup-db.service
sudo systemctl status backup-db.service --no-pager
ขั้นตอนที่ 3: สร้างไฟล์ .timer
sudo nano /etc/systemd/system/backup-db.timer
[Unit]
Description=สำรองฐานข้อมูลทุกวันตอนตีสาม
[Timer]
OnCalendar=*-*-* 03:00:00
# กระจายเวลาเริ่มแบบสุ่มไม่เกิน 10 นาที กันงานหลายตัวชนกัน
RandomizedDelaySec=600
# หากเครื่องดับตอนถึงรอบ ให้รันทันทีที่เปิดเครื่องกลับมา
Persistent=true
[Install]
WantedBy=timers.target
บรรทัด Persistent=true คือสิ่งที่ Cron ไม่มี ถ้าเครื่องปิดอยู่ตอนตีสาม Cron จะข้ามรอบนั้นไปเงียบ ๆ แต่ systemd จะรันให้ทันทีที่เครื่องกลับมา
เปิดใช้งาน
sudo systemctl daemon-reload
sudo systemctl enable --now backup-db.timer
ขั้นตอนที่ 4: ตรวจสอบว่ารอบเวลาถูกต้อง
ดู Timer ทั้งหมดในเครื่องพร้อมเวลาที่จะทำงานครั้งถัดไป
systemctl list-timers --all
คอลัมน์ NEXT คือเวลาที่จะรันรอบหน้า และ LAST คือรอบล่าสุด ถ้ายังไม่แน่ใจว่ารูปแบบเวลาที่เขียนหมายถึงอะไร ให้ทดสอบด้วย
systemd-analyze calendar "*-*-* 03:00:00"
systemd-analyze calendar "Mon *-*-* 02:30:00"
systemd-analyze calendar "*-*-01 00:00:00"
คำสั่งนี้จะบอกว่าเวลาที่ตรงเงื่อนไขครั้งถัดไปคือเมื่อไร เป็นวิธีที่เร็วที่สุดในการยืนยันว่าเขียนถูก