EQVPS

วิธีตั้งเวลางานบน VPS ด้วย cron และตัวจับเวลาของ systemd

รันการสำรองข้อมูล การล้างไฟล์ และสคริปต์ตามกำหนดเวลา cron ในห้านาทีพร้อมกับดักสามข้อที่ทำให้งานพังแบบเงียบ ๆ จากนั้นคือตัวจับเวลาของ systemd สำหรับล็อก การรันชดเชยหลังเครื่องดับ และเวลาที่แม่นยำ ทุกพฤติกรรมด้านล่างทดสอบบนเซิร์ฟเวอร์ Ubuntu 24.04 ใหม่

เซิร์ฟเวอร์คุ้มค่าเพราะมันทำงานตอนที่คุณหลับ: สำรองข้อมูล หมุนเวียนล็อก ต่ออายุใบรับรอง รายงานตอนเก้าโมง Linux มีตัวตั้งเวลาสองแบบสำหรับเรื่องนี้ cron มีอายุสี่สิบปีและใช้แค่บรรทัดเดียว ตัวจับเวลาของ systemd ใช้ไฟล์เล็ก ๆ สองไฟล์ แต่แลกมาด้วยล็อก การรันชดเชยหลังเครื่องดับ และเวลาที่แม่นยำ คู่มือนี้อธิบายทั้งสองแบบบน Ubuntu 24.04 โดยตรวจทุกพฤติกรรมบนเซิร์ฟเวอร์ใหม่จากอิมเมจของเรา รวมถึงข้อผิดพลาดสามข้อที่ทำให้งาน cron ล้มเหลวโดยไม่มีข้อความใด ๆ

คุณต้องมีผู้ใช้ที่ใช้ sudo ได้ คู่มือตั้งค่า Ubuntu จะสร้างให้

1. cron ในห้านาที

cron ติดตั้งและทำงานอยู่บนอิมเมจ Ubuntu และ Debian ของเรา ผู้ใช้แต่ละคนมีตารางงานของตัวเอง แก้ไขของคุณด้วย:

crontab -e

แต่ละบรรทัดมีช่องเวลาห้าช่องและคำสั่งหนึ่งคำสั่ง:

# min hour day month weekday  command
  30  3    *   *     *        /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  */5 *    *   *     *        /usr/local/bin/check.sh
  0   9    *   *     1-5      /usr/local/bin/report.sh
  @reboot                     /usr/local/bin/start-tunnel.sh

คือ: ทุกคืนเวลา 03:30 ทุก 5 นาที วันทำงานเวลา 09:00 และหนึ่งครั้งหลังบูตทุกครั้ง */5 หมายถึงทุกค่าที่ห้า 1-5 คือช่วง 1,15 คือรายการ ใช้เขตเวลาของเซิร์ฟเวอร์ ซึ่งเป็น UTC บนอิมเมจของเรา ตั้งของคุณครั้งเดียวด้วยคู่มือชื่อโฮสต์และเขตเวลา แล้ว cron จะใช้ตาม

ดูสิ่งที่รันไปแล้วด้วย journalctl -u cron ทุกการรันจะบันทึกผู้ใช้และคำสั่ง และกับดักทั้งสามก็ปรากฏตรงนั้น

กับดักที่ 1: ผลลัพธ์หายไป cron ส่งผลลัพธ์ของงานให้ผู้ใช้ทางอีเมล อิมเมจของเราไม่มีเมลเซิร์ฟเวอร์ journal จึงแสดง (CRON) info (No MTA installed, discarding output) และผลลัพธ์หายไป ให้เปลี่ยนเส้นทางเอง เหมือนตัวอย่างแรกที่ใช้ >> /var/log/backup.log 2>&1

กับดักที่ 2: เครื่องหมายเปอร์เซ็นต์ ใน crontab เครื่องหมาย % จะจบคำสั่งและเปลี่ยนส่วนที่เหลือเป็นอินพุตมาตรฐาน เราตั้งเวลา date +%F > /tmp/today และ journal แสดงสิ่งที่ cron รันจริงคือ CMD (date +) ไม่มีไฟล์ถูกเขียน และไม่มีข้อผิดพลาดใดมาถึงเรา ให้เขียน \% หรือใส่คำสั่งไว้ในสคริปต์แล้วตั้งเวลาสคริปต์นั้น

กับดักที่ 3: สภาพแวดล้อมต่างกัน cron รันงานด้วย /bin/sh ไม่ใช่ bash ในโฮมไดเรกทอรีของคุณ และด้วยสภาพแวดล้อมแบบน้อยที่สุด บน Ubuntu 24.04 PATH ดีกว่าชื่อเสียงของมัน: เป็นพาธระบบเต็ม รวม /usr/local/bin และ /snap/bin ทั้งสำหรับ root และผู้ใช้ทั่วไป สิ่งที่ยังขาดคือทุกอย่างใต้โฮมไดเรกทอรี: ~/.local/bin, node ของ nvm, pipx และ cargo ให้ใช้พาธเต็มในงาน หรือขึ้นต้นบรรทัดด้วย . $HOME/.profile;

