إدارة أنظمة لينكس

systemd وإدارة الخدمات

18 دقيقة الدرس 1 من 28

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 start nginx # تشغيل الوحدة الآن (لا يستمر بعد إعادة التشغيل) systemctl stop nginx # إيقاف أنيق (يُرسل SIGTERM ثم SIGKILL) systemctl restart nginx # إيقاف ثم تشغيل systemctl reload nginx # إرسال SIGHUP (إعادة تحميل الإعداد دون توقف — يتطلب ExecReload=) systemctl status nginx # حالة تفصيلية: الحالة، PID، آخر 10 أسطر من السجل، رمز الخروج # --- الثبات عند الإقلاع --- systemctl enable nginx # إنشاء رابط رمزي لتشغيل الوحدة عند الإقلاع systemctl disable nginx # حذف الرابط الرمزي systemctl enable --now nginx # تمكين وتشغيل في أمر واحد (المفضّل في الأتمتة) systemctl is-enabled nginx # يطبع: enabled / disabled / static / masked # --- الاستعلام والفحص --- systemctl list-units --type=service # جميع وحدات الخدمات المحمَّلة systemctl list-units --type=service --failed # الخدمات التي فشلت systemctl cat nginx # طباعة ملف الوحدة الفعلي (مع التخصيصات) systemctl show nginx # تفريغ جميع الخصائص بصيغة key=value systemctl show nginx -p Restart,RestartSec # تصفية خصائص محددة # --- التبعيات --- systemctl list-dependencies nginx # شجرة ما تطلبه nginx systemctl list-dependencies --reverse nginx # ما يعتمد على nginx
نصيحة احترافية: في أتمتة الإنتاج (Ansible وcloud-init وسكريبتات النشر)، استخدم دائمًا systemctl enable --now بدلًا من أمرين منفصلين. فهو ذري وإدمجي — تشغيله مرتين آمن تمامًا.

كتابة وحدة خدمة من الصفر

معرفة كيفية كتابة ملف وحدة صحيح وجاهز للإنتاج هي أهم مهارة في systemd. فيما يلي مثال من العالم الحقيقي لواجهة برمجية Node.js مع شرح مبرر لكل توجيه.

# /etc/systemd/system/myapi.service [Unit] Description=My Production API Service Documentation=https://internal-wiki.company.com/myapi # تبعية صارمة: فشل التشغيل إذا لم تكن الشبكة جاهزة Requires=network-online.target # الترتيب: ابدأ بعد بلوغ هذا الهدف After=network-online.target # تبعية لينة على PostgreSQL: حاول البدء بعدها لكن لا تفشل إن غابت Wants=postgresql.service After=postgresql.service [Service] Type=notify # العملية تُشعر systemd عند جاهزيتها (مفضّل للخوادم) User=myapi # تشغيل كمستخدم نظام مخصص غير root Group=myapi WorkingDirectory=/opt/myapi EnvironmentFile=/etc/myapi/env # تحميل الأسرار من ملف خارجي ExecStart=/usr/bin/node /opt/myapi/server.js ExecReload=/bin/kill -HUP $MAINPID # سياسة إعادة التشغيل — المعيار الإنتاجي Restart=on-failure # إعادة التشغيل عند الخروج غير النظيف، لكن ليس عند الإيقاف النظيف RestartSec=5s # انتظر 5 ثوانٍ قبل المحاولة StartLimitIntervalSec=60s StartLimitBurst=5 # حد أقصى 5 محاولات في 60 ثانية ثم التوقف والتنبيه # حدود الموارد — ضعها دائمًا لمنع الإفراط في الاستهلاك LimitNOFILE=65536 MemoryMax=512M CPUQuota=80% # التصليب الأمني — احجب ما لا تحتاجه الخدمة NoNewPrivileges=true ProtectSystem=strict PrivateTmp=true ReadWritePaths=/var/lib/myapi /var/log/myapi [Install] WantedBy=multi-user.target # ابدأ أثناء الإقلاع متعدد المستخدمين الاعتيادي

بعد كتابة أي ملف وحدة أو تعديله، يجب إعادة تحميل daemon قبل أن يرى systemctl التغييرات:

systemctl daemon-reload systemctl enable --now myapi.service # تحقق من بدء التشغيل بنجاح systemctl status myapi.service journalctl -u myapi.service -n 50 --no-pager
فخ إنتاجي: نسيان 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:

