Photo by Towfiqu barbhuiya / Unsplash

جدولة المهام باستخدام systemd timers كبديل لـ cron.

DevOps ١١ سبتمبر ٢٠٢٤

بالنسبة لأي مسؤول نظم، مهندس DevOps، أو أي شخص مهتم بنظام Linux بشكل عام، فإن جدولة المهام المزعجة/الصعبة أمر مهم. وتلعب جدولة المهام دورًا رئيسيًا في الأتمتة.

لجدولة المهام، الحل المعروف والمنتشر هو cron. يحتوي ملف (crontab) على قائمة المهام، أوامر التنفيذ، وتوقيتاتها. بشرط أن تتقن تعبيرات الجدولة، ويُعتبر cron حلاً قويًا وأنيقًا.

بالنسبة لمسؤولي أنظمة Linux، يوجد بديل يوفر تكاملاً وتوافقا مع systemd، ويُعرف باسم systemd timers.

لماذا اختار systemd timers بدلاً من cron

إذا كان cron يعمل، لماذا نستخدم systemd timers؟ السؤال هنا لا يتعلق بالتفوق. فكلاهما يعمل بشكل جيد، ولكل منهما إيجابياته وسلبياته.

أميل إلى استخدام systemd timers في الحالات التالية:

  • اذا كان systemd متوفر لماذا لا نستخدمه
  • عند الرغبة في التعامل مع المناطق الزمنية (التعامل مع التوقيت الصيفي مثلا، أو ببساطة لتعيين الأوقات بتنسيق غير UTC)
  • التكامل مع السجلات logs وإمكانية الوصول إليها باستخدام journalctl
  • اختبار العملية بشكل منفرد (دون انتظار الجدولة التالية).

من جهة أخرى، قد يكون cron هو الخيار الأفضل إذا كنت أنت وفريقك على دراية عالية بطريقة استخدامه ولا ترغب بالتعامل مع اداة جديدة.

الخدمة Service

بالنسبة إلى systemd timer، هناك ملفان يجب إنشاؤهما:

  • الخدمة التي سيتم تشغيلها
  • المؤقت الذي يقوم بجدولة الخدمة

الخدمة لا يجب ان تكون طويلة الامد يمكن استخدام نوع oneshot حتى لو تم ايقافها بشكل فوري لا تزال تسمى خدمة.

هنا مثال بسيط يعرض كيفية إنشاء خدمة oneshot بسيطة تحدث رسالة الترحيب اليومية بمعلومات الطقس.

[Unit]
Description=Update message of the day with current weather

[Service]
ExecStart=/usr/bin/curl -o /etc/motd http://wttr.in/?1Fq
Type=oneshot

/etc/systemd/system/motd-weather.service

بعض الملاحظات:

  • تسمية الملفات مهمة طالما أن الخدمة والمُؤقت يحملان نفس الاسم (باستثناء الامتداد)، فسيتمكنان تلقائيًا من العثور على بعضهما البعض، دون الحاجة إلى الإشارة الصريحة إلى أسماء الملفات. يجب أن يكون امتداد الخدمة هو .service وامتداد المؤقت هو .timer.
  • يمكنك كتابة Description يوضح الغرض من الخدمة.
  • يجب أن يكتب في ExecStart الأمر المناسب. المسارات الكاملة للتنفيذ غالبًا ما تكون ضرورية، ولهذا نحدد مثلاً /usr/bin/curl بدلاً من مجرد كتابة curl.
  • بافتراض أن الأمر يتم تنفيذه ثم ينتهي، قمت بتعيين Type=oneshot. هذا يُشير إلى systemd أن الخدمة لا يجب اعتبارها "ميتة" فقط لأنها انتهت من التنفيذ.

تجربة الخدمة:

systemctl daemon-reload
systemctl start motd-weather.service
cat /etc/motd

هل تم تحديث ملف cat /etc/motd ؟

المؤقت timer

تتكون systemd timers من نظام يتضمن ملفين. لقد أنجزنا الجزء الأول وهو الخدمة، والآن نقوم بإنشاء مؤقت مطابق لجدولة هذه الخدمة.

فيما يلي ملف مؤقت يتوافق مع الخدمة المذكورة أعلاه:

[Unit]
Description=Download weather to motd nightly

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1h

[Install]
WantedBy=timers.target

/etc/systemd/system/motd-weather.timer

إذا كانت الخدمة موجودة في /etc/systemd/system/motd-weather.service، فيجب أن يكون هذا الملف هو /etc/systemd/system/motd-weather.timer. لاحظ أن الفرق الوحيد هو الامتداد: .service مقابل .timer، بينما يتم الاحتفاظ بالجزء الرئيسي من الاسم motd-weather لتفعيل الاكتشاف التلقائي.

