Сервер завис: консоль не отвечает, SSH молчит, мониторинг показывает процессор на ста процентах. Это одна из самых неприятных ситуаций дежурства, потому что стандартные инструменты диагностики - те самые, что и зависли. К счастью, у Linux есть два штатных механизма для именно таких случаев: Magic SysRq - аварийный канал управления ядром в обход зависшей оболочки, и постоянный журнал systemd - окно в прошлую загрузку для послесмертного разбора. В этой статье полный маршрут: как безопасно реанимировать зависший сервер, не потеряв улики, и как по журналу предыдущей загрузки найти виновника.
Симптомы настоящего зависания
Сначала полезно отличить настоящий клин от локальной неприятности. Настоящее зависание выглядит так: SSH не открывает новые сессии, существующие сессии не отвечают на ввод, ping при этом проходит, потому что сетевой стек ядра жив. Иногда отвечает консоль, иногда нет - зависит от того, кто именно съел процессор: если все ядра заняты задачей с высоким приоритетом, даже переключение в SysRq-консоль может тормозить.
Вторая категория - псевдозависание: интерактив тормозит, но новые SSH-сессии всё же открываются со скрипом. Это значит, что часть ресурсов ещё жива, и в этом случае не спешите перезагружать: по живой системе успевают сработать top, ps aux --sort=-%cpu и perf top, которые снимут фамилию виновника за секунды. Настоящий клин оставляет только два пути - Magic SysRq и посмертный разбор журнала.
Magic SysRq как кислородный клапан
Magic SysRq - это сочетания клавиш, обрабатываемые самим ядром в обход всего пользовательского окружения: они работают, когда не работает больше ничего. По умолчанию механизм может быть отключён, и первое, что проверяется на любом сервере заранее, - файл /proc/sys/kernel/sysrq. Значение 1 включает все команды, 0 отключает всё, значения по маске включают подмножества. Забота об этом параметре - часть подготовки к аварии, а не часть разборов на пепелище.
На физической консоли команды вводятся сочетанием Alt+PrintScreen+буква. По сети из SSH работает echo буква > /proc/sysrq-trigger - тот же канал, но текстом. Ключевые буквы стоит знать наизусть: w печатает список задач в непрерывном состоянии; t печатает стеки всех задач; e отправляет всем процессам SIGTERM; i - SIGKILL; s сбрасывает дисковые буферы на диск; u перемонтирует всё только для чтения; b перезагружает систему немедленно.
Порядок букв не случаен, и в следующем разделе он превращается в протокол.
Порядок действий при зависании через SysRq
Классическая мнемоника REISUB - последовательность R, E, I, S, U, B - делает восстановление управляемым. R забирает у иксов и консоли контроль над клавиатурой; E аккуратно завершает все процессы сигналом TERM, давая шанс на сохранение данных; I добивает упрямых сигналом KILL; S синхронизирует дисковые буферы, чтобы файловые системы остались согласованными; U перемонтирует корень в режим только для чтения; B перезагружает. Между E и I выдерживается пауза в несколько секунд: многие процессы завершаются по первому сигналу, и принуждение оказывается не нужным.
Пропускать S и U не стоит, даже когда хочется перезагрузить побыстрее: именно эти две буквы сохраняют файловые системы от повреждения при жёстком рестарте. А перед самым началом последовательности, если консоль хоть как-то реагирует, выполняются две команды-улики: echo w > /proc/sysrq-trigger и echo t > /proc/sysrq-trigger, чей вывод попадает в системный журнал и даст материал для разбора после перезагрузки.
Анализ прошлой загрузки через journalctl --boot=-1
После перезагрузки начинается следствие, и главный его инструмент - журнал предыдущей загрузки. Предпосылка - постоянное хранение журнала: в /etc/systemd/journald.conf параметр Storage=persistent, иначе журнал прошлой загрузки просто не сохранился. Настроенные заранее серверы отвечают на journalctl --list-boots списком загрузок с номерами, и -1 - последняя завершившаяся, то есть зависшая.
Разбор движется от общего к частному. Сначала journalctl --boot=-1 -p err - последние ошибки перед зависанием. Затем journalctl --boot=-1 -n 200 - последние события: часто видно, что система ещё что-то писала, а потом наступила тишина. Полезный приём - journalctl --boot=-1 -k -n 100: сообщения ядра в конце загрузки рассказывают о состоянии памяти, дисков и планировщика в момент гибели.
Именно SysRq-улики w и t из прошлого раздела всплывают в этом журнале: список задач с пометкой D (непрерываемое состояние) и стеки, застрявшие в конкретной функции ядра, - это готовое указание на виновника. Задачи в состоянии D - следы ожидания ввода-вывода или блокировки в ядре, и их стеки через журнал читаются напрямую.
Типичные виновники ста процентов
Практика разборов сводит виновников в устойчивый список. Первое место - бесконечный цикл в приложении, особенно в обработчике ошибок, который сам и порождает следующую ошибку. Второе - регрессия производительности после релиза: новый запрос к базе, разворачивающий миллион строк. Третье - сборщик мусора нагруженной JVM, крутящий циклы без прогресса. Четвёртое - зависший драйвер или FUSE-файловая система, которая держит задачи в состоянии D и не отпускает никогда.
Каждый тип оставляет характерный след в журнале: цикл - одну и ту же строку, повторяющуюся тысячи раз; регрессия - медленно растущие тайминги запросов; сборщик мусора - чередование пауз; драйвер - задачи в D со стеком в одной функции. Навык чтения этих следов накапливается за несколько разборов и потом служит годами.
Профилактика следующего зависания
Зрелая инфраструктура не ждёт второго зависания. Постоянный журнал включается на всех серверах - это одна строка конфигурации. На критичных машинах настраивается watchdog: параметр ядра softdog с watchdogd или встроенные механизмы systemd перезагружают зависший сервер автоматически, если сердцебиение прекращается. Лимиты CPU через cgroups или systemd-юниты с CPUQuota удерживают отдельно взятый сервис от захвата всей машины - и тогда зависание приложения превращается из аварии в инцидент на одном сервисе.
И последний штрих: посмертный разбор каждого случая заканчивается записью в runbook - одна строка о виновнике и одна о мере против повторения. Через год таких записей набирается на статистику, которая сама подсказывает, где инфраструктура слабеет чаще всего.
Памятка дежурного
Короткий порядок всей статьи: проверить, жив ли SSH хоть частично; если жив - снять ps и perf top; если мёртв - SysRq-улики w и t; последовательность REISUB с паузами; после старта journalctl --boot=-1 по следам; виновника - в runbook. От клика до перезагрузки проходит меньше минуты, разбор занимает вечер, и на выходе появляется не просто перезагруженный сервер, а понимание, что именно его уложило.
Грани регулярной практики
SysRq - инструмент аварийный, и потому его место не в повседневной работе, а в подготовке к ней. Проверка /proc/sys/kernel/sysrq на новых серверах входит в первые шаги настройки; значение 1 ставится там, где физическая или IPMI-консоль доступна, и сознательно не ставится на машинах с высокими требованиями к защите от физического доступа. Параметр kernel.sysrq из sysctl закрепляет выбор в конфигурации, чтобы настройка переживала перезагрузки.
Отдельная проверка - тест канала на стенде: один раз отправить echo s > /proc/sysrq-trigger и убедиться, что консоль реагирует. Это занимает минуту, а в момент аварии вы будете знать, что канал работает, а не предполагать это.
Watchdog как автопилот для автоперезагрузки
Там, где сервер должен возвращаться в строй без человека, настраивается аппаратный или программный watchdog: ядро и watchdog-демон пожимают друг другу руки раз в несколько секунд, и когда система зависает так, что рука не подаётся, таймер истекает и железо перезагружается. Это не лечение - это автоматическая этажерка того же REISUB, только без участия дежурного. Для критичных сервисов связка watchdog + постоянный журнал + systemctl --failed при старте даёт полностью закрытый цикл: сервер возвращается сам и оставляет улики сам.
Улики при живой системе
Если система отвечает хоть частично, минуты драгоценны. Порядок съёма: ps aux --sort=-%cpu | head -20 - пятёрка прожорливых; pidstat 1 5 - динамика, а не мгновенный срез; perf top - что именно внутри процессов жрёт; cat /proc/pressure/cpu - насколько ядро само страдает от нехватки ресурса. Вывод сразу сохраняется в файл за пределами зависающей машины - по сети или на флешку, потому что данные, оставшиеся на зависшем сервере, до перезагрузки не доживут.
И тонкий приём для удалённой диагностики: если SSH не открывает новые сессии, но старая сессия висит, шанс остаётся - иногда спасает сессия tmux, работающая через уже открытое соединение. Именно поэтому привычка держать tmux на каждом сервере не про удобство, а про выживание.
История из практики
Характерный случай: файловый сервер со 100% CPU по тревоге в три часа ночи. SSH не открылся, но старая сессия дежурного, оставленная вечером, отозвалась. Быстрый ps показал процесс rsync в состоянии R, съевший все ядра; pidstat подтвердил стационарность нагрузки; culprit - скрипт зеркалирования, ушедший в цикл из-за гонки состояний. Убийство процесса вернуло сервер за минуту без перезагрузки, а журнал той загрузки зафиксировал всю историю для утреннего разбора. Вывод в runbook звучал коротко: перед выкаткой синхронизации - проверять блокировки, перед дежурством - оставлять сессию.
Короткая шпаргалка клавиш
Для печати на стену достаточно шести строк: w - задачи в непрерываемом состоянии; t - стеки всех задач; e - завершение всех процессов сигналом TERM; i - то же сигналом KILL; s - сброс буферов на диск; u - перемонтирование в только чтение; b - немедленная перезагрузка. Памятка из семи букв, которая в среду вечером висит на стене, в четверг утром экономит сервер.
Культура разбора после аварии
Финальный акцент статьи - культурный. Разбор зависания заканчивается не перезагрузкой, а записью: виновник, признак, мера. Команда, которая ведёт такие записи, через год знает свои слабые места по статистике, а не по ощущениям. Перезагрузка без разбора - потерянный шанс: авария уже оплатила себя, и было бы расточительно не извлечь из неё урок. Постоянный журнал, SysRq-улики и пятнадцать минут внимания - вот и вся разница между просто перезагруженным сервером и укреплённой инфраструктурой.
Связка инструментов как единый протокол
Со временем SysRq, журнал и watchdog перестают быть отдельными приёмами и складываются в единый протокол реакции на зависание, который знает вся команда: улики до перезагрузки, корректная последовательность REISUB, разбор по journalctl --boot=-1 утром, запись в runbook и мера против повторения. Такой протокол превращает самую страшную категорию ночных алертов в отработанный процесс с предсказуемым финалом - и в этом смысле вся статья держится не на конкретных командах, а на культуре, которую они создают.
Что стоит распечатать и повесить на стену
Опыт нескольких поколений дежурных сходится на простой рекомендации: распечатайте шпаргалку из семи букв SysRq и порядок уличающих команд, и приклейте там, где случаются ночные смены. Стены серверных существуют не для сертификатов и дипломов - они для памяток, которые спасают инфраструктуру в моменты, когда Интернет недоступен, память подводит, а счёт идёт на минуты. Каждая авария, разрешённая по бумажке на стене, окупает всю типографскую краску в этом здании.
И это же правило объясняет, зачем вообще писать такие статьи: они суть та же настенная памятка, только в цифровом виде - с местом в закладках браузера на случай, если стены под рукой не окажется.
Пусть ваши серверы висят редко, а REISUB остаётся экзотикой из учебников - но знание этой экзотики и отличает инженера, который управляет инцидентом, от инженера, которого ведёт за руку случай.