دليل عملي

‏cron مقابل systemd timers: أتمتة VPS Linux

الأتمتة12 دقيقةً للقراءةعدد الخطوات: 13

على أي ‎VPS‎ يعمل بـ‎Linux‎، الأتمتة ضرورة لا رفاهية: النسخ الاحتياطية الليلية، وتجديد شهادات ‎TLS‎، وتنظيف السجلات، وفحوصات الصحة — هذه المهام إما تعمل وحدها أو لا تعمل أبداً. ‎cron‎ يهيمن منذ عقود، لكن ‎systemd timers‎ تقدم تكاملاً أعمق مع مدير الخدمات.

المحتويات· لماذا أتمتة المهام على VPS؟1/12
  1. 01لماذا أتمتة المهام على VPS؟
  2. 02حالات الاستخدام الشائعة على VPS
  3. 03‏cron: مراجعة سريعة
  4. 04‏systemd timers: مقدمة
  5. 05‏cron مقابل systemd timers: جدول مقارنة
  6. 06متى تبقى على cron؟
  7. 07متى تنتقل إلى systemd timers؟
  8. 08إنشاء systemd timer من الصفر (مثال: نسخ احتياطي يومي)
  9. 09هجرة مهمة cron موجودة إلى systemd
  10. 10التحقق من صحة تعبيرات OnCalendar وتصحيحها
  11. 11استكشاف أخطاء systemd timers الشائعة
  12. 12خلاصة

لماذا أتمتة المهام على VPS؟

‎VPS‎ هو خادم يعمل باستمرار، غالباً دون إشراف بشري مباشر. لهذا السبب بالذات تُعدّ أتمتة المهام المتكررة أمراً جوهرياً: لن يكون هناك أحد في الساعة الثالثة صباحاً لتشغيل النسخ الاحتياطي أو تجديد شهادة ‎Let's Encrypt‎. مهمة منسية قد تعني فقدان بيانات، أو شهادة منتهية الصلاحية، أو قرصاً ممتلئاً.

في الممارسة العملية، خمس فئات من المهام تظهر على كل ‎VPS Linux‎ جاد: تدوير سجلات ‎Nginx‎ لمنع امتلاء ‎/var/log/nginx/‎ بالجيجابايت؛ تنظيف صور ‎Docker‎ الأيتام (docker image prune -af) لاسترداد مساحة القرص؛ النسخ الاحتياطي لـ‎PostgreSQL‎ إلى تخزين بعيد؛ التحقق من شهادة ‎SSL‎ قبل انتهائها لتشغيل certbot؛ وأخيراً نصوص الجمع الخفيف المجدولة التي تحتاج إلى التشغيل في وقت محدد. الأداتان الرئيسيتان المتاحتان على ‎Linux‎ — ‎cron‎ و‎systemd timers‎ — تتيحان جدولة هذه العمليات بصورة موثوقة.