بعض الملاحظات:

  • يمكنك كتابة Description يوضح الغرض من المؤقت.
  • من أجل البساطة، تم استخدام OnCalendar=daily لتشغيل الخدمة عند منتصف الليل. يمكن تخصيص الجدولة لمزيد من المرونة.
  • ماذا لو كان الخادم مغلقًا أو غير متصل بسبب الصيانة أو فشل الشبكة؟ سيكون من المؤسف إذا عاد الخادم إلى العمل برسالة ترحيب تحتوي على طقس اليوم السابق! لذلك نستخدم Persistent=true حتى يتم تشغيل الخدمة في الإقلاع التالي إذا كان من المفترض تشغيلها أثناء فترة التوقف. يمكنك حذف هذا السطر إذا لم يكن هذا مطلوبًا، حيث إن القيمة الافتراضية هي false.
  • بافتراض أن لدينا العديد من المؤقتات التي تعمل مع OnCalendar=daily، فقد يتسبب ذلك في تشغيل العديد من الخدمات في منتصف الليل، مما قد يؤثر على أداء النظام. بالطبع يمكننا تغيير daily إلى وقت محدد. في هذه الحالة، قمت بتعيين RandomizedDelaySec=1h، مما يعني أن systemd سيختار عشوائيًا وقت التشغيل خلال ساعة واحدة بعد منتصف الليل. باستخدام هذا الإعداد مع مؤقتات يومية أخرى، سنحقق التوازن في النظام.
  • في قسم [Install]، نخبر systemd بأن الهدف timers.target يحتاج إلى هذا المؤقت. هذا يعني أنه عند إعادة التشغيل، عندما يبدأ timers.target، سيتم تفعيل هذا المؤقت والمؤقتات الأخرى المرتبطة كذلك. لا يعني ذلك تشغيل الخدمات المرتبطة، بل يعني تفعيل المؤقتات عند الإقلاع.

بهذه الطريقة، يمكن إعداد مؤقتات systemd بطريقة مرنة ومتوازنة لجدولة المهام اليومية وغيرها من المهام المتكررة.

تفعيل وتشغيل المؤقت

بافتراض أننا قمنا باختبار الخدمة وتعمل كما هو مطلوب، الآن يمكم تشغيل المؤقت. استخدم الأمر التالي:

sudo systemctl enable --now motd-weather.timer

لاحظ أننا نقوم بتفعيل وتشغيل المؤقت، ومن ثم يقوم المؤقت بتشغيل الخدمة وفقًا للجدول المحدد. لا نقوم بتشغيل الخدمة مباشرة.

إذا تم التثبيت بشكل صحيح، يجب أن تكون قادرًا على رؤية المؤقت وبعض معلومات الجدولة عند عرض المؤقتات الخاصة بـ systemd باستخدام الأمر التالي:

systemctl list-timers

إذا كانت القائمة طويلة، يمكنك التصفية باستخدام wildcards كما يلي:

systemctl list-timers motd*

بهذا تكون قد أتممت إعداد وتشغيل المؤقت، وستعمل الخدمة وفق الجدول المحدد دون الحاجة إلى التدخل اليدوي.

تعبيرات التقويم

إحدى المكونات المهمة في systemd timers والتي تستحق دراستها المستمرة هي ما يتم تعيينه في OnCalendar: وهو تعبير التقويم.

يمكنك أيضًا استخدام هذه التعبيرات المختصرة مع systemd timers لجدولة المهام بسهولة:

  • minutely: لتشغيل المهمة كل دقيقة.
  • hourly: لتشغيل المهمة كل ساعة.
  • daily: لتشغيل المهمة يوميًا.
  • weekly: لتشغيل المهمة أسبوعيًا.
  • monthly: لتشغيل المهمة شهريًا.
  • yearly: لتشغيل المهمة سنويًا.
  • quarterly: لتشغيل المهمة كل ثلاثة أشهر.
  • semiannually : لتشغيل المهمة كل نصف سنة.

باستخدام هذه التعبيرات المختصرة، يمكنك بسهولة إعداد جدول زمني دون الحاجة إلى كتابة تعبير تقويمي مفصل.

يمكن ايضا استخدام Calendar expressions مثل *-01-01 00:00:00 UTC او *-*-* 08:00:00 America/New_York يمكن ان نكتب عنها في وقت لاحق.

مؤقتات العد التنازلي

لا يتعين على مؤقتات systemd أن تكون مرتبطة بأحداث تقويمية متكررة أو واحدة. بمعنى آخر، هناك خيارات أخرى غير OnCalendar=.

تشمل هذه الخيارات ما يلي:

  • OnActiveSec=: يقوم بتشغيل الخدمة بعد مرور وقت محدد من تنشيط المؤقت.
  • OnBootSec=: يشغل الخدمة بعد مرور وقت محدد منذ إقلاع النظام.
  • OnStartupSec=: يشبه OnBootSec=، لكنه يشير إلى وقت منذ بدء مدير الخدمة. أفضل استخدام OnStartupSec= لأنه أكثر مرونة وينطبق أيضًا على الخدمات المخصصة للمستخدم، والتي قد يتم تشغيلها فقط بعد تسجيل الدخول.
  • OnUnitActiveSec=: يشغل الخدمة بعد مرور وقت محدد من آخر مرة تم فيها تنشيط الوحدة.
  • OnUnitInactiveSec=: يشغل الخدمة بعد مرور وقت محدد من آخر مرة تم فيها إيقاف الوحدة.

تتيح هذه الخيارات إعداد مؤقتات تعتمد على وقت تشغيل النظام أو حالة الوحدة السابقة، مما يمنحك مرونة أكبر في جدولة المهام.

الوثائق

اذا كنت تستمتع بقراءة الوثائق:

  • systemd: الأساس لفهم كيفية عمل النظام ككل.
  • systemd timers: تفاصيل حول إعداد واستخدام المؤقتات في systemd.
  • systemd units: معلومات حول الوحدات وكيفية إدارتها.
  • systemd time: شرح شامل لتنسيقات الوقت المستخدمة مع systemd.
  • systemd-analyze: أداة لتحليل أداء النظام والتوقيتات.

قراءة هذه الوثائق ستمنحك فهمًا أعمق لإمكانيات systemd، مما سيساعدك في تحقيق المزيد من التحكم والمرونة في إدارة النظام.

أخيرا

لا تتردد في مشاركة أفكارك، نصائحك، وأسئلتك في قسم التعليقات!

تصنيفات