الأمن السيبراني

systemd: إنشاء والإشراف على خدمة Linux — درس خطوة بخطوة

5 min de lecture

🔝 الدليل الرئيسي: أساسيات Linux 2026

على كل Linux حديث — Ubuntu 24.04 و26.04 LTS، Debian 13، AlmaLinux 9، Rocky Linux 9، Fedora، openSUSE — systemd هو الذي يُطلق النظام، يُشرف على الخدمات، يُركّب الأقراص، يُدير logs ويُنسّق الإيقاف. تعلّم إنشاء والإشراف على خدمة systemd من أكثر الكفاءات ربحية لمسؤول أو مطوّر ينشر على VPS: في 5 أسطر إعداد، تُحوّل script Node أو Python هشًّا إلى démon resilient يُعيد التشغيل وحده، يُسجّل بنظافة، ويصمد عند reboot.

المتطلبات

  • Linux حديث بـ systemd 245+. الإصدار 260 المنشور مارس 2026 يحذف دعم scripts SysV التاريخية
  • حساب بوصول sudo
  • أساسيات سطر الأوامر والصلاحيات
  • المستوى: متوسط مبتدئ
  • الوقت المُقدَّر: 90 دقيقة

الخطوة 1 — التحقق من التثبيت وقراءة الحالة العامة

systemctl --version                          # version exacte de systemd
systemctl status                             # vue arborescente du PID 1
systemctl list-units --type=service          # tous les services chargés
systemctl list-units --state=failed          # uniquement les services en échec
systemctl list-unit-files --type=service     # services connus, activés ou non

مخرج systemctl status يُظهر شجرة حيث PID 1 هو systemd نفسه، تليه كل الوحدات النشطة. على خادم سليم، systemctl list-units --state=failed يُرجع « 0 loaded units listed ». التمييز بين list-units (خدمات مُحمَّلة في الذاكرة) وlist-unit-files (خدمات ملفها موجود على القرص) مفيد.

الخطوة 2 — إتقان systemctl على خدمة موجودة

sudo systemctl start nginx           # démarre maintenant
sudo systemctl stop nginx            # arrête maintenant
sudo systemctl restart nginx         # arrête puis démarre
sudo systemctl reload nginx          # recharge la config sans interruption (si supporté)
sudo systemctl enable nginx          # activera au prochain démarrage
sudo systemctl disable nginx         # désactivera au prochain démarrage
sudo systemctl enable --now nginx    # active ET démarre dans la foulée
systemctl status nginx               # état détaillé : actif/inactif, PID, RAM, dernières lignes de log

التمييز بين restart وreload يستحق الفهم. restart يقتل العملية ويُعيد إطلاقها — هناك انقطاع بضع ميلي ثوانٍ إلى ثوانٍ. reload يُرسل إشارة SIGHUP للـ démon الذي يُعيد قراءة تهيئته دون إنهاء اتصالاته النشطة — لا انقطاع، لكن كل démons لا تدعم هذه العملية. Nginx، sshd، postfix يدعمون؛ script Node بدون إدارة SIGHUP لا.

الخطوة 3 — تشريح ملف unit

خدمة systemd مُوصَفة في ملف نص بصيغة INI، تقليديًا في /etc/systemd/system/ للخدمات المخصصة وفي /lib/systemd/system/ للحزمية. الملف يحوي 3 أقسام إلزامية: [Unit]، [Service]، [Install].

[Unit]
Description=Mon API Node.js
Documentation=https://exemple.com/docs
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mon-app
Group=mon-app
WorkingDirectory=/opt/mon-app
ExecStart=/usr/bin/node /opt/mon-app/server.js
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

After= وWants= يضمنان توفّر الشبكة قبل الإطلاق. Type=simple يُشير لأن العملية في ExecStart هي الخدمة نفسها. User= وGroup= يُنفّذان الخدمة تحت حساب غير مُمتاز. Restart=on-failure يُعيد التشغيل تلقائيًا عند crash. StandardOutput=journal يُرسل المخرج لـ journalctl.

الخطوة 4 — إنشاء أول خدمة

# 1. Créer un utilisateur de service dédié
sudo useradd -r -s /usr/sbin/nologin -d /opt/mon-app -m mon-app

# 2. Déposer un script Node trivial
sudo tee /opt/mon-app/server.js >/dev/null <<'JS'
const http = require('http');
const port = process.env.PORT || 3000;
http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Bonjour depuis systemd\n');
}).listen(port, () => console.log('listening on', port));
JS

