عند تشغيل خدمة على خادم Linux لا يكفي أن تعمل مرة واحدة، بل تحتاج إلى إدارتها بثبات ومعرفة ما يحدث داخلها لحظة بلحظة. هنا يأتي systemd كمدير الخدمات الافتراضي في معظم التوزيعات الحديثة، فيمنحك واجهة موحّدة لتشغيل الخدمات وإيقافها وتمكينها عند الإقلاع، إلى جانب نظام سجلات مركزي هو journald. المشكلة الشائعة أن كثيرًا من المستخدمين يحفظون أوامر متفرقة دون فهم العلاقة بين ملف الوحدة وحالة الخدمة والسجل. في هذا الدليل نتناول الموضوع بشكل عملي: كيف تنشئ ملف وحدة نظيفًا، وكيف تتحكم بالخدمة بأمان، وكيف تقرأ سجلاتها وتفلترها لتحديد سبب الفشل بسرعة. سنركّز على أوامر حقيقية وإعدادات قابلة للتطبيق، مع تمييز واضح بين ما هو إرشاد مباشر وما هو مجرد مثال افتراضي للتوضيح.
ما هي وحدة systemd وكيف يفكّر بها النظام؟
في systemd تُوصف كل خدمة بوحدة unit ذات امتداد .service، وهي ملف نصي يحدد كيف يبدأ البرنامج ومن يشغّله وما يعتمد عليه. يفصل systemd بين الوحدة كمصدر للحقيقة وبين حالة الخدمة اللحظية التي يديرها في الذاكرة. تعيش ملفات الوحدات المثبّتة من الحزم في المسار /usr/lib/systemd/system/، بينما تضع تعديلاتك الخاصة في /etc/systemd/system/ لأنها ذات أولوية أعلى. هذا الفصل يعني أن ترقية الحزمة لا تمحو إعداداتك، وهي قاعدة عملية يجب أن تبني عليها أي تعديل تجريه على خدمة قائمة.
تظهر الأقسام داخل ملف الوحدة بوضوح: قسم [Unit] للوصف والاعتماديات، وقسم [Service] لنوع الخدمة وأمر التشغيل والمستخدم، وقسم [Install] لعلاقة التمكين بالهدف مثل multi-user.target. لا تحتاج إلى حفظ المواقع المحتملة لملف الوحدة عن ظهر قلب، بل استخدم الأمر systemctl cat لعرض المحتوى الفعلي، و systemctl show لرؤية القيم المحسوبة بعد دمج التجاوزات. هكذا تعرف دائمًا النسخة التي ينفّذها النظام فعلاً بدل الاعتماد على التخمين أو على ذاكرة قديمة.
إنشاء ملف وحدة خدمة خطوة بخطوة
لإنشاء خدمة جديدة، أنشئ ملفًا باسم واضح مثل myapp.service داخل المجلد /etc/systemd/system/. ابدأ بقسم [Unit] مع Description، وأضف After=network-online.target إذا كانت الخدمة تحتاج الشبكة عند البدء. ثم اكتب قسم [Service] مع Type=simple و ExecStart الذي يحمل مسارًا مطلقًا للملف التنفيذي. ضع المسار الكامل دائمًا لأن systemd لا يعتمد على متغيرات بيئة الصدفة نفسها التي تستخدمها في الطرفية، وهذا فرق يسبب كثيرًا من حالات الفشل الصامت عند الإقلاع.
أضف عوامل أمان عملية: User و Group لتجنّب التشغيل بصلاحيات root، و WorkingDirectory لتثبيت مجلد العمل، و Environment للمتغيرات أو EnvironmentFile لملف إعداد منفصل. اختم ب ـ WantedBy=multi-user.target داخل قسم [Install] لتصبح الخدمة قابلة للتمكين. قبل التشغيل تحقّق من الصحة النحوية بالأمر systemd-analyze verify على مسار الملف، فهذا يلتقط أخطاء المسارات والاعتماديات مبكرًا بدل انتظار فشل غامض عند الإقلاع التالي مباشرة.
أوامر systemctl الأساسية للتحكم بالخدمة
التحكم اليومي يتم عبر systemctl: الأمر systemctl start يشغّل الخدمة الآن، و stop يوقفها، و restart يعيد تشغيلها بالكامل، بينما reload يطلب من البرنامج إعادة قراءة إعداداته دون مقاطعة الخدمة إن كان يدعم ذلك عبر ExecReload. تحقّق من الحالة ب ـ systemctl status الذي يعرض الحالة ومعرّف العملية وأسطر السجل الأخيرة في شاشة واحدة مفيدة تجمع كل ما تحتاجه للنظرة الأولى.
استخدم is-active و is-enabled داخل السكربتات لأن مخرجهما مناسب للتحقق الآلي، واعتمد على رمز الخروج بدل تحليل النصوص المطبوعة. الأمر systemctl list-units --type=service يعطيك نظرة شاملة على الوحدات النشطة، بينما systemctl list-unit-files --type=service يبيّن ما هو ممكّن وما هو معطّل. هذه الفروق الصغيرة بين أوامر العرض وأوامر التحكم توفّر وقتًا كبيرًا عند إدارة عدة خدمات معًا على خادم واحد.
التمكين عند الإقلاع وإدارة التغييرات بأمان
التمكين enable يعني إنشاء روابط رمزية تجعل الخدمة تُشغّل تلقائيًا عند الإقلاع، وهو مختلف تمامًا عن التشغيل الآن start. يمكنك الجمع بينهما بأمر واحد هو systemctl enable --now. لتعطيل البدء التلقائي استخدم disable، ولإيقاف خدمة عاملة فورًا مع تعطيلها استخدم disable --now. الخلط بين المفهومين سبب شائع لظنّ المستخدم أن خدمته تعمل بينما هي في الحقيقة معطّلة ولن تبدأ عند إعادة التشغيل القادمة.
بعد أي تعديل على ملف الوحدة أعد تحميل المدير بالأمر systemctl daemon-reload ثم أعد تشغيل الخدمة ليُطبّق التغيير، لأن systemd يخزّن تعريف الوحدة في الذاكرة. للتعديل دون لمس الملف الأصلي استخدم systemctl edit الذي ينشئ ملف تجاوز داخل مجلد .d، وهو أسلوب أنظف يحافظ على التحديثات المستقبلية ويعرض لك systemctl cat الملف الناتج بعد دمج الطبقات كلها معًا.
قراءة السجلات باستخدام journalctl
سجلات systemd تُجمع في journald، والوصول إليها يتم عبر journalctl. لعرض سجل خدمة واحدة استخدم journalctl -u، وهذا أهم أمر عملي عند التشخيص لأنه يستبعد ضجيج بقية النظام ويضعك أمام رسائل الخدمة وحدها. لمتابعة الرسائل لحظيًا أضف الخيار -f، وهو مفيد أثناء اختبار خدمة جديدة أو إعادة تشغيلها ورؤية مخرجاتها تظهر فورًا على الشاشة.
لمعرفة سلوك الخدمة في إقلاع سابق دون خلطه بالإقلاع الحالي، استخدم -b للإقلاع الحالي و-b -1 للسابق. ابدأ القراءة من الأقدم نحو الأحدث لفهم التسلسل، لأن أخطاء systemd كثيرًا ما تُطبع قبل رسالة الفشل النهائية بعدة أسطر. أضف -n لتحديد آخر خمسين سطرًا عند الطباعة المباشرة، وتذكّر أن systemctl status يعرض بدوره آخر أسطر السجل كاختصار سريع لذيذ.
تصفية السجلات حسب الوقت والأولوية
الفلترة الدقيقة توفّر وقتًا حقيقيًا. حدّد نطاقًا زمنيًا بالخيارين --since و--until، فتحصل على نافذة مطابقة للحادثة بدل سجل ضخم يصعب مسحه. يمكن دمج الشرط مع الوحدة مثل journalctl -u myapp.service --since today، أو حصر الرسائل بالأخطاء فقط عبر -p err، أو استخدام -p warning لتوسيع النطاق قليلًا ورؤية التحذيرات التي سبقت العطل.
لتشخيص بنيوي استخدم -o json-pretty أو -o short-iso للحصول على طوابع زمنية دقيقة سهلة المقارنة، ما يساعد عند ربط حادثة في الخدمة بحدث آخر في النظام. يمكنك أيضًا الفلترة حسب معرّف العملية أو حسب رقم الإقلاع، ودمج عدة فلترات في أمر واحد. تعوّد على بناء أمر فلترة شامل بدل تصفح السجل الكامل، فهذا الفرق هو ما يفصل التشخيص السريع عن التخمين المرتبك.
جعل السجلات دائمة وضبط حجمها
افتراضيًا قد تبقى سجلات journald في الذاكرة فقط فتُفقد بعد إعادة الإقلاع. لجعلها دائمة فعّل Storage=persistent في ملف /etc/systemd/journald.conf، أو أنشئ المجلد /var/log/journal ثم أعد تشغيل systemd-journald. عندها تتم الكتابة على القرص ويمكنك مراجعة أعطال الإقلاع السابقة، وهو أمر ضروري على الخوادم التي تُعاد تشغيلها دون مراقبة مباشرة لحظة الحادثة.
السجلات الدائمة تنمو، لذا اضبط حدودًا واضحة عبر SystemMaxUse لسقف المساحة، و SystemMaxFileSize لحجم الملف الواحد، و MaxRetentionSec لمدة الاحتفاظ. نظّف يدويًا عند الحاجة بأمر journalctl --vacuum-time أو --vacuum-size. راقب استخدام القرص بعد التفعيل، فسجل صامت يستهلك المساحة بالكامل قد يسبب أعطالًا أصعب من العطل الأصلي الذي تحاول متابعته وتشخيصه.
استكشاف الأخطاء الشائعة عبر السجل
أكثر سبب لفشل خدمة هو خطأ في ExecStart أو صلاحيات غير كافية للمستخدم الذي تعمل به الوحدة. عند رؤية حالة failed اقرأ أولًا journalctl -u NAME -b بحثًا عن رسالة مثل عدم العثور على الملف التنفيذي أو رفض الوصول. كثير من مشكلات Permission denied تُحلّ بضبط User و Group أو بمنح صلاحية على المسار الصحيح بدل التشغيل بصلاحيات root غير الضرورية.
إذا أُعادت الخدمة التشغيل المتكرر بسرعة، فغالبًا هناك Restart=on-failure مع RestartSec صغير جدًا، فيدخل النظام في حلقة إعادة تشغيل تُخفي السبب الحقيقي وتُربك السجل. أضف RestartSec بقيمة مناسبة على الأقل لفترة مستقرة، وراقب عدد المحاولات في السجل. أما إن فشلت الخدمة عند الإقلاع فقط فتحقق من الاعتماديات مثل After و Wants وهدف الشبكة، ومن توقيت جاهزية الموارد التي تعتمد عليها.
المؤقتات systemd timer كبديل ل ـ cron
المؤقتات في systemd بامتداد .timer بديل عملي ل ـ cron بمزايا إضافية: تسجيل موحّد في journald، وإمكانية الاعتماد على خدمات أخرى، وتحكم دقيق في الجدولة عبر OnCalendar. تُقترن وحدة المؤقّت بوحدة خدمة تحمل الاسم نفسه تقريبًا، فيُشغّل المؤقّت الخدمة في الأوقات المحددة دون الحاجة إلى سكربت خارجي معقّد لإدارة المهام.
لتفعيل مؤقّت استخدم systemctl enable --now ثم تحقق ب ـ systemctl list-timers التي تعرض موعد التشغيل القادم والآخر السابق. اختبر جدولة OnCalendar بأمر systemd-analyze calendar قبل الاعتماد عليها، خصوصًا في صيغ الوقت المعقّدة. ميزة مهمة أن مخرجات المهمة تذهب تلقائيًا إلى السجل ذاته، فتراجع نتائجها بنفس أمر journalctl -u الذي تستخدمه مع أي خدمة أخرى.
أفضل ممارسات للإدارة المستمرة
اجعل لكل خدمة ملف وحدة واضح الاسم ومسارًا مطلقًا ووصفًا مفيدًا، واحتفظ بالتعديلات في /etc/systemd/system/ عبر systemctl edit بدل تعديل ملفات الحزم مباشرة. وثّق الاعتماديات وحدود إعادة التشغيل والمستخدم المستهدف لكل وحدة، واستخدم systemd-analyze verify كفحص روتيني قبل النشر لتقليل الأعطال المفاجئة عند الإقلاع في بيئة الإنتاج.
أنشئ عادة تشخيص ثابتة: الحالة أولًا، ثم سجل الوحدة في نطاق زمني محدّد، ثم فلترة بمستوى الخطأ. ثبّت السجلات الدائمة واضبط حدودها لمنع امتلاء القرص، وراجع المؤقتات دوريًا إن كنت تعتمد على مهام مجدولة. هذه الممارسات البسيطة تجعل إدارة الخدمات متوقعة وقابلة للتكرار على أي خادم تقريبًا، وتقلّل زمن الاستجابة للأعطال بشكل ملموس.
أسئلة شائعة
ما الفرق بين systemctl start و systemctl enable؟
الأمر start يشغّل الخدمة الآن ضمن الجلسة الحالية فقط ولا يؤثر على الإقلاع التالي، بينما enable ينشئ ارتباطًا دائمًا يجعل الخدمة تُشغّل تلقائيًا عند إقلاع النظام. يمكنك الجمع بينهما بأمر واحد هو enable --now لتوفير خطوة. إذا شغّلت خدمة ب ـ start دون enable فستتوقف عن العمل بعد إعادة التشغيل، وهذا سبب شائع جدًا للالتباس عند إعداد خادم جديد أو عند اختبار خدمة سريعة.
كيف أعرف سبب فشل خدمة معينة؟
ابدأ ب ـ systemctl status لرؤية الحالة ورمز الخروج وآخر الأسطر، ثم انتقل إلى journalctl -u NAME -b لقراءة سجل الإقلاع الحالي كاملًا للخدمة وحدها. ابحث عن أول خطأ زمنيًا لا عن الرسالة الأخيرة فقط، لأن الرسالة النهائية غالبًا نتيجة لخطأ أسبق مثل مسار خاطئ أو صلاحية مرفوضة. أضف -p err لحصر الأخطاء إذا كان السجل طويلًا، وراجع إقلاعًا سابقًا ب ـ -b -1 عند الحاجة.
لماذا لا أرى سجلاتي القديمة بعد إعادة التشغيل؟
لأن الإعداد الافتراضي قد يحتفظ بالسجلات في الذاكرة فقط فتُفقد عند إعادة الإقلاع. لجعلها دائمة فعّل Storage=persistent في الملف /etc/systemd/journald.conf أو أنشئ المجلد /var/log/journal ثم أعد تشغيل خدمة systemd-journald لتفعيل الكتابة على القرص. بعد ذلك يمكنك مراجعة إقلاعات سابقة، مع ضبط SystemMaxUse لمنع تضخّم السجلات واستهلاك مساحة القرص مع الوقت.
هل يمكنني تعديل خدمة دون تغيير ملف الوحدة الأصلي؟
نعم، استخدم الأمر systemctl edit على اسم الخدمة، فينشئ ملف تجاوز داخل مجلد يحمل الامتداد .d بجوار الوحدة. تكتب فيه فقط الإعدادات التي تريد تغييرها، فيدمجها systemd فوق الملف الأصلي. هذه الطريقة تحافظ على ملفات الحزم سليمة فلا تمحوها الترقية، وتعرض لك systemctl cat المحتوى النهائي بعد الدمج، ما يسهّل فهم الأولوية بين الطبقات.
شارك في تقييم ونقاش المقال
رأيك يضيف قيمة للمقال ويساعدنا على تحسين المحتوى والنقاش حوله.
النقاش
جارٍ تحميل التعليقات…