المهام المجدولة: cron والمؤقتات
المهام المجدولة: cron والمؤقتات
كل نظام إنتاجي يُشغّل أعمالاً بجدول زمني: تدوير السجلات، وإعادة ضغط قواعد البيانات، وتجديد الشهادات، وإنشاء لقطات، وتدفئة الذاكرة المؤقتة، وتقارير الحالة. تتوفر آليتان على لينكس الحديث: cron الكلاسيكي ومؤقتات systemd. يجب على مهندس DevOps المتمرس أن يتقن الاثنين، ويعرف متى يختار كل منهما، ويفهم أوجه الفشل التي تعرقل الفرق في الإنتاج.
cron: المُجدِّد الكلاسيكي
يُشحن cron مع أنظمة Unix منذ عام 1975. لا يزال الأداة المناسبة للمهام البسيطة المملوكة للمستخدمين وللأنظمة التي لا يتوفر فيها systemd. يقرأ الخادم ملفات crontab ويُشغّل الأوامر عند تطابق الأوقات.
كل سطر في crontab يتبع مواصفة زمنية ثابتة من خمسة حقول متبوعة بالأمر:
إدارة ملفات Crontab
استخدم crontab -e لتعديل crontab المستخدم الحالي، وcrontab -l لسرده، وcrontab -r لحذفه. تذهب المهام الخاصة بالنظام والمملوكة لـ root إلى /etc/crontab أو كملفات منفصلة تحت /etc/cron.d/ — وتحتوي هذه على حقل إضافي لتحديد المستخدم الذي تُنفَّذ الأوامر باسمه:
>> /var/log/myapp/job.log 2>&1 وأرسل تلك السجلات إلى مُجمِّع السجلات المركزي.
مؤقتات systemd: البديل الحديث
مؤقتات systemd هي وحدات .timer تُنشّط وحدة .service مُقترنة بها. إنها أقوى من cron لأحمال العمل الإنتاجية لأنها تتكامل مع journald للتسجيل، وتدعم ترتيب التبعيات، ويمكنها اللحاق بالتشغيلات الفائتة عبر الاستمرارية، وتُصدر أحداثاً منظمة يمكن لأنظمة المراقبة جمعها.
المؤقت يقترن دائماً بوحدة .service بنفس الاسم الأساسي. سير العمل: اكتب الخدمة، اكتب المؤقت، مكّن المؤقت.
Persistent=true: إذا كان النظام مُغلَقاً عندما كان يُفترض أن يُطلق المؤقت، فإن Persistent=true يجعل systemd يُشغّل المهمة فوراً عند الإقلاع التالي إذا فُوّت آخر تشغيل. هذا ضروري للمهام الليلية على الخوادم التي قد تُعاد تشغيلها للصيانة. لا توفر cron آلية مماثلة.
صياغة OnCalendar
يستخدم systemd لغة تعبيرات تقويم خاصة به، وهي أكثر تعبيراً من صيغة cron ذات الخمسة حقول. استخدم systemd-analyze calendar للتحقق من أي تعبير قبل نشره:
مخطط: هندسة cron مقارنة بمؤقت systemd
أفضل ممارسات الجدولة في الإنتاج
تجنب قطيع الرعد
في أسطول من 50 خادماً، تُطلَق كل مهمة cron مضبوطة على 0 3 * * * في آنٍ واحد، مما يُنشئ ذروة حركة مرور على قواعد البيانات وواجهات البرمجة المشتركة. بالنسبة لـ cron، أضف سكوناً عشوائياً في بداية السكريبت: sleep $((RANDOM % 300)). بالنسبة لمؤقتات systemd، استخدم RandomizedDelaySec=300 في قسم [Timer] — يُطبّق systemd التأجيل لكل مضيف دون التعديل على السكريبت.
استخدم القفل لمنع التداخل
إذا استغرق تشغيل مهمة وقتاً أطول من فترتها، تبدأ نسخة ثانية بينما الأولى لا تزال تعمل. استخدم flock لمنع ذلك:
بالنسبة لخدمات systemd، اضبط Type=oneshot — لن يبدأ systemd نسخة ثانية بينما الأولى نشطة. ادمجها مع فحوصات ExecStartPre= عند الحاجة.
chronic من moreutils مع cron: ثبّت moreutils وأضف chronic قبل أوامر cron. يُثبّط المخرجات ما لم يخرج الأمر بحالة غير صفرية، فيصل بريد cron فقط عند حدوث فشل فعلي — عكس السلوك الافتراضي الذي يُنتج ضجيجاً في كل تشغيل. مثال: chronic /usr/local/bin/db-backup.sh
متى تستخدم cron مقابل مؤقتات systemd
استخدم cron عندما: تحتاج مهام خاصة بالمستخدم (crontab -e)، أو النظام المستهدف لا يحتوي على systemd (الحاويات، Alpine Linux، بعض الأنظمة المدمجة)، أو تصون بيئة قديمة حيث الاتساق أهم من الميزات.
استخدم مؤقتات systemd عندما: يجب أن تعتمد المهمة على خدمة شبكة أو قاعدة بيانات (After=)، أو تحتاج لحاقاً مضموناً بعد إعادة التشغيل (Persistent=true)، أو تريد حدوداً لموارد cgroup على عملية المهمة، أو تريد تدفق السجلات بصورة طبيعية إلى journald. على نطاق الشركات الكبرى، مؤقتات systemd هي المعيار للعمل المجدول على مستوى المضيف لأن قصة الملاحظة والموثوقية أفضل جوهرياً.
CronJob أو مُجدِّل موزع (Temporal، Celery Beat، AWS EventBridge). على الخوادم الفعلية أو الأجهزة الافتراضية، مؤقتات systemd هي الأداة الصحيحة.
تشخيص المهام المجدولة
عندما تفشل مهمة بصمت، اتّبع هذا التسلسل التشخيصي:
- تحقق من أن المؤقت أُطلق:
systemctl list-timers --all | grep job-name - تحقق من رمز الخروج للخدمة:
systemctl status job-name.service - اقرأ السجل الكامل:
journalctl -u job-name.service -n 100 --no-pager - شغّل الخدمة يدوياً لإعادة إنتاج المشكلة:
systemctl start job-name.service - بالنسبة لـ cron: تحقق من
/var/log/syslogأو/var/log/cronللسطور الخاصة بالخادم، ثم تحقق مما إذا كان السكريبت يستخدم متغيرات بيئة لا يُصدرها cron (PATH، HOME — اضبط هذه دائماً صراحةً في أعلى سكريبتات cron).