ตั้งเวลางานอัตโนมัติด้วย systemd Timer แทน Cron

สร้าง systemd Timer สำหรับงานตามเวลา พร้อมข้อดีที่ Cron ไม่มี ทั้ง Log ในตัว การกันงานทับซ้อน และการรันงานที่พลาดไปตอนเครื่องดับ

โดย ทีมงาน THAI DATA CLOUD อัปเดต อ่าน 8 นาที เข้าชม 4 ครั้ง

แชร์FacebookXLINELinkedIn
systemd Timer — ตั้งเวลางานอัตโนมัติด้วย systemd Timer แทน Cron
ตั้งเวลางานอัตโนมัติด้วย systemd Timer แทน Cron

คำตอบสั้น ๆ: สร้าง 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"

คำสั่งนี้จะบอกว่าเวลาที่ตรงเงื่อนไขครั้งถัดไปคือเมื่อไร เป็นวิธีที่เร็วที่สุดในการยืนยันว่าเขียนถูก

ขั้นตอนที่ 5: รูปแบบเวลาที่ใช้บ่อย

  • OnCalendar=hourly ทุกต้นชั่วโมง
  • OnCalendar=daily ทุกวันเที่ยงคืน
  • OnCalendar=*-*-* 03:00:00 ทุกวันตอนตีสาม
  • OnCalendar=Mon..Fri 08:30:00 จันทร์ถึงศุกร์ 8:30 น.
  • OnCalendar=*-*-* *:00/15:00 ทุก 15 นาที
  • OnCalendar=*-*-01 04:00:00 ทุกวันที่ 1 ของเดือน

เวลาทั้งหมดอิงตาม Timezone ของเครื่อง ตรวจได้ด้วย timedatectl หากเซิร์ฟเวอร์ตั้งเป็น UTC แต่คุณคิดเป็นเวลาไทย จะคลาดกัน 7 ชั่วโมง

ขั้นตอนที่ 6: ดู Log

ทุกอย่างที่สคริปต์พิมพ์ออกมาถูกเก็บให้อัตโนมัติ ไม่ต้อง redirect เอง

journalctl -u backup-db.service -n 50 --no-pager
journalctl -u backup-db.service --since today
journalctl -u backup-db.service -f

คำสั่งสุดท้ายคือเฝ้าดูแบบเรียลไทม์ มีประโยชน์ตอนสั่งรันด้วยมือเพื่อทดสอบ

ขั้นตอนที่ 7: ย้ายงานเดิมจาก Cron

ดูงาน Cron ที่มีอยู่ก่อน

sudo crontab -l
ls -l /etc/cron.d/

เทียบรูปแบบเวลาได้ตรงตัว เช่น 30 2 * * 1 ใน Cron เท่ากับ OnCalendar=Mon *-*-* 02:30:00 เมื่อย้ายเสร็จและยืนยันว่า Timer ทำงานแล้ว จึงค่อยลบบรรทัดเดิมออกจาก Cron อย่าปล่อยให้ทั้งสองระบบรันงานเดียวกันพร้อมกัน

ปัญหาที่พบบ่อยเกี่ยวกับ systemd Timer

Timer ขึ้นว่า enabled แต่ไม่เคยรัน

ตรวจว่าชื่อไฟล์ .service กับ .timer ตรงกันทุกตัวอักษร ถ้าชื่อไม่ตรง ต้องระบุเป้าหมายให้ชัดด้วย Unit= ในส่วน [Timer]

รันด้วยมือได้ แต่รันตามเวลาแล้วพัง

เกือบทุกครั้งเป็นเรื่อง PATH และตัวแปรสภาพแวดล้อม systemd ไม่โหลด .bashrc หรือ .profile ให้ แก้โดยใช้พาธเต็มในสคริปต์ เช่น /usr/bin/mysqldump แทน mysqldump หรือกำหนดเพิ่มใน [Service] ด้วย Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

งานรอบใหม่เริ่มทั้งที่รอบเก่ายังไม่เสร็จ

systemd ป้องกันให้อยู่แล้ว service หนึ่งตัวจะไม่ถูกสั่งเริ่มซ้ำขณะที่ยังทำงานอยู่ ต่างจาก Cron ที่ยิงซ้ำได้เรื่อย ๆ หากอยากให้รอบที่ทับกันถูกยกเลิกไปเลย ให้ดูที่ Log ว่ามีข้อความ start request repeated too quickly หรือไม่

อยากรันหลังบูตเครื่องแทนเวลาที่กำหนด

ใช้ OnBootSec=5min แทน OnCalendar หรือใส่ทั้งสองบรรทัดพร้อมกันได้ เช่น รันหลังบูต 5 นาที และรันทุกวันตอนตีสาม

หากต้องการให้ทีมงานช่วยวางรอบงานสำรองข้อมูลให้เหมาะกับระบบของคุณ ติดต่อ THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

สรุปขั้นตอน systemd Timer

ทบทวน systemd Timer ทั้งหมดตามลำดับ ก่อนนำไปใช้กับระบบจริง

  1. เขียนสคริปต์ที่จะให้ทำงาน (systemd Timer)
  2. สร้างไฟล์ .service
  3. สร้างไฟล์ .timer
  4. ตรวจสอบว่ารอบเวลาถูกต้อง
  5. รูปแบบเวลาที่ใช้บ่อย
  6. ดู Log
  7. ย้ายงานเดิมจาก Cron

เมื่อทำครบทุกข้อแล้ว systemd Timer จะพร้อมใช้งาน หากผลลัพธ์ไม่เป็นไปตามที่คาด ให้ย้อนกลับไปดูหัวข้อปัญหาที่พบบ่อยด้านบน ซึ่งรวบรวมกรณีที่พบจริงจากการใช้งาน systemd Timer ไว้แล้ว

อ่านเพิ่มเติมเกี่ยวกับ systemd Timer

แชร์FacebookXLINELinkedIn

มีคำถามเรื่องบริการ? ติดต่อทีมงาน