العودة إلى المدونة

مهام Cron على خادم Linux VPS: جدولة المهام دون مفاجآت

فريق تحرير IM Host25 أكتوبر 20267 دقائق قراءة
مهام Cron على خادم Linux VPS: جدولة المهام دون مفاجآت

Cron هو الطريقة المعتادة لتشغيل أمر وفق جدول زمني في Linux: نسخ احتياطي ليلي، وتنظيف الملفات المؤقتة، وإرسال التقارير، وتشغيل طوابير المهام. تعلّم صيغته يستغرق خمس دقائق، أما المفاجآت فتأتي لاحقًا، عندما لا تفعل مهمة تعمل جيدًا في الطرفية أي شيء في الثالثة فجرًا. يشرح هذا الدليل الصيغة، ثم الأمور الأربعة التي تسبب معظم أعطال Cron: البيئة، وعلامة النسبة المئوية، والسجلات، والتشغيل المتداخل.

أين توجد مهام Cron

لكل مستخدم ملف crontab خاص به. عدّله بالأمر crontab -e، ولا تعدّل الملفات داخل /var/spool/cron مباشرة، لأن المحرر يتحقق من الصيغة عند الحفظ.

crontab -e        # edit your own crontab
crontab -l        # list it
sudo crontab -e   # edit root's crontab

وهناك أيضًا مواقع على مستوى النظام: الملف /etc/crontab والملفات داخل /etc/cron.d/. للأسطر هنا حقل إضافي هو اسم المستخدم الذي يشغّل الأمر:

# /etc/cron.d/backup  (note the extra user field)
30 3 * * *  root  /usr/local/bin/backup.sh

ثم هناك المجلدات /etc/cron.hourly و/etc/cron.daily و/etc/cron.weekly و/etc/cron.monthly. ضع سكربتًا قابلًا للتنفيذ في أحدها فيعمل وفق ذلك الجدول. وهنا فخ في Debian وUbuntu: هذه المجلدات يشغّلها run-parts، وهو يتجاهل أسماء الملفات التي تحتوي على نقطة. سكربت باسم backup.sh داخل /etc/cron.daily لن يعمل أبدًا؛ سمّه backup. الأمر run-parts --test /etc/cron.daily يعرض ما سيعمل فعلًا.

الحقول الخمسة

# ┌──────── minute (0-59)
# │ ┌────── hour (0-23)
# │ │ ┌──── day of month (1-31)
# │ │ │ ┌── month (1-12)
# │ │ │ │ ┌ day of week (0-7, 0 and 7 = Sunday)
# │ │ │ │ │
*/5 * * * *   /usr/local/bin/check-queue.sh
30 3 * * *    /usr/local/bin/backup.sh
0 9 * * 1-5   /usr/local/bin/report.sh
0 0 1 * *     /usr/local/bin/monthly-cleanup.sh
@reboot       /usr/local/bin/start-worker.sh

يمكن أن يكون الحقل رقمًا، أو قائمة (1,15)، أو نطاقًا (1-5)، أو خطوة (*/5 أي كل 5 وحدات). والاختصارات @reboot و@daily و@hourly متاحة أيضًا.

هناك قاعدة تفاجئ الجميع تقريبًا: عند تحديد يوم الشهر ويوم الأسبوع معًا، يشغّل Cron المهمة عند تطابق أيٍّ منهما، لا كليهما:

# runs on the 13th of every month AND on every Friday
0 0 13 * 5  /usr/local/bin/job.sh

إذا احتجت "الجمعة الموافقة لـ 13" فجدول المهمة كل جمعة وتحقق من التاريخ داخل السكربت. وتذكّر أن Cron يستخدم المنطقة الزمنية للخادم؛ تحقق منها بالأمر timedatectl قبل جدولة أي مهمة مرتبطة بساعات العمل.

المشكلة الأولى: بيئة Cron شبه فارغة

الطرفية التفاعلية تحمّل .bashrc و.profile وإضافاتك إلى PATH وأدوات مثل nvm وpyenv. أما Cron فلا يحمّل شيئًا من ذلك. يشغّل الأمر بواسطة /bin/sh مع PATH محدود (في Ubuntu عادة /usr/bin:/bin فقط)، ويكون مجلد العمل هو المجلد الرئيسي للمستخدم. النتيجة مهمة تعمل يدويًا وتفشل تحت Cron برسالة مثل:

