systemd وإدارة الخدمات
systemd وإدارة الخدمات
كل نظام Linux إنتاجي ستعمل عليه طوال حياتك المهنية يُشغِّل systemd بوصفه PID 1 — وهو نظام التهيئة (init) الذي يُقلِع نظام التشغيل، ويدير دورة حياة كل خدمة، وهو الأب المباشر لجميع العمليات الأخرى. إتقان systemd ليس اختياريًا لمهندس DevOps؛ إنه الأساس الذي تبني عليه خدمات موثوقة وقابلة للرصد على أي نطاق.
الوحدات (Units): لبنات بناء systemd
يُنظِّم systemd كل ما يديره في وحدات (units). الوحدة ليست سوى ملف نصي منظَّم يحمل لاحقة مثل .service أو .socket أو .timer أو .mount أو .path أو .target وغيرها. معظم وقتك سيكون مع وحدات .service و.target.
تتواجد ملفات الوحدات في ثلاثة مواضع — يدمجها systemd ويرتّب أولوياتها على هذا النحو:
/lib/systemd/system/— تُشحَن مع الحزم (للقراءة فقط؛ لا تعدّلها مباشرة أبدًا)/etc/systemd/system/— التخصيصات المحلية والوحدات المخصصة (هذا ملعبك)/run/systemd/system/— وحدات مؤقتة تُولَّد أثناء التشغيل (زائلة)
/etc/systemd/system/ يحمل الاسم ذاته لملف في /lib/systemd/system/ يُخفيه كليًا. هذه هي الطريقة الآمنة لتجاوز الإعدادات الافتراضية للحزم دون لمس ملفاتها الأصلية — وستظل تخصيصاتك سليمة بعد تحديث الحزمة.
systemctl: واجهة التحكم
كل التفاعل اليومي مع systemd يمر عبر systemctl. فيما يلي الأوامر التي ستستخدمها باستمرار في بيئة الإنتاج:
systemctl enable --now بدلًا من أمرين منفصلين. فهو ذري وإدمجي — تشغيله مرتين آمن تمامًا.
كتابة وحدة خدمة من الصفر
معرفة كيفية كتابة ملف وحدة صحيح وجاهز للإنتاج هي أهم مهارة في systemd. فيما يلي مثال من العالم الحقيقي لواجهة برمجية Node.js مع شرح مبرر لكل توجيه.
بعد كتابة أي ملف وحدة أو تعديله، يجب إعادة تحميل daemon قبل أن يرى systemctl التغييرات:
systemctl daemon-reload بعد تعديل ملف الوحدة يجعل systemd يعمل بالتعريف القديم المحمَّل في الذاكرة. تعديلاتك لن يكون لها أي أثر، ولن يحذّرك مخرج الحالة من ذلك. اجعله ذاكرة عضلية: تعديل ← daemon-reload ← إعادة تشغيل أو إعادة تحميل.
أنواع الخدمات (Service Types)
يُخبر التوجيه Type= نظامَ systemd بكيفية تحديد متى أنهت الخدمة عملية بدء التشغيل. اختيار النوع الخاطئ يُسبب ظروف سباق صامتة عند الإقلاع:
Type=simple(الافتراضي) — يعتبر systemd الخدمة قد بدأت فور تفريعExecStart. صحيح فقط إذا لم تُطلِق العملية خلفية لنفسها وتُشير للجاهزية فورًا.Type=notify— تستدعي العمليةsd_notify("READY=1")عندما تكون جاهزة فعلًا. ينتظر systemd هذه الإشارة. استخدمه لأي خدمة تحتاج وقتًا للتهيئة (اتصالات قاعدة البيانات، تسخين التخزين المؤقت).Type=forking— قديم؛ للخوادم التقليدية التي تُضاعف نفسها. تجنّبه في الكود الجديد.Type=oneshot— للسكريبتات التي تُنفَّذ وتنتهي. اجمعه معRemainAfterExit=yesلكي يعدّها systemd "نشطة" بعد الانتهاء.Type=exec— مثلsimpleلكنه ينتظر نجاح استدعاءexecve()قبل اعتبار الوحدة قد بدأت. خيار افتراضي أكثر أمانًا منsimpleلمعظم الخدمات الجديدة.
الأهداف (Targets) وشجرة تبعيات الإقلاع
الـtarget هو نقطة تزامن — معلَم مُسمَّى في سلسلة الإقلاع. تجمع الأهداف الخدمات وتُرسي الترتيب بينها. إنها بديل مستويات التشغيل القديمة في SysV:
أهم الأهداف لأحمال عمل الخوادم:
sysinit.target— تركيب أنظمة الملفات، وحدات النواة، إعداد الأجهزة المبكرbasic.target— المنافذ والمؤقتات والمسارات — الحد الأدنى لنظام يعملnetwork-online.target— الواجهات الشبكية جاهزة ولديها IP. استخدم هذا دائمًا (لاnetwork.target) كتبعية للخدمات التي تُجري اتصالات عند بدء التشغيلmulti-user.target— حالة "الخادم يعمل، جميع الخدمات نشطة"؛ يعادل مستوى التشغيل 3 في SysVgraphical.target— multi-user.target مع مدير عرض؛ غير ذي صلة بالخوادم عديمة الشاشة
network.target عند تطبيق الإعداد الشبكي، لا عند صلاحية الشبكة فعلًا. خدمة تعتمد فقط على network.target قد تبدأ قبل أن يُخصِّص DHCP عنوان IP. استخدم دائمًا network-online.target للخدمات التي تتصل بقواعد البيانات أو وسطاء الرسائل أو أي نقطة نهاية بعيدة عند الإقلاع.
Drop-in Overrides — الطريقة الآمنة لتخصيص وحدات الحزم
عندما تحتاج لتعديل وحدة مُشحونة مع حزمة (مثل رفع حد الملفات المفتوحة لـnginx) دون استبدال الملف بأكمله، استخدم drop-in. تُدمَج Drop-ins فوق الوحدة الأساسية بشكل جراحي:
systemctl edit nginx هو سير العمل المفضَّل — ينشئ مجلد الـ drop-in والملف تلقائيًا، ويُشغِّل daemon-reload عند الحفظ. استخدمه بدلًا من إنشاء الملفات يدويًا لتجنب الأخطاء المطبعية في المسارات.
توجيهات التبعية: Requires مقابل Wants
أكثر مصدر شائع لأخطاء وقت الإقلاع في ملفات الوحدات المخصصة هو سوء استخدام توجيهات التبعية. إليك الدلالات الدقيقة:
Requires=— تبعية صارمة. إذا فشلت الوحدة المطلوبة في البدء، تُوقَف هذه الوحدة أيضًا. استخدمها باعتدال.Wants=— تبعية لينة. يحاول systemd تشغيل الوحدة المطلوبة، لكن هذه الوحدة تستمر حتى لو فشلت. مفضَّلة لمعظم تبعيات الخدمات.After=/Before=— ترتيب فقط، لا تبعية. بدون هذه التوجيهات، يبدأ systemd الوحدات بالتوازي. اقرن دائمًا توجيه التبعية بتوجيه ترتيب.BindsTo=— مثلRequires=لكنه أشد: إذا توقفت الوحدة المرتبطة لأي سبب (ليس فقط فشل البدء)، تُوقَف هذه الوحدة فورًا. استخدمه للوحدات التي تفقد معناها تمامًا بغياب الأخرى.
النمط الأكثر شيوعًا لخدمات التطبيقات: Wants= وAfter= للتبعيات اللينة (قاعدة البيانات، التخزين المؤقت)، وRequires= وAfter= فقط للبنية التحتية الصارمة (الشبكة، التركيبات المطلوبة).