لماذا أتمتة المهام على 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: جدول مقارنة
مرّر الجدول أفقيًا
| المعيار | cron | systemd 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 من الصفر (مثال: نسخ احتياطي يومي)
إنشاء ملف الخدمة
افتح /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يشير إلى أن الخدمة تنتهي بعد تنفيذ النص البرمجي — النوع المعتاد للمهام المجدولة.إنشاء ملف الـ timer
أنشئ /etc/systemd/system/backup.timer بـ:
[Unit] Description=Daily trigger for backup [Timer] OnCalendar=*-*-* 03:00:00 Persistent=true [Install] WantedBy=timers.targetPersistent=trueيضمن أنه إذا كانت الآلة مغلقة الساعة 3، يُشغَّل النسخ الاحتياطي عند الإقلاع التالي.إعادة تحميل systemd وتفعيل الـ timer
نفِّذ
systemctl daemon-reloadحتى يأخذ systemd الملفات الجديدة بالاعتبار، ثمsystemctl enable --now backup.timerلتفعيل الـtimer وتشغيله فوراً.التحقق مع systemctl status
تحقق من حالة الـtimer بـ
systemctl status backup.timer— يجب أن تُظهر الإخراج active (waiting) مع تاريخ الإطلاق التالي. راجع أيضاً حالة الخدمة بعد أول تنفيذ بـsystemctl status backup.service.إدراج جميع الـ timers النشطة
الأمر
systemctl list-timers --allيعرض جدولاً بجميع timers النظام مع ثلاثة أعمدة رئيسية: NEXT (التنفيذ التالي)، LEFT (الوقت المتبقي) وLAST (آخر تنفيذ). هذه لوحة التحكم للتحقق من أن timers تُطلَق كما هو متوقع.قراءة السجلات
اطّلع على كل مخرجات الخدمة بـ
journalctl -u backup.serviceللتاريخ الكامل، أوjournalctl -u backup.service -n 50 --since todayلآخر 50 سطراً اليوم.إضافة إشعار خطأ (اختياري)
للتنبيه عند الفشل، أضف في كتلة
[Unit]من ملف .service:OnFailure=notify-failure@%n.serviceهذه الخدمة يمكنها إرسال بريد إلكتروني أو ويب هوك Slack أو تنبيه ntfy حسب بنيتك التحتية.
اختبار الخدمة يدوياً
قبل انتظار الإطلاق المجدول، اختبر التنفيذ الفوري بـ
systemctl start backup.service(بدون .timer). راجع النتيجة فوراً بـjournalctl -u backup.service -n 20. إذا نجح النص البرمجي، فالإعداد مُتحقَّق منه.
هجرة مهمة 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
اختبر فوراً بـ
systemctl start certbot-renew.serviceوراجع النتيجة عبرjournalctl -u certbot-renew.service -n 20. تحقق من ظهور الـtimer فيsystemctl list-timers. لا تعطّل سطر cron إلا بعد التحقق.تعطيل سطر 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.