Что делает PID 1
Когда ядро загрузилось - оно стартует один процесс с PID 1. На современных Linux это systemd. Его задачи:
- Запустить нужные сервисы при старте системы
- Следить за их состоянием, перезапускать упавшие
- Управлять зависимостями («старт docker после network»)
- Подбирать «осиротевшие» процессы (parent умер - мы становимся новым)
- Логировать всё через journald
- Управлять cgroups для лимитов и изоляции
Unit-файлы
Всё что systemd умеет делать = unit. Виды:
.service- обычный демон (nginx, ssh, postgres).socket- слушать сокет, запустить сервис при первом подключении.timer- cron-replacement: запускать unit по расписанию.mount/.automount- монтирование ФС.target- группа unit'ов (как runlevel:multi-user.target,graphical.target).path- реагировать на появление/изменение файла.slice- узел иерархии cgroup'ов
Расположение:
/lib/systemd/system/- от пакетов/etc/systemd/system/- твои локальные / drop-in'ы (приоритет)
Минимальный service unit
[Unit]
Description=My App
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp --config=/etc/myapp.yml
Restart=on-failure
RestartSec=5
User=myapp
Group=myapp
[Install]
WantedBy=multi-user.target
Положить в /etc/systemd/system/myapp.service, потом:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
Type - модель запуска
simple- ExecStart = бинарь который НЕ форкается; defaultforking- старая школа (бинарь форкает дочерку и выходит);PIDFile=нуженnotify- приложение шлётREADY=1черезsd_notifyкогда готовоoneshot- выполнить и выйти (для скриптов/настроек)idle- простой, но запустить когда другие units стабилизировались
Зависимости
Wants=- мягкая зависимость (мы хотим, но не критично)Requires=- жёсткая (если зависимость упала - мы тоже)After=/Before=- порядок (не зависимость, а очерёдность!)Conflicts=- взаимоисключение
Изоляция через cgroups
systemd кладёт каждый сервис в свой cgroup и поддерживает sandboxing:
[Service]
MemoryMax=512M
CPUQuota=50%
PrivateTmp=yes # свой /tmp
ProtectSystem=strict # /usr, /etc r/o
ProtectHome=yes # /home, /root недоступны
NoNewPrivileges=yes # запрет setuid-эскалации
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
Это даёт большую часть «контейнеризации» без Docker.
Targets вместо runlevels
Старое vs новое:
| runlevel | systemd target |
|---|---|
| 0 | poweroff.target |
| 1 | rescue.target |
| 3 | multi-user.target (CLI) |
| 5 | graphical.target (GUI) |
| 6 | reboot.target |
systemctl get-default # текущий default-target
sudo systemctl set-default multi-user.target
systemctl isolate rescue.target # перейти в target прямо сейчас
Дебаг
systemctl status myapp # state + последние логи
journalctl -u myapp -f # tail логов
systemd-analyze blame # кто медленно стартовал
systemd-analyze critical-chain # критический путь загрузки
systemctl list-dependencies myapp