/bin/sh: 1: mariadb-dump: not found
(CRON) info (No MTA installed, discarding output)

ثلاثة حلول، الأفضل أولًا:

  1. استخدم المسار الكامل لكل برنامج وملف في المهمة. الأمر which mariadb-dump يعطيك المسار.

  2. عرّف البيئة في أعلى ملف crontab، فـ Cron يقبل أسطرًا بصيغة NAME=value.

  3. ضع العمل في سكربت يحدد PATH بنفسه وينتقل إلى المجلد الصحيح بالأمر cd.

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""

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

ولتحاكي بيئة Cron قبل أن تعتمد على المهمة، شغّلها ببيئة شبه فارغة:

env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME="$HOME" /bin/sh -c '/usr/local/bin/backup.sh'

إذا فشلت هنا فستفشل تحت Cron.

المشكلة الثانية: علامة النسبة المئوية

في سطر crontab، علامة % غير المهرّبة تُنهي الأمر، وكل ما بعدها يُرسل إلى الأمر كمدخلات قياسية. هذا يكسر أشهر سطر للنسخ الاحتياطي على الإنترنت:

# wrong: cron cuts the line at the first %
0 2 * * *  mariadb-dump --all-databases | gzip > /var/backups/db-$(date +%F).sql.gz

# right
0 2 * * *  mariadb-dump --all-databases | gzip > /var/backups/db-$(date +\%F).sql.gz

هرّب كل % بشرطة مائلة عكسية، أو انقل الأمر إلى سكربت حيث لا يكون لـ % معنى خاص. وقاعدة صغيرة أخرى: يجب أن ينتهي ملف crontab بسطر جديد. معظم المحررات تضيفه، لكن الملف المثبت بالأمر crontab file.txt قد لا يحتويه، فتُتجاهل المهمة الأخيرة.

المشكلة الثالثة: المخرجات تضيع

يرسل Cron افتراضيًا أي مخرجات بالبريد إلى صاحب المهمة. وعلى معظم خوادم VPS لا يوجد خادم بريد، فتُحذف المخرجات ويسجّل syslog رسالة "No MTA installed, discarding output"، وتختفي معها رسائل الخطأ.

وجّه المخرجات القياسية ومخرجات الأخطاء معًا إلى ملف سجل يمكنك قراءته: >> /var/log/backup.log 2>&1 كما في المثال السابق. الترتيب مهم: يجب أن تأتي 2>&1 بعد التوجيه إلى الملف. ولا يستطيع crontab الخاص بمستخدم عادي الكتابة إلا حيث يملك هذا المستخدم الصلاحية، فاستخدم مسارًا مثل $HOME/logs/.

وللمهام الأطول، اجعل السكربت يكتب سجله بنفسه مع الوقت، فتعرف متى بدأ كل تشغيل وهل انتهى:

#!/bin/bash
set -euo pipefail
exec >> /var/log/backup.log 2>&1
echo "[$(date -Is)] start"

# ... the real work ...

echo "[$(date -Is)] done"

وللتأكد من أن Cron شغّل المهمة أصلًا، راجع سجل خدمة Cron نفسها:

journalctl -u cron --since today
grep CRON /var/log/syslog | tail -n 20

ملفات السجل تكبر بلا توقف ما لم تدوّرها. أضف ملفًا صغيرًا في /etc/logrotate.d/ لكل سجل تنشئه.

المشكلة الرابعة: التشغيل المتداخل

لا يعرف Cron هل انتهى التشغيل السابق أم لا. إذا كانت مهمة مجدولة كل 5 دقائق تستغرق أحيانًا 7 دقائق، فستعمل نسختان في الوقت نفسه. ومع النسخ الاحتياطي أو الاستيراد أو معالجة الطوابير يعني ذلك ملفات تالفة أو عملًا مكررًا، أو خادمًا يبطؤ حتى يصبح كل تشغيل أطول من 5 دقائق فتتراكم النسخ.

