EQVPS
לוח בקרההתחלה

איך מתזמנים משימות ב-VPS עם cron וטיימרים של systemd

הריצו גיבויים, ניקויים וסקריפטים לפי לוח זמנים. cron בחמש דקות עם שלוש המלכודות ששוברות משימות בשקט, ואחר כך טיימרים של systemd ללוגים, להשלמת הרצות אחרי השבתה ולזמן מדויק. כל התנהגות למטה נבדקה על שרת Ubuntu 24.04 חדש.

שרת מצדיק את מחירו כשהוא עובד בזמן שאתם ישנים: גיבויים, סבב לוגים, חידוש תעודות, דוח בתשע. ללינוקס יש שני מתזמנים לזה. 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 בימי ראשון וב-06:52 ב-1 בחודש. שימו שם סקריפט בר-הרצה והוא ירוץ יחד עם השאר.

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
הגדרהשורה אחתשני קבצים
לוגיםרק אם מפנים את הפלט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 בבדיקה שלנו.

תגובות

אין עדיין תגובות. היו הראשונים.

השאירו תגובה

התגובות עוברות מודרציה לפני שהן מופיעות.