02:13, ваш пейджер робить свій крихітний сердитий танець, а сервіс, який треба перезапустити, «зупиняється…», ніби розмірковує про сенс життя. Найгірше: бізнес просить «просто перезавантажити сервер», бо це нібито «завжди допомагає». Звісно. Це також миттєво розширює вашу зону ураження до максимуму.
Ось безпечніший підхід: діагностуйте, чому сервіс завис, вирішіть, чи справді перезапуск потрібний, і якщо потрібно застосовувати примус, робіть це хірургічно. Ніякої магії. Жодних непотрібних перезавантажень. Тільки контрольовані наслідки.
Принципи: що означає «безпечний перезапуск»
«Безпечний перезапуск» — це не просто «сервіс запустився». Це «сервіс повернувся в робочий стан та ми не пошкодили дані, не заблокували хост і не спричинили каскадного збою». Це звучить очевидно, поки ви о 2 ранку дивитесь на завислий юніт і руки самі починають вводити reboot за звичкою.
1) Не перезапускайте, поки не зрозумієте, що саме зависло
Коли сервіс не зупиняється, це зазвичай не «упертість» процесу. Він чекає на щось: диск I/O, мережевий монт, блокування ядра, залежність, завершальний хук, дочірній процес, що не завершується, PID-файл, що бреше, або systemd, який очікує на нотифікацію, що не надійде.
Якщо ви перезапустите всліпу, часто перетворюєте одиночний завислий процес на місце злочину з багатьма процесами. Особливо якщо є клієнти бази даних, черги або шляхи зберігання.
2) Стан — це першокласна сутність
Безстанні веб-сервіси пробачають помилки. Сервіси зі станом — ні. Якщо завислий сервіс володіє даними (база даних, черга, експорт файлової системи, кеш із персистентністю), насильницька зупинка може спричинити довготривале відновлення, хвилі реплею або корупцію. Ваше завдання — явно зважити негайну доступність проти збереження цілісності та часу на відновлення.
3) Розумійте, що робить systemd, а не те, що ви надієтеся, що він робить
systemd — це не «крутий init-скрипт». Це супервізор процесів із графами залежностей, watchdog, cgroup та таймаутами. Коли ви просите його зупинити щось, він використовує конкретні сигнали у певному порядку для певного набору процесів. Якщо він завис, причину зазвичай можна знайти — якщо перевірити властивості юніта, членство в cgroup та журнал.
4) Ескалюйте примус поетапно та повідомляйте, що ви робите
Вам потрібна сходи ескалації. Починайте з ввічливих сигналів та перевірок коректності. Лише потім переходьте до SIGKILL, термінації cgroup або скидання стану failed. І коли ви дієте, комунікуйте. «Швидкий рестарт», що скидає роботу в польоті, рідко буває швидким для людей далі по ланцюжку.
5) Перезавантаження — це не виправлення; це ампутація
Перезавантаження працює тому, що воно очищає стан, скидає драйвери, розблоковує ресурси ядра та перезапускає все. Саме через це воно небезпечне: ви втрачаєте можливість діагностики, порушуєте роботу незв’язаних сервісів і можете спричинити довгі fsck / RAID rebuild / journal replay процедури.
Одна цитата, яка стане в пригоді як правильне мислення під час інциденту:
«Надія — не стратегія.» — Vince Lombardi
Так, це зі спорту. Операції теж контактний вид спорту; супротивник — ентропія.
Швидкий план діагностики (перший/другий/третій)
Це версія «у мене є п’ять хвилин, поки канал інциденту не перетвориться на інтерпретативний танець». Мета — швидко локалізувати клас вузького місця: логіка сервісу, залежності, системні ресурси або ядро/зберігання.
Перший: перевірте, що саме зависло
- Перевірте стан юніта та останні логи: Чи він «activating», «deactivating», «failed» чи «running but unhealthy»?
- Підтвердіть main PID та процеси в cgroup: Чи є дочірні процеси, що пережили головний процес?
- Подивіться, на що чекає systemd: таймаути, notify, watchdog, ExecStop хуки або порядок залежностей.
Другий: визначте, чи це очікування на I/O, блокування чи мережу
- Шукайте процеси в D state: uninterruptible sleep — величезний червоний прапор; вбивати такі процеси марно.
- Перевірте тиск на диск: високий iowait, насичений пристрій, застряглий multipath, заповнена файловa система.
- Перевірте мережеві монтування і розв’язання імен: зависання NFS і таймаути DNS можуть зупиняти стоп-скрипти.
Третій: оберіть найменш ризикову інтервенцію
- Якщо логіка зависла: перезапустіть ввічливо, злокуйте трафік, по черзі перезапускайте процеси.
- Якщо зависла залежність: виправте залежність першою (зберігання, DNS, монтування), потім перезапустіть.
- Якщо ядро/зберігання застрягли: не спамте рестарти. Вирішіть між ізоляцією хоста, фейловером або ретельно спланованим перезавантаженням.
Жарт №1: Якщо ви перезапускаєте сервіс п’ять разів і він «випадково» починає працювати, вітаю — ви винайшли навантажувальне тестування для своєї вдачі.
Цікаві факти та історичний контекст
- Unix-сигнали були створені для кооперативного завершення.
SIGTERM— це прохання, а не команда. Деякі демони поводяться чемно; деякі ігнорують його; деякі сприймають як особистий виклик. - «Зависання в D state» старше за ваш стек моніторингу. Uninterruptible sleep існує десятиліттями, оскільки ядро чекає завершення I/O; це підказка, що проблема нижче userspace.
- systemd ввів відстеження процесів через cgroup як практичне рішення для класичної проблеми «демон здійснив fork і лишив сироту», яка переслідувала SysV init-скрипти.
- PID-файли — історичний компроміс. Вони були обхідним шляхом для супервізорів, які не могли надійно відслідковувати процеси. Вони досі спричиняють інциденти, коли застарівають.
- Таймаути — відносно сучасна операційна звичка. Старі init-скрипти часто чекали вічно; сучасні менеджери сервісів вважають «вічно» неприйнятним і ескалують або фейлять.
- NFS застрягає під час завершення роботи ще з 1980-х. Це не злеумисно; це розподілені системи, що чесно показують моделі відмов.
- У баз даних є режими «швидкого завершення» з причини. PostgreSQL, MySQL та інші відрізняють «завершити роботу», «зробити чекпойнт» та «скинути все», бо вартість відновлення після краху реальна.
- Watchdog-інструменти стали популярні після достатньої кількості тихих зависань. Сервіс, що «працює», але не просувається вперед, гірший за сервіс, що швидко падає; супервізори додали watchdog для примусу рішення.
Практичні завдання: команди, виводи та рішення
Нижче — практичні завдання, які можна виконати на типовому Linux-хості з systemd. Кожне завдання містить: команду, приклад виводу, що це означає, та рішення, яке ви приймаєте.
Завдання 1: Підтвердіть стан юніта та чому systemd вважає його завислим
cr0x@server:~$ systemctl status nginx.service --no-pager
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: deactivating (stop-sigterm) since Tue 2026-02-05 02:11:27 UTC; 1min 32s ago
Docs: man:nginx(8)
Process: 18421 ExecStop=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid (code=exited, status=0/SUCCESS)
Main PID: 17102 (nginx)
Tasks: 5 (limit: 18958)
Memory: 62.3M
CPU: 3.114s
CGroup: /system.slice/nginx.service
├─17102 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─17105 nginx: worker process
└─17106 nginx: worker process
Feb 05 02:11:27 server systemd[1]: Stopping A high performance web server and a reverse proxy server...
Feb 05 02:11:57 server systemd[1]: nginx.service: State 'stop-sigterm' timed out. Killing.
Feb 05 02:11:57 server systemd[1]: nginx.service: Killing process 17102 (nginx) with signal SIGKILL.
Що це означає: systemd спробував ввічливу зупинку, досяг таймауту і ескалував. Юніт досі деактивується, що свідчить або про те, що процес не помер, або діти втекли, або ядро не може його прибрати.
Рішення: Перейдіть від «вигляду юніта» до «вигляду процесів». Визначте, які PID усе ще існують і в якому вони стані. Не продовжуйте бездумно викликати рестарти; збирайте докази.
Завдання 2: Показати точну поведінку зупинки юніта (таймаути, kill mode)
cr0x@server:~$ systemctl show nginx.service -p TimeoutStopUSec -p KillMode -p KillSignal -p SendSIGKILL -p ExecStop -p ExecStopPost
TimeoutStopUSec=1min 30s
KillMode=control-group
KillSignal=SIGTERM
SendSIGKILL=yes
ExecStop={ path=/sbin/start-stop-daemon ; argv[]=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=18421 ; code=exited ; status=0 }
ExecStopPost=
Що це означає: systemd вб’є всю cgroup, якщо потрібно. Таймаут — 90 секунд. Команда зупинки використовує PID-файл; це потенційний вектор помилок.
Рішення: Якщо PID-файл застарів або неправильний, зупинка може зависнути. Перевірте PID-файл і чи є процеси в cgroup.
Завдання 3: Інспектувати членство в cgroup (що systemd вважає своїм)
cr0x@server:~$ systemd-cgls /system.slice/nginx.service
Control group /system.slice/nginx.service:
├─17102 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─17105 nginx: worker process
└─17106 nginx: worker process
Що це означає: Це процеси, яким systemd відправить сигнали при зупинці. Якщо ви бачите несподівані процеси, ваш сервіс може форкати помічники, які ніколи не виходять.
Рішення: Якщо cgroup порожня, а systemd каже «stopping», ви можете чекати ExecStop хуків або завислого монтування/DNS виклику всередині них.
Завдання 4: Перевірити, чи процес у стані uninterruptible sleep (D state)
cr0x@server:~$ ps -o pid,ppid,stat,wchan:30,cmd -p 17102
PID PPID STAT WCHAN CMD
17102 1 D nfs_wait_bit_killable nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
Що це означає: Процес у D state, чекає на функцію ядра пов’язану з NFS. У D state сигнали не опрацьовуються, поки системний виклик не поверне управління. SIGKILL не допоможе.
Рішення: Перестаньте намагатися його вбити. Знайдіть NFS-монтування, мережеву/зберігальну проблему або плануйте фейловер. Це не проблема nginx.
Завдання 5: Виявити відкриті файли/сокети, що можуть блокувати зупинку
cr0x@server:~$ sudo lsof -p 17102 | tail -n 8
nginx 17102 root 10u IPv4 541233 0t0 TCP *:http (LISTEN)
nginx 17102 root 11u IPv4 541234 0t0 TCP *:https (LISTEN)
nginx 17102 root 12r REG 0,37 10485760 917514 /var/log/nginx/access.log
nginx 17102 root 13r REG 0,37 2097152 917515 /var/log/nginx/error.log
nginx 17102 root 14r REG 0,41 131072 26233 /mnt/shared/certs/bundle.pem
nginx 17102 root 15r REG 0,41 131072 26234 /mnt/shared/certs/key.pem
Що це означає: Сервіс має відкриті файли в /mnt/shared, що, здається, знаходиться на окремій файловій системі (пристрій 0,41). Якщо це NFS і він нездоровий, завершення може блокуватися на файлових операціях.
Рішення: Перевірте тип монтування і стан. Якщо це віддалене монтування і воно застрягло, виправте його спочатку або відключіть трафік і перемістіть нагрузку.
Завдання 6: Підтвердити тип монтування і чи це мережева файлова система
cr0x@server:~$ findmnt -T /mnt/shared -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET SOURCE FSTYPE OPTIONS
/mnt/shared nfs01:/exports/shared nfs4 rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,sec=sys
Що це означає: Це NFSv4.1 з опцією hard. Hard-монтування може зависати процеси під час проблем з сервером/мережею. Іноді це правильний вибір; ніколи — безкоштовний.
Рішення: Якщо NFS нестабільний, вирішіть між відновленням NFS, фейловером сервісу на інший вузол або (востаннє) перезавантаженням клієнта, розуміючи, що при нездоровому NFS проблема може повернутися.
Завдання 7: Перевірити, чи хост під пам’яттєвим тиском або свапиться
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 31Gi 29Gi 512Mi 1.2Gi 1.6Gi 745Mi
Swap: 8Gi 6.9Gi 1.1Gi
Що це означає: Низька доступна пам’ять і інтенсивне свапування. Навіть ввічлива зупинка може повільно виконуватись, якщо процес активно підвантажується з диску.
Рішення: Перед перезапуском подумайте про полегшення навантаження (зупиніть некритичні завдання, масштабування, додайте пам’ять або зменшіть навантаження). Перезапуск сервісу, що виснажує пам’ять, може погіршити ситуацію через підвантаження кешів і повторне свапування.
Завдання 8: Перевірити насиченість I/O (зависання може бути через латентність зберігання)
cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (server) 02/05/2026 _x86_64_ (8 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.21 0.00 4.05 38.44 0.00 45.30
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
nvme0n1 120.0 4800.0 0.0 0.0 120.22 40.0 95.0 9200.0 2.0 2.1 210.45 96.8 35.20 99.8
Що це означає: Практично 100% використання пристрою, величезні часи очікування і високий iowait. Ваш «завислий стоп» може бути жертвою насиченого диска.
Рішення: Не перезапускайте сервіси в середовищі з палаючою чергою I/O. Знайдіть топ-споживачів I/O, полегшіть навантаження або зробіть фейловер. Перезапуск часто збільшує I/O (реплей логів, прогрів кешу, ребілд індексів тощо).
Завдання 9: Знайти процеси, що навантажують диск
cr0x@server:~$ sudo iotop -oPa -n 1
Total DISK READ: 15.20 M/s | Total DISK WRITE: 42.10 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND
22340 be/4 postgres 1.20 M/s 18.30 M/s 12.00 % 85.00 % postgres: checkpointer
22410 be/4 postgres 0.00 B/s 10.20 M/s 0.00 % 60.00 % postgres: wal writer
30112 be/4 root 0.00 B/s 9.80 M/s 0.00 % 55.00 % rsync -a /var/log/ /mnt/backup/
Що це означає: Фонові завдання (записувачі бази даних, rsync для бекапу) домінують у записах. Ваш завислий сервіс може бути побічною жертвою.
Рішення: Призупиніть або обмежте непотрібні I/O задачі (бекапи, пакетні роботи). Якщо база даних виконує необхідне відновлення, дайте їй завершити, перш ніж торкатися залежних сервісів.
Завдання 10: Перевірити журнал на предмет блокуючих stop/start хукiв
cr0x@server:~$ journalctl -u nginx.service -n 50 --no-pager
Feb 05 02:10:55 server systemd[1]: Stopping A high performance web server and a reverse proxy server...
Feb 05 02:10:55 server start-stop-daemon[18421]: stopping nginx (pid 17102)...
Feb 05 02:11:25 server start-stop-daemon[18421]: waiting for nginx to die...
Feb 05 02:11:55 server start-stop-daemon[18421]: waiting for nginx to die...
Feb 05 02:11:57 server systemd[1]: nginx.service: State 'stop-sigterm' timed out. Killing.
Що це означає: Стоп-хелпер чекає. Це не доказ багу в застосунку; це доказ того, що процес не може вийти або не може бути прибиран.
Рішення: Корелюйте з станом процесу (ps) та очікуваннями ядра (wchan). Якщо це D-state, виправляйте підкладковий I/O збій.
Завдання 11: Перевірити здоров’я DNS (так, DNS може зависати під час завершення)
cr0x@server:~$ resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (ens192)
Current Scopes: DNS
Protocols: +DefaultRoute
Current DNS Server: 10.10.0.53
DNS Servers: 10.10.0.53 10.10.0.54
DNS Domain: corp.internal
Що це означає: У вас systemd-resolved у stub-режимі. Якщо резолвер повільний або недоступний, сервіси, які роблять зворотні запити під час завершення (логування, автентифікація, метрики), можуть зависати.
Рішення: Якщо в логах видно затримки розв’язання імен, протестуйте запити і відновіть доступність резолвера перед перезапуском залежних сервісів.
Завдання 12: Побачити збої залежностей і проблеми порядку запуску
cr0x@server:~$ systemctl list-dependencies --reverse postgresql.service
postgresql.service
● ├─app-api.service
● ├─worker.service
● └─reporting.service
Що це означає: Ці сервіси залежать від PostgreSQL. Перезапуск PostgreSQL пошириться назовні — якщо ви не зупините/зальшите залежні сервіси першими.
Рішення: Вирішіть, чи треба перезапустити залежність (Postgres) або залежний сервіс (app-api). Часто симптом знаходиться в залежному, а причина — у залежності. Працюйте з порядком навмисно.
Завдання 13: Перевірити застарілий PID-файл, що блокує старт/стоп
cr0x@server:~$ sudo cat /run/nginx.pid
99999
cr0x@server:~$ ps -p 99999 -o pid,cmd
PID CMD
Що це означає: PID-файл вказує на неіснуючий процес. Деякі стоп-скрипти чекатимуть або помиляться; деякі старт-скрипти відмовлятимуться стартувати, думаючи, що демон ще живий.
Рішення: Якщо ви підтвердили, що nginx не запущений, видаліть застарілий PID-файл і стартуйте чисто. Якщо він запущений, але PID змінився, виправте юніт, щоб уникнути крихкості PID-файлу (віддавайте перевагу відстеженню через cgroup / Type=notify коли можливо).
Завдання 14: Спробувати безпечний reload замість restart (коли можливо)
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
cr0x@server:~$ sudo systemctl reload nginx.service
Що це означає: Reload зберігає master-процес і підмінює конфіг, часто уникаючи розривів з’єднань. Це не завжди підтримується, але коли так — це найм’якший важіль.
Рішення: Якщо проблема пов’язана з конфігурацією, віддавайте перевагу reload. Якщо процес завис, reload не допоможе; не прикидайтеся, що він допоможе.
Завдання 15: Скинути systemd-стан failed перед повторною спробою (щоб бачити свіжі помилки)
cr0x@server:~$ systemctl is-failed worker.service
failed
cr0x@server:~$ sudo systemctl reset-failed worker.service
Що це означає: Скидання failed очищує защіпку, щоб моніторинг і люди бачили наступну помилку чисто, а не минуле привидне повідомлення.
Рішення: Робіть це перед наступною спробою старту під час інциденту, інакше ви гнатиметеся за застарілими симптомами.
Завдання 16: Коли systemd зависає, перевірте черги job-ів і заблоковані транзакції
cr0x@server:~$ systemctl list-jobs
JOB UNIT TYPE STATE
421 nginx.service stop running
422 app-api.service stop waiting
Що це означає: app-api чекає, бо stop nginx досі виконується. Транзакції systemd серіалізують певні операції; один завислий юніт може заблокувати інші.
Рішення: Виправте/очистіть завислий юніт або ізолюйте, працюючи з юнітами, що не заблоковані транзакцією. Уникайте запуску десятків нових job-ів у заторі.
Сходи ескалації: від ввічливого до жорсткого (без паніки)
Коли щось зависає, люди тягнуться до великих червоних кнопок. Протистійте цьому. Використовуйте сходи ескалації, бо ви хочете найменш руйнівний спосіб відновити сервіс, зберігаючи докази.
Рівень 0: Вирішіть, чи перезапуск взагалі потрібний
Перезапуск доцільний, коли:
- Процес живий, але погано працює (deadlock, витік пам’яті, завислий пул потоків) і у вас є надмірність або план дренування.
- Потрібен перезапуск через зміну конфігурації (і reload недоступний).
- Залежність виправлена і потрібен чистий хендшейк (наприклад, повторне підключення до бази даних, перемонтування сховища).
Перезапуск — погана ідея, коли:
- Процес у D state (I/O wait). Вбивати марно; повторні спроби тільки марнують час і плутають ситуацію.
- Хост під сильним пам’яттєвим або I/O тиском. Перезапуск додає навантаження.
- Ви можете зробити фейловер на здоровий вузол швидше, ніж «полагодити» цей.
Рівень 1: Ввічлива зупинка/старт з дренуванням трафіку
Якщо ви за балансувальником, спочатку дренуйте. Не «перезапускайте на місці», сподіваючись, що клієнти почекають.
cr0x@server:~$ sudo systemctl stop app-api.service
Рішення: Якщо stop відбувся швидко і чисто — стартуйте. Якщо завис — переходьте до інспекції, а не повторюйте команду.
Рівень 2: Використовуйте reload або ротацію воркерів, якщо підтримується
Для деяких демонів (nginx, haproxy, деякі агенти логування) reload достатній. Для інших можна «поворотити» воркерів (наприклад, відправити сигнал для створення нових і завершення старих). Це зберігає сокети відкритими і зменшує біль клієнтів.
Рівень 3: Підтвердіть, що блокує завершення (локи, I/O, залежності)
Тут ви використовуєте journalctl, lsof, ps wchan та інструменти ресурсів. Мета — визначити, чи ви чекаєте на зберігання, мережу чи логіку застосунку.
Рівень 4: Цільова термінація потрібних процесів
Якщо процес у переривному стані і просто ігнорує SIGTERM, ви можете ескалювати з наміром. Робіть це в cgroup сервісу, а не випадково шукати PIDs.
cr0x@server:~$ sudo systemctl kill -s SIGTERM nginx.service
cr0x@server:~$ sudo systemctl kill -s SIGKILL nginx.service
Що це означає: Ви просите systemd послати сигнал усім процесам у cgroup юніта. Це чистіше, ніж полювання за PID-ами, і поважає межі сервісу.
Рішення: Використовуйте SIGKILL лише коли ви вирішили, що втрата стану прийнятна і процес не у D state. Якщо він у D state, SIGKILL не спрацює.
Рівень 5: Якщо сам systemd застряг, обережно чистіть cgroup
Іноді юніт «зник», але cgroup залишилася в безладі, або процес втік. Ви можете інспектувати і діяти, але тримайте руки твердо.
cr0x@server:~$ systemctl show nginx.service -p ControlGroup
ControlGroup=/system.slice/nginx.service
cr0x@server:~$ cat /sys/fs/cgroup/system.slice/nginx.service/cgroup.procs
17102
17105
17106
Рішення: Якщо ці PIDs справді належать сервісу, зупиніть юніт та вбийте через systemd. Якщо PIDs у D state — зупиніться тут і виправляйте I/O; вбивство cgroup не допоможе.
Рівень 6: Останній засіб — перезавантаження з наміром, а не з фрустрацією
Якщо ядро застрягло на зберіганні (завислий пристрій, померлий NFS, зламаний multipath шлях) і ви не можете відновитися з userspace, перезавантаження може бути правильним вибором. Але робіть це як інженер:
- Зробіть фейловер трафіку спочатку.
- Зберіть докази:
dmesg, журнал, зразки iostat, стан монтувань. - Повідомте про очікуваний вплив і кроки відновлення.
Жарт №2: Перезавантаження — як вимкнути й увімкнути знову — тільки «знову» включає пояснення change board.
Три корпоративні історії з передової
Інцидент через неправильне припущення: «Перезапуск сервісу не вплине на дані»
Середня компанія мала API платежів на черзі та реляційній базі даних. Ноди API вважалися «безстанними», або принаймні так всі казали. Деплой ввів тонку помилку: воркер-потоки накопичувалися в очікуванні з’єднання до БД, яке ніколи не поверталося. Затримка зросла; кількість помилок теж.
Онкалл побачив завислі потоки і зробив стандартний хід: перезапустив API на одному вузлі. Воно повернулося. Приблизно на три хвилини. Потім знову зависло. Тоді перезапустили більше вузлів. Швидко. Балансувальник трафіку перемістив навантаження на решту вузлів, які одразу наситилися.
Припущення, що їх підвело: «API безстанний». Насправді на кожному вузлі працював локальний агент, який буферизував запити на диск для повторної відправки під час тимчасових збоїв. Під час шторму рестартів spool агента гонявся, повторно надсилав старі запити, а idempotency-ключі в БД були реалізовані непослідовно в різних шляхах коду.
Вони майже не втратили грошей. Але втратили вихідні на звірки і розбір інциденту. Виправлення не було «не перезапускати». Виправлення — чесно змоделювати стан системи: де він зберігається, як відбувається повторна відправка і які компоненти треба злити перед рестартом. Тепер у рунабоці є пункти «відключити локальний retry-агент» і «перевірити застосування ідемпотентності» перед масовим рестартом.
Практичний висновок: ви не можете безпечно перезапустити те, що не задокументували. «Безстанний» — це не настрій; це архітектурне рішення, яке можна довести.
Оптимізація, що зіграла злий жарт: «Коротші таймаути зроблять рестарти швидшими»
Інша організація мала флот хостів з log forwarder та metrics agent. Команда налаштувала systemd-юніти, скоротивши час зупинки під час кочування: TimeoutStopSec=10 скрізь. Це виглядало чітко і ефективно. Але було неправильно.
Одного дня відбулося коротке падіння віддаленого сховища, яке спричинило інтермітентні затримки при записі буферів логів. Форвардеру потрібно було ~20–30 секунд, щоб безпечно злити буфери. Але з 10-секундним таймаутом systemd регулярно ескалював до SIGKILL. Результат не був миттєвим простою; він був гірший: мовчазно втрачені аудиторські логи і напівдоставлені батчі, які ламали downstream-парсинг.
Інцидент важко було помітити, бо «рестарт вважався успішним». Зелені дашборди. Щасливий статус юніта. Тим часом відділ комплаєнсу помітив прогалини, а команда, що обробляє дані, бачила «псевдо-дублікати» подій.
Відкат полягав у поверненні розумних таймаутів і додаванні видимості метрик зливу. Також навчилися диференціювати сервіси: веб-проксі можна вбити швидше, ніж буферизуючий pipeline. Уніфікована «оптимізація» — шлях до уніфікованих помилок в масштабі.
Практичний висновок: таймаути — частина історії цілісності даних. Робіть їх специфічними для сервісу і перевіряйте провалами, а не естетикою.
Нудна, але правильна практика, що врятувала ситуацію: «Дренувати, перевірити, потім рестартувати одну ноду»
SaaS-компанія мала кластер нод за балансувальником та окремий рівень зберігання. Одна нода почала інтермітентно провалювати health checks. Аплікейшн був не мертвий; він «живий, але завислий», повільно відповідав і пропускав watchdog-терміни.
Онкалл виконав нудний рунабук. Спочатку злокував ноду в балансувальнику і підтвердив падіння трафіку до майже нуля. Потім зібрав локальні докази: 5-хвилинне вікно iostat, поточний systemctl status і останні 200 рядків журналу для юніта.
Лише тоді перезапустили сервіс на одній ноді. Він все одно завис при зупинці. Замість сліпої ескалації перевірили стани процесів і знайшли D-state очікування, пов’язані зі старим iSCSI шляхом. Вони від’єднали ноду від сесії зберігання (плановано), вивели її з ротації і дозволили кластеру працювати далі.
Пізніше, у світлий час, виправили налаштування multipath і додали алерти на флапи шляхів. Жодних героїчних перезавантажень. Жодних масових рестартів. Вплив на клієнтів був мінімальний, бо вони розглядали «одну погану ноду» як проблему ізоляції, а не флоту.
Практичний висновок: дренування і канарка на одній ноді здаються повільними, але вони запобігають класичному «я перетворив одну проблему на дванадцять».
Типові помилки: симптом → корінна причина → виправлення
1) Симптом: systemctl stop зависає вічно (або до таймауту)
Корінна причина: Процес у D state очікує на зберігання/мережевий I/O (NFS, iSCSI, мертвий диск), або ExecStop скрипт заблокований на DNS/монтуванні.
Виправлення: Підтвердіть за допомогою ps ... wchan і findmnt. Відновіть залежність (зберігання/мережа/DNS) або зробіть фейловер; не спамте сигнали.
2) Симптом: рестарт «працює», але сервіс відразу стає повільним
Корінна причина: Підкладковий тиск на ресурси (I/O насичення, свап) плюс холодні кеші після рестарту.
Виправлення: Перевірте iostat, free та топ споживачів I/O. Зменшіть навантаження або виправте вузьке місце; рестарт — останній крок.
3) Симптом: systemd каже «active (running)», але сервіс мертвий/невідповідний
Корінна причина: Неправильний тип юніта або сигнали готовності (наприклад, Type=forking з застарілим PID-файлом), або відсутність health check-ів.
Виправлення: Перевірте відстеження PID; віддавайте перевагу Type=notify, коли доступно. Додайте watchdog/health checks або socket-перевірки і оповіщення по ним.
4) Симптом: systemctl start не вдається з «Address already in use»
Корінна причина: Старий процес ще слухає (escaped cgroup, orphan), або плутанина з socket-activated юнітом.
Виправлення: Знайдіть слухачів за допомогою ss -ltnp, підтвердіть членство в cgroup, вбийте правильний процес через systemd або зупиніть socket-юніт, якщо потрібно.
5) Симптом: сервіс постійно перезапускається в циклі
Корінна причина: Restart=always плюс постійна помилка (погана конфігурація, відсутня залежність, проблеми з правами). Цикл може створити навантаження і шторми логів.
Виправлення: Зупиніть юніт, інспектуйте логи, виправте реальну помилку, потім стартуйте. Розгляньте backoff або StartLimitIntervalSec / StartLimitBurst.
6) Симптом: зупинка пройшла, але старт зависає в «activating»
Корінна причина: Readiness-перевірка ніколи не завершується (чекає на notify), або чекає на залежність, що «вгору», але непридатна (DNS відповідає, але повільно; монтування є, але застаріле).
Виправлення: Перевірте systemctl status + властивості юніта (Type=, notify), протестуйте здоров’я залежностей прямими командами (DNS lookup, тест читання/запису монтування).
7) Симптом: вбивання PID нічого не дає
Корінна причина: PID неправильний (застарілий PID-файл), або процес застряг у uninterruptible sleep.
Виправлення: Підтвердіть PID за допомогою systemctl status і systemd-cgls. Якщо D state — розглядайте це як проблему підкладки I/O.
8) Симптом: «Unit is masked» або «Refusing to operate on alias name» під час інциденту
Корінна причина: Хтось замаскував сервіс, щоб він не запускався, або ви таргетите аліас/сімлінк замість реального юніта.
Виправлення: Підтвердіть за допомогою systemctl status і systemctl cat. Розмаскуйте свідомо, задокументуйте, чому його маскували, і запускайте канонічний юніт.
Контрольні списки / покроковий план
Чекліст A: Безпечний перезапуск типовго безстанного сервісу (web/API)
- Підтвердіть зону впливу: Чи цей вузол надлишковий? Якщо ні, призупиніть і сплануйте вікно обслуговування або фейловер.
- Злийте трафік: видаліть ноду з балансувальника; підтвердіть, що активні з’єднання впали.
- Зберіть докази:
systemctl status, останні 100 рядківjournalctl -uі знімокpsпроцесів сервісу. - Спробуйте ввічливу дію: reload, якщо підтримується; інакше stop через systemd.
- Якщо стоп завис: перевірте D state, монтування, I/O тиск. Виправте причину перед примусом.
- Старт і перевірка: юніт активний, порт слухає, health endpoint OK, рівень помилок стабільний.
- Поверніть трафік поступово: додайте ноду назад; спостерігайте за затримкою і насиченням.
Чекліст B: Безпечний перезапуск stateful сервісу (база даних/черга)
- Підтвердіть стан реплікації / HA: знайте primary vs replica, кворум і правила фейловера.
- Призупиніть записи, якщо потрібно: пауза споживачів, зупинка пакетних робіт або увімкнення режиму обслуговування.
- Спочатку перевірте диск простір і I/O: низький простір або висока латентність перетворять «рестарт» на марафон відновлення.
- Використовуйте нативні адмін-команди: наприклад, швидке завершення в базі даних vs жорстке вбивання, щоб уникнути довгого recovery.
- Зупиніть через systemd і спостерігайте: дивіться логи на повідомлення про чекпойнт/злив.
- Якщо завис: не SIGKILL рефлексивно. Визначте, чи відбувається чекпойнт, fsync або очікування на підкладкове сховище.
- Після старту: перевірте сигнали консистентності (реплікація catch-up, WAL replay завершено) і лише тоді дозволяйте записи/споживачів.
Чекліст C: Коли підозрюєте, що проблема в сховищі/мережі
- Шукайте процеси в D state: якщо є — ймовірно проблема нижче userspace.
- Перевірте монтування та віддалені файлові системи: NFS, iSCSI, SMB — підтвердіть здоров’я, латентність і логі помилок.
- Перегляньте повідомлення ядра: таймаути, флапи лінків, SCSI resets, NVMe помилки.
- Ізолюйте хост: злийте трафік і припиніть робити йому гірше.
- Відновіть залежність або зробіть фейловер: відновіть шляхи, перезапустіть служби монтування або перемістіть робоче навантаження.
- Перезавантажуйте лише з планом: після збору доказів і з підтвердженим фейловером.
FAQ
1) Чому б просто не перезавантажити? Це швидше.
Іноді це швидше для вас, але не для системи. Перезавантаження скидає все, може спричинити довгі відновлення (fsck, RAID resync, replay бази даних) і знищує докази. Перезавантажуйте, коли ви ідентифікували wedge на рівні ядра або вже зробили фейловер і хочете чистий стан.
2) Що означає «D state» і чому це важливо?
D state — uninterruptible sleep: процес чекає в ядрі переважно на I/O. Сигнали не будуть опрацьовані, поки системний виклик не завершиться. Якщо процес у D state, вбивання не спрацює; треба виправити підкладку I/O або перезавантажити хост.
3) Чи варто використовувати killall чи pkill під час інциденту?
Зазвичай ні. Це грубі інструменти і вони люблять побічні втрати. Віддавайте перевагу systemctl kill для конкретного юніта, щоб вражати правильну cgroup. Якщо мусите використовувати pkill, дуже вузько обмежуйте його та перевіряйте за допомогою ps і systemd-cgls.
4) Чому systemd каже, що він вбив процес, а він досі є?
Якщо процес у D state, він може залишатися, бо не може завершити системний виклик і вийти. Інша можливість: ви дивитесь на інший PID (застарілий PID-файл) або дочірній процес поза cgroup юніта.
5) Чи варто знижувати TimeoutStopSec, щоб уникнути зависань?
Не як універсальна політика. Для буферизуючих pipeline і stateful сервісів короткі таймаути перетворюють ввічливе завершення на втрату даних. Налаштовуйте таймаути для кожного сервісу на основі вимірів часу завершення під навантаженням і в умовах деградації.
6) Коли дозволено використовувати SIGKILL?
Коли ви визначили, що збереження стану менш важливе, ніж відновлення доступності, і ви розумієте вартість відновлення. Це також прийнятно для справді безстанних сервісів, що просто грубі. Це не вирішення для D-state зависань.
7) У чому різниця між restart і try-restart?
restart запускає сервіс навіть якщо він не працює. try-restart перезапускає тільки якщо сервіс уже запущений. У продакшені try-restart безпечніший, коли ви не хочете випадково запустити те, що було навмисно зупинено.
8) Як уникнути каскадних збоїв при перезапуску залежностей?
Знайте граф залежностей. Дренуйте або зупиняйте залежні сервіси перед рестартом критичної залежності (база даних, черга, DNS, сховище). Поверніть залежності першими, потім залежні сервіси малими партіями, спостерігаючи за помилками і насиченням.
9) systemd показує «activating» вічно. Яка звичайна причина?
Сигнал готовності. Сервіс може чекати на подію notify, PID-файл або ExecStartPost, що викликає мережевий запит. Перегляньте тип юніта та ExecStartPost, логи і таймаути.
10) Як зберегти докази, якщо треба діяти швидко?
Заберіть мінімально необхідні докази перед застосуванням сили: стан юніта, останні ~200 рядків журналу, знімок стану процесів і короткий I/O сніппет. Цього достатньо для діагностики більшості причин «завислого стопу» пізніше.
Наступні кроки, які ви реально можете зробити завтра
Безпечні перезапуски — не про обережність; вони про точність. Коли сервіс зависає, ваше перше завдання — класифікувати: це зависання застосунку, зависання залежності чи зависання ядра/зберігання? Лише одну з цих проблем вирішує «жорсткіший рестарт».
Зробіть наступне:
- Напишіть сходи ескалації для ваших топ-5 сервісів: reload vs restart, кроки дренування і коли дозволено SIGKILL.
- Заміряйте час завершення під нормальним навантаженням та під легкою деградацією, потім встановіть
TimeoutStopSecвідповідно. - Додайте алерт на D state: рахуйте процеси в D state і пейджте при спайку. Це раннє попередження, що ваше сховище/мережа бреше вам.
- Інвентаризуйте стан: визначте, які «безстанні» сервіси насправді зберігають, буферизують або локально повторюють.
- Потренуйтеся на одній ноді у staging-подібному середовищі: змусьте зависле монтування, спостерігайте поведінку stop і оновіть рунабук з отриманими знаннями.
Мета — не ніколи не перезавантажувати. Мета — перезавантажувати лише тоді, коли ви розумієте і хочете ту зону ураження.