งานที่เป็นของระบบไม่ใช่ของผู้ใช้ ให้วางใน /etc/cron.d/ เป็นไฟล์ที่มีช่องเพิ่มอีกหนึ่งช่อง คือผู้ใช้:

30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Ubuntu ยังรันทุกอย่างใน /etc/cron.hourly, cron.daily, cron.weekly และ cron.monthly ที่นาทีที่ 17 ของทุกชั่วโมง เวลา 06:25 วันอาทิตย์เวลา 06:47 และวันที่ 1 ของเดือนเวลา 06:52 วางสคริปต์ที่รันได้ไว้ที่นั่น แล้วมันจะรันพร้อมกับตัวอื่น

2. ตัวจับเวลาของ systemd

ตัวจับเวลาประกอบด้วยสองยูนิต: เซอร์วิสที่ทำงาน และตัวจับเวลาที่บอกว่าเมื่อไร สร้างสคริปต์ก่อน แล้วจึงสร้างยูนิต

sudo nano /usr/local/bin/backup.sh
sudo chmod +x /usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night at 03:30

[Timer]
OnCalendar=*-*-* 03:30
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

เปิดใช้งานตัวจับเวลา ไม่ใช่เซอร์วิส:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers backup.timer

list-timers แสดงรอบถัดไป เวลาที่เหลือ และรอบล่าสุด ล็อกไม่ต้องตั้งค่า: journalctl -u backup.service มีทุกการเริ่ม ทุกการจบ และทุกบรรทัดของผลลัพธ์พร้อมเวลา หากต้องการรันงานทันที ใช้ sudo systemctl start backup.service และนี่ก็เป็นวิธีทดสอบสคริปต์ก่อนเชื่อใจตารางเวลา

ไวยากรณ์ปฏิทิน OnCalendar อ่านว่า วันในสัปดาห์ ปี-เดือน-วัน ชั่วโมง:นาที:วินาที โดย * หมายถึงค่าใดก็ได้ ทดสอบนิพจน์ก่อนใช้:

systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2

คำสั่งจะพิมพ์รูปแบบที่ปรับมาตรฐานแล้วและวันที่ถัดไป นิพจน์บางส่วนที่เราตรวจแล้ว:

OnCalendarความหมาย
*-*-* 03:30ทุกวันเวลา 03:30
Mon..Fri *-*-* 09:00วันทำงานเวลา 09:00
*:0/15ทุก 15 นาที
weeklyวันจันทร์เวลา 00:00
*-*-01 04:00วันที่ 1 ของทุกเดือนเวลา 04:00
*-*-* 03:00 Europe/Kyiv03:00 ตามเวลาเคียฟ ในการทดสอบของเราทำงานเวลา 01:00 UTC

การตั้งค่าสามอย่างที่ควรรู้ Persistent=true บันทึกรอบล่าสุดลงดิสก์ และรันชดเชยรอบที่พลาดไปขณะเซิร์ฟเวอร์ปิดหรือตัวจับเวลาหยุดอยู่ เราหยุดตัวจับเวลาทุกสองนาทีของเรา รอให้รอบที่กำหนดผ่านไป แล้วเปิดใหม่: รอบที่พลาดเริ่มทำงานในวินาทีเดียวกันนั้น หากไม่มี Persistent มันจะแค่รอรอบถัดไป

เรื่องไม่คาดคิดคือ AccuracySec โดยค่าเริ่มต้น systemd รวมตัวจับเวลาไว้ในหน้าต่างหนึ่งนาทีเพื่อลดการปลุกระบบ รอบแรกของเราจึงมาตอน 07:42:18 ไม่ใช่ 07:42:00 เมื่อตั้ง AccuracySec=1s รอบต่อมาตรงวินาทีพอดีที่ 07:46:00 และ 07:48:00 ตั้งค่านี้เมื่อนาทีสำคัญ ส่วนการสำรองข้อมูลตอนกลางคืนใช้ค่าเริ่มต้นได้

RandomizedDelaySec=10m ตั้งใจทำตรงกันข้าม: กระจายเวลาเริ่มไปในหน้าต่างหนึ่ง เพื่อไม่ให้เซิร์ฟเวอร์สิบเครื่องเข้าถึงที่เก็บสำรองข้อมูลเดียวกันในวินาทีเดียวกัน

สำหรับงานที่รันครั้งเดียว ไม่ต้องใช้ไฟล์เลย:

sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh

คำสั่งนี้สร้างตัวจับเวลาชั่วคราวที่ทำงานครั้งเดียวแล้วหายไป จนถึงตอนนั้น systemctl list-timers จะแสดงมันไว้

3. ควรใช้แบบไหน

cronตัวจับเวลา systemd
การตั้งค่า1 บรรทัด2 ไฟล์
ล็อกเฉพาะเมื่อเปลี่ยนเส้นทางผลลัพธ์journalctl -u name.service
รอบที่พลาดขณะเซิร์ฟเวอร์ปิดหายไปรันตอนบูตด้วย Persistent=true
งานยังรันอยู่เมื่อถึงรอบถัดไปเริ่มสำเนาที่สองข้ามไปจนกว่าตัวแรกจะเสร็จ
เวลาที่แม่นยำระดับนาทีระดับวินาทีด้วย AccuracySec=1s
สถานะไม่มีlist-timers และ systemctl status