حالات الاستخدام الشائعة على VPS

  • النسخ الاحتياطية — تفريغ ‎MySQL‎ أو ‎PostgreSQL‎ إلى تخزين بعيد (pg_dump -Fc mydb | gzip > /backup/mydb_$(date +%F).sql.gz)، أرشفة الملفات إلى تخزين ‎S3‎
  • تجديد شهادات ‎TLS‎ — certbot renew أو acme‎.sh --cron مجدولان مرتين يومياً مع إعادة تحميل ‎Nginx‎ تلقائياً
  • تدوير السجلات وتنظيفها — حذف الملفات الأقدم من 30 يوماً (find /var/log -mtime +30 -delete)، ضغط أسبوعي، تفريغ سجلات التطبيقات
  • تنظيف ‎Docker‎ — docker image prune -af، حذف المجلدات الأيتام، تدوير سجلات الحاويات لتحرير ‎/var/lib/docker‎
  • فحوصات الصحة — التحقق من أن الخدمة تستمع (curl -sf http://localhost:3000/health)، إعادة التشغيل التلقائي عند الحاجة
  • مزامنة البيانات — rsync إلى عقدة ثانية، تحديث ذاكرة التخزين المؤقت
  • المهام التطبيقية المجدولة — إرسال رسائل تذكير، حساب تقارير دورية، فهرسة المحتوى، نصوص الجمع الخفيف

‏cron: مراجعة سريعة

‎cron‎ موجود في كل توزيعات ‎Linux‎ تقريباً. لجدولة مهمة، يمكنك تحرير ‎crontab‎ المستخدم الحالي بالأمر crontab -e. تعتمد الصياغة على خمسة حقول مفصولة بمسافات تليها الأمر:

# الدقيقة (0-59)، الساعة (0-23)، يوم الشهر (1-31)، الشهر (1-12)، يوم الأسبوع (0-7)
  0  3  *  *  *  /usr/local/bin/backup.sh

الاختصارات ‎@daily‎ و‎@weekly‎ و‎@reboot‎ تبسّط الحالات الشائعة. الملفات في ‎/etc/cron.d/‎ تخص النظام وتحدد مستخدم التنفيذ مباشرة. لمراجعة التنفيذات السابقة: ‎/var/log/syslog‎ مع grep CRON /var/log/syslog، أو journalctl -t CRON. نقطة مهمة: ‎PATH‎ الافتراضي في ‎cron‎ محدود جداً (/usr/bin:/bin)، مما يستلزم استخدام المسارات المطلقة في النصوص، أو إعادة تعريف ‎PATH=‎ في بداية ‎crontab‎. لا يحتفظ ‎cron‎ بأي سجل أصلي للتنفيذات السابقة ولا يتعامل مع المهام الفائتة إذا كانت الآلة مغلقة.

‏systemd timers: مقدمة

‎systemd timer‎ هو زوج من ملفَي وحدة: ملف ‎.service‎ يصف ما يجب تنفيذه، وملف ‎.timer‎ يصف موعد تشغيله. يُفعَّل الـ‎timer‎ بالأمر systemctl enable --now myservice‎.timer ويُدار بواسطة مدير الخدمات.

توجيه OnCalendar= يقبل صياغة غنية:
- daily — كل يوم عند منتصف الليل
- Mon *-*-* 03:00:00 — كل اثنين الساعة الثالثة
- *-*-* 02:30:00 — كل يوم الساعة 2:30
- *:0/15 — كل 15 دقيقة

توجيه AccuracySec= (الافتراضي: دقيقة واحدة) يتيح لـ‎systemd‎ تجميع الـ‎timers‎ المتقاربة لتوفير استيقاظ المعالج. توجيه Persistent=true في كتلة [Timer] هو الميزة الجوهرية: إذا كانت الآلة مغلقة عند الموعد المحدد، يُعيد الـ‎timer‎ التنفيذ الفائت عند الإقلاع التالي. تمر جميع المخرجات عبر ‎journald‎. الأمر systemctl list-timers يُظهر قائمة بجميع الـ‎timers‎ النشطة مع موعدها التالي وآخر تفعيل.

‏cron مقابل systemd timers: جدول مقارنة

مرّر الجدول أفقيًا

المعيارcronsystemd timer
صياغة الجدولةخمسة حقول `* * * * *``OnCalendar=` مقروء (`daily`، `Mon 03:00`)
السجلاتبريد إلكتروني أو إعادة توجيه يدوية، ‎/var/log/syslog‎‎journald‎ أصلي، `journalctl -u`
المهام الفائتة (الآلة مغلقة)مفقودة (إلا مع `anacron`)`Persistent=true` يعيد التشغيل بعد إعادة التشغيل
التبعيات بين الخدماتلا يوجد`After=`، `Requires=`، `Wants=`
العزل وحدود الموارديرث بيئة الـ‎shell‎، ‎PATH‎ محدود‎Cgroups‎، `MemoryMax=`، `CPUQuota=`
إشعار الفشللا يوجد بشكل أصلي`OnFailure=notify.service` يُشغّل تنبيهاً
التشغيل كمستخدم محددحقل المستخدم في ‎/etc/cron.d/‎`User=` و`Group=` في ملف ‎.service‎
تشخيص الأخطاءصعب بدون سجلات`systemctl status`، `journalctl -xe`
التوفرجميع أنظمة ‎Unix‎ (حاويات، ‎BSD‎، ‎Alpine‎)الأنظمة مع ‎systemd‎ (‎Debian‎، ‎Ubuntu‎، ‎RHEL‎…)

متى تبقى على cron؟

يبقى ‎cron‎ الخيار الصحيح في عدة حالات عملية. على الأنظمة القديمة أو المُخففة بدون ‎systemd‎ — بعض الحاويات، و‎BSD‎، وصور ‎Alpine‎ — يكون ‎cron‎ في الغالب الأداة الوحيدة المتاحة. في البيئات متعددة المستخدمين حيث يدير كل مستخدم مهامه الخاصة عبر crontab -e، يكون ‎cron‎ أبسط للتفويض دون منح صلاحيات ‎root‎.

لـالنصوص البسيطة من سطر واحد — تجديد certbot أسبوعياً، ‎rsync‎ ليلياً بدون تبعيات — تبقى ‎crontab‎ مقروءة وقابلة للصيانة دون إنشاء ملفين. ‎cron‎ هو أيضاً الخيار الصحيح لـتأهيل فريق غير متخصص في ‎Linux‎: صياغة الخمسة حقول، الموثقة في كل مكان، أكثر سهولة من وحدة ‎systemd‎. المعيار الحاسم هو الحاجة إلى سجلات منظمة أو تبعيات بين الخدمات أو قيود على الموارد — إن لم تكن بحاجة إليها، فـ‎cron‎ يؤدي المهمة.

متى تنتقل إلى systemd timers؟

تصبح ‎systemd timers‎ الخيار الواضح حين تتجاوز المهمة نطاق أمر بسيط ومعزول. تحتاج ‎journald‎ لتتبع كل تنفيذ ومخرجاته ورمز إرجاعه دون تمديد يدوي؟ ‎systemd timers‎. يجب أن يعمل سكريبت النسخ الاحتياطي فقط بعد أن يصبح ‎PostgreSQL‎ جاهزاً (After=postgresql‎.service)؟ ‎systemd timers‎.

التبعيات بين الخدمات هي الحجة الأقوى: نسخ احتياطي يُشغَّل وقاعدة البيانات غير جاهزة بعد إعادة التشغيل ينتج ملفاً فارغاً دون أي خطأ مرئي في ‎cron‎. ‎systemd‎ يضمن الترتيب. التسجيل المدمج عبر ‎journald‎ يزيل كل ضجيج إعادة التوجيه اليدوية. معالجة الأخطاء مع OnFailure= تتيح تشغيل إشعار (بريد إلكتروني، ويب هوك) دون تلويث النص البرمجي نفسه. وإذا أُعيد تشغيل الخادم الساعة 2:58 بينما كان من المقرر تشغيل النسخ الاحتياطية الساعة 3، فإن Persistent=true يضمن تشغيلها عند الإقلاع التالي.

إنشاء systemd timer من الصفر (مثال: نسخ احتياطي يومي)

  1. إنشاء ملف الخدمة

    افتح ‎/etc/systemd/system/backup‎.service‎ وأدخل:

    [Unit]
    Description=Daily PostgreSQL backup
    After=postgresql.service
    
    [Service]
    Type=oneshot
    User=postgres
    ExecStart=/usr/local/bin/backup.sh

    النوع oneshot يشير إلى أن الخدمة تنتهي بعد تنفيذ النص البرمجي — النوع المعتاد للمهام المجدولة.

  2. إنشاء ملف الـ timer

    أنشئ ‎/etc/systemd/system/backup‎.timer‎ بـ:

    [Unit]
    Description=Daily trigger for backup
    
    [Timer]
    OnCalendar=*-*-* 03:00:00
    Persistent=true
    
    [Install]
    WantedBy=timers.target

    Persistent=true يضمن أنه إذا كانت الآلة مغلقة الساعة 3، يُشغَّل النسخ الاحتياطي عند الإقلاع التالي.

  3. إعادة تحميل systemd وتفعيل الـ timer

    نفِّذ systemctl daemon-reload حتى يأخذ ‎systemd‎ الملفات الجديدة بالاعتبار، ثم systemctl enable --now backup‎.timer لتفعيل الـ‎timer‎ وتشغيله فوراً.

  4. التحقق مع systemctl status

    تحقق من حالة الـ‎timer‎ بـ systemctl status backup‎.timer — يجب أن تُظهر الإخراج ‎active (waiting)‎ مع تاريخ الإطلاق التالي. راجع أيضاً حالة الخدمة بعد أول تنفيذ بـ systemctl status backup‎.service.

  5. إدراج جميع الـ timers النشطة

    الأمر systemctl list-timers --all يعرض جدولاً بجميع ‎timers‎ النظام مع ثلاثة أعمدة رئيسية: ‎NEXT‎ (التنفيذ التالي)، ‎LEFT‎ (الوقت المتبقي) و‎LAST‎ (آخر تنفيذ). هذه لوحة التحكم للتحقق من أن ‎timers‎ تُطلَق كما هو متوقع.

  6. قراءة السجلات

    اطّلع على كل مخرجات الخدمة بـ journalctl -u backup‎.service للتاريخ الكامل، أو journalctl -u backup‎.service -n 50 --since today لآخر 50 سطراً اليوم.

  7. إضافة إشعار خطأ (اختياري)

    للتنبيه عند الفشل، أضف في كتلة [Unit] من ملف ‎.service‎:

    OnFailure=notify-failure@%n.service

    هذه الخدمة يمكنها إرسال بريد إلكتروني أو ويب هوك ‎Slack‎ أو تنبيه ‎ntfy‎ حسب بنيتك التحتية.

  8. اختبار الخدمة يدوياً

    قبل انتظار الإطلاق المجدول، اختبر التنفيذ الفوري بـ systemctl start backup.service (بدون ‎.timer‎). راجع النتيجة فوراً بـ journalctl -u backup.service -n 20. إذا نجح النص البرمجي، فالإعداد مُتحقَّق منه.

هجرة مهمة cron موجودة إلى systemd

  1. تحديد مهمة cron المراد هجرتها

    أدرج ‎crontabs‎ بـ crontab -l (المستخدم الحالي) وcat /etc/cron.d/* (النظام). لاحظ الأمر الدقيق، ومستخدم التنفيذ، والجدولة ذات الخمسة حقول.

  2. ترجمة الجدولة إلى OnCalendar

    صيغة OnCalendar= في ‎systemd‎ تُكتب DayOfWeek Year-Month-Day Hour:Minute:Second. يصبح 30 2 * * 1 هو Mon *-*-* 02:30:00.

    لاختبار الترجمة، استخدم systemd-analyze calendar 'Mon *-*-* 02:30:00' الذي يعرض المرات العشر التالية المحسوبة.

  3. إنشاء ملفَي ‎.service‎ و‎.timer‎

    أنشئ ‎/etc/systemd/system/certbot-renew‎.service‎ بـ Type=oneshot، ExecStart=/usr/bin/certbot renew --quiet، وUser=root. ثم أنشئ ‎/etc/systemd/system/certbot-renew‎.timer‎ بـ OnCalendar=Mon *-*-* 02:30:00 وPersistent=true. نفِّذ systemctl daemon-reload && systemctl enable --now certbot-renew‎.timer.

  4. اختبار الخدمة قبل تعطيل cron

    اختبر فوراً بـ systemctl start certbot-renew‎.service وراجع النتيجة عبر journalctl -u certbot-renew‎.service -n 20. تحقق من ظهور الـ‎timer‎ في systemctl list-timers. لا تعطّل سطر cron إلا بعد التحقق.

  5. تعطيل سطر cron

    بمجرد التحقق من الـ‎timer‎، علِّق أو احذف السطر في ‎crontab‎ الأصلي. اترك الاثنين يعملان بالتوازي بضعة أيام إذا كانت المهمة حرجة — ‎cron‎ و‎systemd timers‎ لا يتعارضان.

التحقق من صحة تعبيرات OnCalendar وتصحيحها

قبل تفعيل ‎timer‎ في الإنتاج، تحقق دائماً من تعبير OnCalendar= بـ systemd-analyze calendar:

systemd-analyze calendar "daily"
systemd-analyze calendar "Mon *-*-* 02:30:00"
systemd-analyze calendar "*:0/15"

كل أمر يعرض المرة التالية والعشر التي تليها — مما يجعل أي تعبير سيئ التكوين أو إزاحة زمنية غير متوقعة واضحة فوراً. للاطلاع على نظرة شاملة لجميع ‎timers‎ النشطة، systemctl list-timers --all هو لوحة التشغيل اليومية.

استكشاف أخطاء systemd timers الشائعة

تظهر خمس مشاكل بشكل متكرر عند إعداد الـ‎timers‎.

1. "Unit not found" عند systemctl enable — الملف ‎.timer‎ أو ‎.service‎ غير موجود في ‎/etc/systemd/system/‎ أو اسمه غير صحيح. تحقق بـ ls /etc/systemd/system/*.timer. daemon-reload إلزامي قبل أي enable أو start على ملف جديد.

2. الـ timer نشط لكنه لا يُطلَق — هل يُظهر systemctl list-timers تاريخاً منطقياً في عمود NEXT؟ إذا كان NEXT فارغاً أو في الماضي، غالباً Persistent=true منسي مع آلة لم تُعَد تشغيلها منذ إنشاء الـ‎timer‎. تحقق من systemctl status backup‎.timer: ‎active (waiting)‎ هو الحالة الطبيعية.

3. ExecStart غير موجود أو رُفض الإذن — استخدم دائماً المسار المطلق في ExecStart= (‎/usr/bin/certbot‎ وليس ‎certbot‎). تحقق من أن النص البرمجي قابل للتنفيذ (chmod +x /usr/local/bin/backup‎.sh) وأن المستخدم المُعلَن في User= يملك الوصول إليه.

4. صلاحيات على النص البرمجي — إذا كان User=postgres لكن النص البرمجي مملوك لـ‎root‎ بدون صلاحية تنفيذ لـ‎postgres‎، ستفشل الخدمة بالرمز 203/EXEC. راجع journalctl -u backup‎.service -n 20 لرؤية الرسالة الدقيقة.

5. تعارض بين cron وsystemd على نفس المهمة — إذا كنت تهاجر تدريجياً، تأكد من عدم تفعيل الاثنين في نفس الوقت لمهمة تدميرية (تنظيف سجلات، تدوير ملفات). كلاهما سيُنفَّذ باستقلالية ويضاعف العمليات. التحقق بسيط: crontab -l | grep backup وsystemctl is-active backup.timer.

خلاصة

‎cron‎ و‎systemd timers‎ يتعايشان بلا مشاكل على نفس الخادم — لا يلزمك ترحيل كل شيء دفعة واحدة. احتفظ بـ‎cron‎ لمهامك البسيطة، واعتمد ‎systemd timers‎ للأتمتة الجديدة التي تستفيد من سجلات ‎journald‎ وتبعيات الخدمات أو عزل ‎cgroup‎.

VPS Linux جاهز لأتمتتك

‪VPS‬ السحابية لدينا التي تعمل بـ‪Ubuntu‬ و‪Debian‬ تتضمن ‪systemd‬ و‪journald‬ والمكدس الحديث الكامل. انشر ‪timers‬ وسكريبتاتك ونسخك الاحتياطية على بنية تحتية موثوقة.

بحاجة إلى مساعدة؟

تصفّح مركز المساعدة والأسئلة الشائعة، أو تواصل مع فريقنا — معاودة اتصال أو WhatsApp أو بريد إلكتروني. الدعم بـالعربية والفرنسية والإنجليزية.

راسلنا على WhatsAppيُفتح في علامة تبويب جديدة