لماذا أتمتة المهام على VPS؟
VPS هو خادم يعمل باستمرار، غالباً دون إشراف بشري مباشر. لهذا السبب بالذات تُعدّ أتمتة المهام المتكررة أمراً جوهرياً: لن يكون هناك أحد في الساعة الثالثة صباحاً لتشغيل النسخ الاحتياطي أو تجديد شهادة Let's Encrypt. مهمة منسية قد تعني فقدان بيانات، أو شهادة منتهية الصلاحية، أو قرصاً ممتلئاً. الأداتان الرئيسيتان المتاحتان على Linux — cron وsystemd timers — تتيحان جدولة هذه العمليات بصورة موثوقة.
حالات الاستخدام الشائعة على VPS
- النسخ الاحتياطية — تفريغ MySQL أو PostgreSQL، أرشفة الملفات إلى تخزين بعيد
- تجديد شهادات TLS —
certbot renewأوacme.sh --cronمجدولان مرتين يومياً - تدوير السجلات وتنظيفها — حذف الملفات الأقدم من 30 يوماً، ضغط أسبوعي
- فحوصات الصحة — التحقق من أن الخدمة تستمع، إعادة التشغيل التلقائي عند الحاجة
- مزامنة البيانات —
rsyncإلى عقدة ثانية، تحديث ذاكرة التخزين المؤقت - تنظيف البيئات — حذف الجلسات المنتهية، تفريغ المجلدات المؤقتة
cron: مراجعة سريعة
cron موجود في كل توزيعات Linux تقريباً. لجدولة مهمة، يمكنك تحرير crontab المستخدم الحالي بالأمر crontab -e. تعتمد الصياغة على خمسة حقول مفصولة بمسافات: الدقيقة، الساعة، اليوم من الشهر، الشهر، يوم الأسبوع، تليها الأمر. لا يحتفظ cron بأي سجل أصلي للتنفيذات السابقة ولا يتعامل مع المهام الفائتة إذا كانت الآلة مغلقة.
systemd timers: مقدمة
systemd timer هو زوج من ملفَي وحدة: ملف .service يصف ما يجب تنفيذه، وملف .timer يصف موعد تشغيله. يُفعَّل الـtimer بالأمر systemctl enable --now myservice.timer ويُدار بواسطة مدير الخدمات. توجيه OnCalendar= يقبل صياغة غنية: daily، أو Mon *-*-* 03:00:00. تمر جميع المخرجات عبر journald. الأمر systemctl list-timers يُظهر قائمة بجميع الـtimers النشطة مع موعدها التالي وآخر تفعيل.
cron مقابل systemd timers: جدول مقارنة
| المعيار | cron | systemd timer |
|---|---|---|
| صياغة الجدولة | خمسة حقول `* * * * *` | `OnCalendar=` مقروء (`daily`، `Mon 03:00`) |
| السجلات | بريد إلكتروني أو إعادة توجيه يدوية | journald أصلي، `journalctl -u` |
| المهام الفائتة (الآلة مغلقة) | مفقودة (إلا مع `anacron`) | `Persistent=true` يعيد التشغيل بعد إعادة التشغيل |
| التبعيات بين الخدمات | لا يوجد | `After=`، `Requires=`، `Wants=` |
| العزل وحدود الموارد | يرث بيئة الـshell | Cgroups، `MemoryMax=`، `CPUQuota=` |
| التشغيل كمستخدم محدد | حقل المستخدم في `/etc/cron.d/` | `User=` و`Group=` في ملف .service |
| تشخيص الأخطاء | صعب بدون سجلات | `systemctl status`، `journalctl -xe` |
| التوفر | جميع أنظمة Unix | الأنظمة مع systemd (Debian، Ubuntu، RHEL…) |
متى تبقي على cron؟
يبقى cron الخيار الصحيح في عدة حالات. على الأنظمة القديمة أو المُخففة بدون systemd — بعض الحاويات، وBSD، وصور Alpine — يكون cron في الغالب الأداة الوحيدة المتاحة. في البيئات متعددة المستخدمين حيث يدير كل مستخدم مهامه الخاصة عبر crontab -e، يكون cron أبسط للتفويض. المعيار الحاسم هو الحاجة إلى سجلات أو تبعيات أو قيود على الموارد.
متى تنتقل إلى systemd timers؟
تصبح systemd timers الخيار الواضح حين تتجاوز المهمة نطاق أمر بسيط ومعزول. تحتاج journald لتتبع كل تنفيذ ومخرجاته ورمز إرجاعه دون تمديد يدوي؟ systemd timers. يجب أن يعمل سكريبت النسخ الاحتياطي فقط بعد أن يصبح PostgreSQL جاهزاً (After=postgresql.service)؟ systemd timers.
إنشاء systemd timer من الصفر (مثال: نسخ احتياطي يومي)
إنشاء ملف الخدمة
افتح /etc/systemd/system/backup.service وأدخل: [Unit]، Description=Daily PostgreSQL backup، After=postgresql.service، ثم [Service]، Type=oneshot، User=postgres، ExecStart=/usr/local/bin/backup.sh.
إنشاء ملف الـ timer
أنشئ /etc/systemd/system/backup.timer بـ: [Unit]، ثم [Timer]، OnCalendar=*-*-* 03:00:00، Persistent=true، و[Install]، WantedBy=timers.target.
إعادة تحميل systemd وتفعيل الـ timer
نفِّذ systemctl daemon-reload حتى يأخذ systemd الملفات الجديدة بالاعتبار، ثم systemctl enable --now backup.timer لتفعيل الـtimer وتشغيله فوراً.
التحقق من الـ timer
اكتب systemctl list-timers --all لرؤية الـtimer في القائمة. استخدم systemctl status backup.timer لحالة الـtimer.
قراءة السجلات
اطّلع على كل مخرجات الخدمة بـ journalctl -u backup.service للتاريخ الكامل، أو journalctl -u backup.service -n 50 --since today لآخر 50 سطراً اليوم.
هجرة مهمة cron موجودة إلى systemd
تحديد مهمة cron المراد هجرتها
أدرج crontabs بـ crontab -l (المستخدم الحالي) وcat /etc/cron.d/* (النظام). لاحظ الأمر الدقيق، ومستخدم التنفيذ، والجدولة ذات الخمسة حقول.
ترجمة الجدولة إلى OnCalendar
صيغة OnCalendar= في systemd تُكتب DayOfWeek Year-Month-Day Hour:Minute:Second. يصبح 30 2 * * 1 هو Mon *-*-* 02:30:00. لاختبار الترجمة، استخدم systemd-analyze calendar 'Mon *-*-* 02:30:00'.
إنشاء ملفَي .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.
تعطيل سطر cron والتحقق
علِّق أو احذف السطر في crontab الأصلي. اختبر فوراً بـ systemctl start certbot-renew.service وراجع النتيجة عبر journalctl -u certbot-renew.service -n 20.
لمهمة يجب تشغيلها بعد دقائق من الإقلاع ثم بصورة منتظمة، ادمج OnBootSec=5min وOnUnitActiveSec=1h في كتلة [Timer]. تبدأ المهمة بعد 5 دقائق من الإقلاع، ثم كل ساعة من هناك.
استكشاف أخطاء systemd timers الشائعة
تظهر ثلاث مشاكل بشكل متكرر عند إعداد الـtimers. الأولى: الـtimer نشط لكنه لا يُطلَق — نسيان daemon-reload بعد تعديل ملف وحدة هو السبب الأول. الثانية: الخدمة تفشل بصمت — راجع journalctl -u myservice.service -n 50. الثالثة: المهام الفائتة لا تُعاد — تأكد من وجود Persistent=true في كتلة [Timer].
خلاصة
cron وsystemd timers يتعايشان بلا مشاكل على نفس الخادم — لا يلزمك ترحيل كل شيء دفعة واحدة. احتفظ بـcron لمهامك البسيطة، واعتمد systemd timers للأتمتة الجديدة التي تستفيد من سجلات journald وتبعيات الخدمات أو عزل cgroup.