# 3. Ajuster les droits
sudo chown -R mon-app:mon-app /opt/mon-app
sudo chmod 750 /opt/mon-app

# 4. Créer le fichier d'unité
sudo tee /etc/systemd/system/mon-app.service >/dev/null <<'UNIT'
[Unit]
Description=Mon API Node de test
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mon-app
Group=mon-app
WorkingDirectory=/opt/mon-app
Environment=PORT=3000
ExecStart=/usr/bin/node /opt/mon-app/server.js
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
UNIT

# 5. Recharger systemd, activer et démarrer
sudo systemctl daemon-reload
sudo systemctl enable --now mon-app

# 6. Vérifier
systemctl status mon-app
curl http://127.0.0.1:3000/

systemctl daemon-reload ضروري بعد أي تعديل لملف unit. إذا عرض systemctl status mon-app « active (running) » وأرجع curl « Bonjour depuis systemd »، الخدمة تعمل. للتحقق من المرونة، اقتل العملية بـ sudo pkill -f server.js ثم أعد systemctl status mon-app بعد 5 ثوانٍ: ستكون عادت.

الخطوة 5 — كشف التهيئة ومتغيّرات البيئة

sudo mkdir -p /etc/mon-app
sudo tee /etc/mon-app/env >/dev/null <<'ENV'
PORT=3000
DATABASE_URL=postgres://user:pass@localhost/db
LOG_LEVEL=info
ENV
sudo chmod 640 /etc/mon-app/env
sudo chown root:mon-app /etc/mon-app/env

في ملف unit، استبدل Environment=PORT=3000 بـ EnvironmentFile=/etc/mon-app/env ثم sudo systemctl daemon-reload && sudo systemctl restart mon-app. الصلاحيات 640 مع مالك root ومجموعة mon-app تضمن أن الخدمة فقط تصل في قراءة، أبدًا في كتابة، ولا مستخدم آخر يستطيع قراءة الأسرار.

الخطوة 6 — التقوية بـ directives الـ sandboxing

[Service]
NoNewPrivileges=yes              # impossible d'élever les privilèges via setuid
ProtectSystem=strict             # / en lecture seule, sauf /etc /var
ProtectHome=yes                  # /home, /root, /run/user invisibles
PrivateTmp=yes                   # /tmp et /var/tmp privés au service
PrivateDevices=yes               # accès aux périphériques refusé
ProtectKernelTunables=yes        # /proc/sys et /sys en lecture seule
ProtectKernelModules=yes         # impossible de charger des modules noyau
RestrictAddressFamilies=AF_INET AF_INET6  # uniquement IPv4 et IPv6
LockPersonality=yes              # interdit personality(2)
MemoryDenyWriteExecute=yes       # interdit pages mémoire write+exec (anti-JIT exploit)

كل هذه directives لا يمكن تطبيقها على أي خدمة: MemoryDenyWriteExecute=yes يكسر بعض runtimes JIT مثل V8 القديمة. الانعكاس الجيد: فعّل directive واحدة في كل مرة، أعد التشغيل، تحقّق أن الخدمة لا تزال تعمل، تابع. الأمر systemd-analyze security mon-app.service يُعطي علامة الأمان من 0 إلى 10 ويسرد directives للإضافة.

الخطوة 7 — Drop-ins وtimers

بدلًا من تعديل ملف unit مُقدَّم من حزمة (الذي سيُكتَب فوقه عند الترقية)، systemd يُتيح إنشاء drop-in:

sudo systemctl edit mon-app
# ouvre un éditeur ; on y met par exemple :
# [Service]
# MemoryMax=512M
# CPUQuota=50%

الملف المُنشأ هو /etc/systemd/system/mon-app.service.d/override.conf. MemoryMax يفرض حدًا للذاكرة. CPUQuota=50% يُحدّد استخدام CPU بنصف نواة. هذه الحدود تتجنّب أن يُشبع bug تطبيقي (تسرّب ذاكرة، حلقة لا نهائية) VPS متواضعًا.

للمهام المتكررة، systemd يحل محل cron عبر timers. timer هو unit .timer مرتبط بـ unit .service. صياغة OnCalendar=*-*-* 03:00:00 تُعيد إنتاج cron يومي الساعة 3 صباحًا، مع ميزة Persistent=true التي تُلحق التنفيذات الفائتة.