systemd boot target chain sysinit.target Mounts, kernel basic.target Sockets, timers network-online.target Network ready multi-user.target All daemons up graphical.target Display manager myapi.service WantedBy=multi-user WantedBy
سلسلة أهداف إقلاع systemd — تُعلِن الخدمات انتماءها عبر WantedBy=.

أهم الأهداف لأحمال عمل الخوادم:

  • sysinit.target — تركيب أنظمة الملفات، وحدات النواة، إعداد الأجهزة المبكر
  • basic.target — المنافذ والمؤقتات والمسارات — الحد الأدنى لنظام يعمل
  • network-online.target — الواجهات الشبكية جاهزة ولديها IP. استخدم هذا دائمًا (لا network.target) كتبعية للخدمات التي تُجري اتصالات عند بدء التشغيل
  • multi-user.target — حالة "الخادم يعمل، جميع الخدمات نشطة"؛ يعادل مستوى التشغيل 3 في SysV
  • graphical.target — multi-user.target مع مدير عرض؛ غير ذي صلة بالخوادم عديمة الشاشة
network.target مقابل network-online.target: يُبلَغ network.target عند تطبيق الإعداد الشبكي، لا عند صلاحية الشبكة فعلًا. خدمة تعتمد فقط على network.target قد تبدأ قبل أن يُخصِّص DHCP عنوان IP. استخدم دائمًا network-online.target للخدمات التي تتصل بقواعد البيانات أو وسطاء الرسائل أو أي نقطة نهاية بعيدة عند الإقلاع.

Drop-in Overrides — الطريقة الآمنة لتخصيص وحدات الحزم

عندما تحتاج لتعديل وحدة مُشحونة مع حزمة (مثل رفع حد الملفات المفتوحة لـnginx) دون استبدال الملف بأكمله، استخدم drop-in. تُدمَج Drop-ins فوق الوحدة الأساسية بشكل جراحي:

# إنشاء مجلد الـ drop-in والملف mkdir -p /etc/systemd/system/nginx.service.d/ cat > /etc/systemd/system/nginx.service.d/override.conf <<'EOF' [Service] LimitNOFILE=100000 EOF systemctl daemon-reload systemctl restart nginx # التحقق من النتيجة المدموجة — يعرض الملف الأساسي ثم "--- /etc/.../override.conf ---" systemctl cat nginx # الاختصار: يفتح $EDITOR بالملف الصحيح تلقائيًا systemctl edit nginx
نصيحة احترافية: systemctl edit nginx هو سير العمل المفضَّل — ينشئ مجلد الـ drop-in والملف تلقائيًا، ويُشغِّل daemon-reload عند الحفظ. استخدمه بدلًا من إنشاء الملفات يدويًا لتجنب الأخطاء المطبعية في المسارات.

توجيهات التبعية: Requires مقابل Wants

أكثر مصدر شائع لأخطاء وقت الإقلاع في ملفات الوحدات المخصصة هو سوء استخدام توجيهات التبعية. إليك الدلالات الدقيقة:

  • Requires= — تبعية صارمة. إذا فشلت الوحدة المطلوبة في البدء، تُوقَف هذه الوحدة أيضًا. استخدمها باعتدال.
  • Wants= — تبعية لينة. يحاول systemd تشغيل الوحدة المطلوبة، لكن هذه الوحدة تستمر حتى لو فشلت. مفضَّلة لمعظم تبعيات الخدمات.
  • After= / Before= — ترتيب فقط، لا تبعية. بدون هذه التوجيهات، يبدأ systemd الوحدات بالتوازي. اقرن دائمًا توجيه التبعية بتوجيه ترتيب.
  • BindsTo= — مثل Requires= لكنه أشد: إذا توقفت الوحدة المرتبطة لأي سبب (ليس فقط فشل البدء)، تُوقَف هذه الوحدة فورًا. استخدمه للوحدات التي تفقد معناها تمامًا بغياب الأخرى.

النمط الأكثر شيوعًا لخدمات التطبيقات: Wants= وAfter= للتبعيات اللينة (قاعدة البيانات، التخزين المؤقت)، وRequires= وAfter= فقط للبنية التحتية الصارمة (الشبكة، التركيبات المطلوبة).