أداة flock جزء من util-linux ومثبتة على كل خادم Ubuntu، وتحل المشكلة ببادئة واحدة:

30 3 * * *  flock -n /run/lock/backup.lock timeout 2h /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  • flock -n يأخذ قفلًا على الملف. إذا كان تشغيل آخر يملك القفل، يخرج التشغيل الجديد فورًا بدل الانتظار.

  • يُحرَّر القفل تلقائيًا عند انتهاء العملية، حتى لو انهارت، فلا يبقى ملف PID قديم يحتاج إلى تنظيف.

  • timeout 2h ينهي التشغيل المعلّق، فلا تمنع مهمة عالقة كل التشغيلات التالية.

استخدم ملف قفل منفصلًا لكل مهمة. المجلد /run/lock مكان مناسب لأنه في الذاكرة ويُفرَّغ عند الإقلاع.

البديل: مؤقتات systemd

يستخدم Ubuntu نظام systemd أصلًا، ومؤقتات systemd (timers) تستطيع أن تحل محل مهام Cron. يحتاج المؤقت إلى ملفين صغيرين: خدمة تحدد ما يُشغَّل، ومؤقت يحدد متى.

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup every night

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=10m

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers
journalctl -u backup.service -n 50
systemd-analyze calendar "Mon..Fri *-*-* 09:00"

ما الذي تكسبه:

  • سجلات تلقائية. كل المخرجات تذهب إلى journal مع الوقت ورمز الخروج، والأمر systemctl status backup.service يوضح هل فشل التشغيل الأخير.

  • لا تداخل. لا يبدأ systemd خدمة ما زالت تعمل، فلا تحتاج إلى flock.

  • تعويض التشغيل الفائت. الإعداد Persistent=true يشغّل المهمة التي فاتت أثناء إيقاف الخادم بمجرد إقلاعه.

  • تحكم كامل في البيئة والحدود. يمكنك تحديد User= وWorkingDirectory= وEnvironment= وحدود الذاكرة والمعالج في ملف الخدمة.

وما الذي تدفعه: ملفان بدل سطر واحد، وصيغة تقويم تحتاج إلى مراجعتها. الأمر systemd-analyze calendar يعرض موعد التشغيل التالي لأي تعبير، فينهي التخمين.

الحالةقاعدة تقريبية
أمر بسيط من سطر واحد وقصير المدةCron
مهمة يجب ألا تتداخل أو يجب أن تعوّض ما فاتها بعد التوقفمؤقت systemd (أو Cron مع flock)
تحتاج إلى معرفة كل مرة فشلت فيهامؤقت systemd
مُجدوِل التطبيق (Laravel أو ووردبريس)سطر Cron واحد يستدعي مُجدوِل التطبيق كل دقيقة

قائمة تحقق قصيرة

  • مسارات كاملة، أو PATH محدد في أعلى crontab.

  • كل علامة % مهرّبة.

  • المخرجات موجهة إلى ملف سجل مع تدويره.

  • flock -n لأي مهمة قد تستغرق أطول من الفاصل بين تشغيلين.

  • اختبار المهمة مرة واحدة بالأمر env -i قبل الاعتماد عليها.

شغّلها على IM HOST

يحتاج Cron ومؤقتات systemd إلى خادم يعمل باستمرار ويمنحك صلاحيات root. تبدأ خطط Linux VPS من IM HOST من 5 دولارات شهريًا مع صلاحيات root كاملة على KVM، والنسخ الاحتياطي اليومي متاح كإضافة اختيارية. وتشمل خطط Cloud VPS نسخًا احتياطيًا تلقائيًا كل 3 أيام طوال مدة الاشتراك. وإذا كانت مهامك المجدولة في الغالب استدعاءات API ونقل بيانات بين التطبيقات، فإن استضافة n8n تأتي مع n8n مثبتًا مسبقًا وعدد غير محدود من سير العمل وعمليات التنفيذ. كل الخطط تشمل دعمًا من المهندسين على مدار الساعة وضمان استرداد المبلغ خلال 30 يومًا.

المزيد من مدونتنا

اكتشف المزيد من الأدلة العملية ورؤى الخدمات من فريق IM Host.

عرض كل المقالات