خدمة systemd لا تبدأ — الأسباب الخمسة، والسجل الذي يسمّيها
لا تكاد تكون غموضاً قط: systemctl status، ثم آخر 50 سطراً من الـjournal — وأحد الأسباب الخمسة يكون قد سُمّي نصاً صريحاً. الإصلاحات بعدها مجرد مطابقة أنماط.
■فضاء · مسار النثر
systemctl status + journalctl -u <unit> -n 50 --no-pager — أحد الأسباب الخمسة مُسمّى في السجل.
203/EXEC = المسار، أو shebang، أو بت التنفيذ — systemd يشغّل ExecStart= دون صدفة.
يفشل عند الإقلاع فقط؟ سباق في الترتيب — أضِف After=/Wants= وحارساً ExecStartPre=.
الأمران
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager
status يعطيك الحالة — inactive (dead), failed (exit-code), activating (auto-restart) — وآخر بضعة أسطر من السجل. والـjournal يعطي الحكاية كاملة: stdout وstderr وشكاوى systemd الخاصة عن الوحدة. فشل سابقاً وتريد سجل ذلك التشغيل تحديداً؟ أضِف -b للإقلاع الحالي، أو --since today. زوج أخير يستحق المعرفة:
systemctl list-units --failed # every red unit on the box
systemctl reset-failed myapp.service # clear the failed state after the fix
verify: systemctl list-units --failed → 0 وحدة محمّلة مدرج
الأسباب الخمسة
بعد أن يسمّي السجل العرض، السبب هو تقريباً دائماً أحد هذه — افحص الأرجح أولاً:
| السجل يقول | السبب المحتمل | الإصلاح |
|---|---|---|
status=203/EXEC | خطأ في ExecStart=المسار أو المفسّر غائب | مسار مطلق، chmod +x، افحص الـshebang |
status=203/EXEC على سكربت | نهايات أسطر CRLF أو shebang سيئ | dos2unix script.sh، أصلح السطر الأول |
status=1/FAILURE، لا سجل من التطبيق | دليل العمل أو متغير البيئة غائب | اضبط WorkingDirectory=، وأضِف Environment= |
Permission denied | المستخدم لا يقدر على قراءة الملفات أو ربط المنفذ | أصلح الملكية؛ المنافذ دون 1024 تطلب AmbientCapabilities=CAP_NET_BIND_SERVICE أو root |
Unit is masked | شخصٌ ما شغّل systemctl mask | systemctl unmask myapp.service |
| تعديل ملف الوحدة «لا يفعل شيئاً» | الخادم لم يُعاد تحميله | systemctl daemon-reload |
عائلة 203/EXEC
الذي يلتهم بعد الظهر. systemd لا يستخدم صدفتك ليطلق ExecStart= — لا توسيع لـ~، لا PATH بحثاً، ولا cd. شاهد الأسطر الخاطئة تصير صحيحة:
سكربت يعمل جيداً تماماً في صدفتك لكنه يفشل برمز 203 تحت systemd، فسببه تقريباً دائماً أحد هذه الأربعة: غير قابل للتنفيذ، أو نهايات CRLF، أو shebang يشير إلى الفراغ، أو مسار نسبي.
يعمل باليد، ينتهي عند الإقلاع
الفشل وقت الإقلاع مسألة ترتيب، لا صياغة. السباق الكلاسيكي: خدمتك تنال الشبكة أو قرصاً مركباً أو قاعدة بيانات قبل أن يكون ذلك موجوداً. صرّح بالتبعيات بدل الأمل:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml
After= يرتّب البدء؛ Wants= تجعل systemd يرفع التبعية فعلاً. network-online.target تعمل فقط إن كانت خدمة الانتظار للشبكة مفعّلة في توزيعك — افحص systemctl is-enabled NetworkManager-wait-online.service (أو ما يوازنها في systemd-networkd). أما الحارس ExecStartPre= فيفشل بصوت عالٍ برسالة مقروءة بدل أثر استدعاء.
verify: systemctl is-enabled NetworkManager-wait-online.service → enabled
الشكل الثاني: يبدأ عند الإقلاع ثم يموت فوراً. ابحث عن ما كان في بيئتك التفاعلية لا عند الإقلاع — PATH مختلف، أو virtualenv، أو HOME. systemd يبدأ الوحدات ببيئة مصفّاة؛ كل ما تقدّمه صدفتك ضمنياً يجب أن تُتهجّى صراحة:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app
للألغاز الحقيقية، شغّل أمر ExecStart= نفسه، وكمستخدم الوحدة، في صدفة — أكثر اختلافات البيئة يظهر في أول عشر ثوانٍ.
لا شيء في الجورنال؟
StandardOutput=وStandardError=في الوحدة — يجب أن يكوناjournal(افتراضياً) أوjournal+console. ربما ضبطهما شخص ما علىnull.- التطبيق يكتب ملفاً بدل stdout. systemd لا يلتقط إلا stdout/stderr؛ من يكتب إلى ملف سجلات يتجاوز الـjournal كلياً. وجّه التطبيق إلى stdout أو اقرأ الملف.
- حدود التخزين أسقطت أسطراً قديمة:
journalctl --disk-usage، وSystemMaxUse=في/etc/systemd/journald.confإن كان الـjournal يخنق نفسه.
الانطلاق من جديد بعد الانهيار
السياسة الافتراضية Restart=no: خدمة منهارة تبقى ميتة، وتكتشف الأمر من مستخدم. أصلح ذلك خدمةً خدمة:
[Service]
Restart=on-failure
RestartSec=5
verify: systemctl show myapp.service --property=Restart → Restart=on-failure
| الخيار | يعيد الاشتغال متى |
|---|---|
no (افتراضي) | أبداً |
on-failure | رمز خروج غير صفر، إشارة، مهلة |
always | أي خروج، حتى النظيف |
on-watchdog | مهلة الـwatchdog وحدها |
اجمع Restart=on-failure مع حارس لمعدل البدء كي لا تدق حلقة الانهيار الصندوق: StartLimitIntervalSec= وStartLimitBurst= في المقطع [Unit]. خمسة إخفاق في ستين ثانية ينبغي أن تستأنس إنساناً، لا أن تدوّر معالجاً عبثاً. وإن كنت تنصّب هذا كلقب مبرمَج بدل الخادم، فالـtimers تنال كتسجيل الجورنال وترتيب التبعيات مجاناً — قَدِّر cron على ميزان timer أولاً.
قائمة الستين ثانية
systemctl status <unit>— اقرأ الحالة وآخر الأسطر.journalctl -u <unit> -n 50 --no-pager— ابحث عن الخطأ الحقيقي.203/EXEC؟ أصلح المسار وshebang والصلاحيات.Permission denied؟ أصلح المستخدم وملكية الملفات.- يفشل عند الإقلاع فقط؟ أضِف
After=/Wants=network-online.targetوحارساًExecStartPre=. - غيّرت ملف وحدة؟
systemctl daemon-reload && systemctl restart <unit>. - أضِف
Restart=on-failureكي يُعلن الانهيار التالي عن نفسه بدل أن يتوارى.
معظم لحظات «systemd بسيط التفصيل المزعوم» ينعقد أحدها إلى الخطأ كان في الجورنال من دهر. اقرأه قبل أن تحرر ملف الوحدة، لا بعده.
نهاية الأسباب الخمسة · لم يُجلب أي شيء · كل شيء محلي