ใช้ cron กับบรรทัดสั้น ๆ ที่อีกปีหนึ่งคุณยังจำได้ ใช้ตัวจับเวลากับทุกงานที่คุณต้องเห็นเมื่อมันล้มเหลว งานที่ห้ามซ้อนกัน หรืองานที่ต้องรันแม้จะพลาดไปเพราะรีสตาร์ต: การสำรองข้อมูล การต่ออายุใบรับรอง การล้างฐานข้อมูล ถ้างาน cron อาจซ้อนกัน ให้ครอบไว้ด้วย flock -n /tmp/backup.lock /usr/local/bin/backup.sh แล้วสำเนาที่สองจะออกทันที

ตัวตั้งเวลาทั้งสองแบบรันงานแล้วก็หยุด โปรแกรมที่ต้องทำงานตลอดเวลาและกลับมาเองหลังแครช ต้องใช้เซอร์วิส ไม่ใช่ตารางเวลา ดูวิธีสร้างเซอร์วิส systemd และสำหรับ Windows ดูวิธีรันสคริปต์ 24/7 บน Windows Server ตัวจับเวลาตัวแรกที่ดีคือตัวจากคู่มือสำรองข้อมูลด้วย restic ของเรา ส่วนการอัปเดตความปลอดภัยอัตโนมัติ เป็นตัวจับเวลาที่ Ubuntu มีมาให้แล้ว คือ apt-daily-upgrade.timer หนึ่งใน 17 ตัวที่ทำงานบนเซิร์ฟเวอร์ใหม่

คำถามที่พบบ่อย

ทำไมงานรันได้ในเทอร์มินัลแต่ไม่รันใน cron?

สาเหตุที่พบบ่อยมีสามข้อ cron รันงานด้วย sh ไม่ใช่ bash ไวยากรณ์ที่มีเฉพาะใน bash จึงล้มเหลว เครื่องมือที่ติดตั้งในโฮมไดเรกทอรี เช่น nvm, pipx หรือ cargo ไม่อยู่ใน PATH ของ cron จึงควรใช้พาธเต็ม และเครื่องหมาย % ใด ๆ ในบรรทัดคำสั่งจะตัดคำสั่งตรงนั้น: บนเซิร์ฟเวอร์ของเรา date +%F กลายเป็น date + ให้เขียนเป็น \% หรือย้ายคำสั่งไปไว้ในสคริปต์

ผลลัพธ์ของ cron ไปไหน?

โดยค่าเริ่มต้นจะส่งไปทางอีเมล อิมเมจของเราไม่มีเมลเซิร์ฟเวอร์ cron จึงบันทึกว่า 'No MTA installed, discarding output' และผลลัพธ์หายไป ให้เพิ่ม >> /var/log/myjob.log 2>&1 ต่อท้ายงาน หรือเปลี่ยนไปใช้ตัวจับเวลา systemd ที่ผลลัพธ์เข้า journal เองอัตโนมัติ

ตัวจับเวลา systemd ดีกว่า cron อย่างไร?

มีล็อกใน journalctl ไม่เริ่มสำเนาที่สองของงานที่ยังรันอยู่ รันชดเชยด้วย Persistent=true สำหรับรอบที่พลาดขณะเซิร์ฟเวอร์ปิด และมีบรรทัดสถานะบอกรอบถัดไปกับรอบล่าสุด cron ชนะเรื่องความกระชับ: บรรทัดเดียวแทนสองไฟล์

ตัวจับเวลาของฉันทำงานช้าไป 18 วินาที ทำไม?

โดยค่าเริ่มต้น systemd จะรวมตัวจับเวลาไว้ในหน้าต่างหนึ่งนาทีเพื่อลดการปลุกระบบ นี่คือการตั้งค่า AccuracySec ตัวจับเวลาทุกสองนาทีของเราทำงานเวลา 07:42:18 แทนที่จะเป็น 07:42:00 เมื่อตั้ง AccuracySec=1s มันทำงานตรง 07:46:00 พอดี ให้ตั้งค่านี้เมื่อนาทีที่แน่นอนสำคัญ

cron และตัวจับเวลาใช้เขตเวลาไหน?

ใช้เขตเวลาของเซิร์ฟเวอร์ ซึ่งบนอิมเมจของเราคือ UTC ตั้งครั้งเดียวด้วย timedatectl (ดูคู่มือเรื่องชื่อโฮสต์และเขตเวลาของเรา) แล้วทั้ง cron และตัวจับเวลาจะใช้ตาม ตัวจับเวลายังกำหนดเขตเวลาของตัวเองได้: OnCalendar=*-*-* 03:00 Europe/Kyiv ทำงานเวลา 01:00 UTC ในการทดสอบของเรา

ความคิดเห็น

ยังไม่มีความคิดเห็น เป็นคนแรกสิ

แสดงความคิดเห็น

ความคิดเห็นจะถูกตรวจสอบก่อนแสดง