الخطوة 8 — التحقق والتنظيف

systemctl status mon-app                          # actif ?
journalctl -u mon-app -n 30 --no-pager            # 30 dernières lignes de log
systemd-analyze security mon-app.service          # note de sécurité
systemctl is-enabled mon-app                      # enabled ?
systemctl show mon-app -p MainPID,User,Restart    # propriétés clés

# Nettoyage si exercice terminé
sudo systemctl disable --now mon-app
sudo rm /etc/systemd/system/mon-app.service
sudo rm -rf /etc/mon-app /opt/mon-app
sudo userdel mon-app
sudo systemctl daemon-reload
sudo systemctl reset-failed mon-app 2>/dev/null || true

إذا كانت علامة الأمان أقل من 5، أضف directives sandboxing وراقب العلامة ترتفع. علامة حول 1 تُشير لخدمة شبه معزولة عن النظام؛ الهدف 2-3 واقعي لغالبية تطبيقات Node أو Python.

أخطاء شائعة

الخطأ السبب الحل
« Failed to start » عند البوت تبعية مفقودة أو شبكة غير جاهزة تحقّق من After=، أضف Wants=network-online.target
خدمة تُعيد التشغيل في حلقة crash تطبيقي؛ Restart=on-failure يُعيد journalctl -u service -n 100
« تعديل الملف متجاهَل » نسيان daemon-reload sudo systemctl daemon-reload && sudo systemctl restart service
« Permission denied » في الخدمة User= لا وصول له chown -R user:group /opt/...
OOM Killer يقتل الخدمة تسرّب ذاكرة أو حد منخفض اضبط MemoryMax
متغيّرات البيئة غائبة خلط بين Environment= وملف .env دائمًا EnvironmentFile= للتهيئات الخارجية
Type=forking لكن الخدمة تُرى ميتة PID file سيء الإعداد فضّل Type=simple أو Type=notify

الأسئلة الشائعة

لماذا systemd بدلًا من cron؟
ثلاث ميزات ملموسة: journalisation مُهيكَلة، إمكانية تعريف تبعيات بين المهام، وخيار Persistent=true الذي يُلحق التنفيذات الفائتة. cron يبقى أبسط للكتابة على الطاير لكن يفقد القراءة بسرعة.

ما الفرق بين Type=simple وType=notify؟
مع Type=simple، systemd يعتبر الخدمة نشطة بمجرد إطلاق binary. مع Type=notify، الخدمة تُرسل إشارة لـ systemd صراحةً عندما تكون جاهزة (عبر مكتبة sd_notify). الثاني مُفضَّل للخدمات بطيئة التهيئة.

كيف نحدّ من الذاكرة وCPU دون قتل الخدمة؟
MemoryMax سقف صارم. MemoryHigh أكثر مرونة: عند تجاوزه، النواة تُبطئ الخدمة. CPUWeight=100 يُحدّد أولوية نسبية.

كيف نرى الخدمات الأكثر تعطّلًا؟
journalctl --list-boots يسرد البدايات. journalctl _SYSTEMD_UNIT=mon-app.service | grep -i "main process exited" يسرد الخروجات الشاذة. systemd-cgtop يعرض في الزمن الحقيقي استخدام CPU/ذاكرة لكل خدمة.

هل نستخدم supervisord أو PM2 أو systemd؟
على خادم Linux حديث، systemd هو الجواب الافتراضي: مُثبَّت بالفعل، يتكامل مع journal النظام، ينجو من reboot، ويُقدّم directives sandboxing مستحيلة الاستنساخ مع superviseur تطبيقي.

كيف نُرحّل من script SysV موجود؟
systemd 260 حذف دعم scripts SysV. للترحيل، اكتب ملف .service مُكافئ. غالبية التحويلات تتسع في 15 سطرًا.

كيف ننفّذ أمرًا بعد فشل دون إعادة التشغيل في حلقة؟
OnFailure= يُتيح إطلاق unit أخرى عند فشل خدمة. نموذجيًا، نُعرّف خدمة مساعدة alerte@.service ترسل إيميل. مع StartLimitBurst=3 وStartLimitIntervalSec=60، نحصل على سياسة « 3 محاولات في دقيقة، ثم تخلّ وتنبيه ».

مقالات ذات صلة

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité