Курсор замер, индикатор NumLock больше не переключается, новый сеанс ssh не проходит дальше этапа подключения, а на мониторе застыл последний кадр. Кнопка сброса под рукой, но жёсткий сброс одновременно ломает целостность файловых систем и стирает лучшие улики: в оперативной памяти в этот момент хранится точный снимок того, кто занял процессор и где именно он застрял. Сочетания Magic SysRq принимает само ядро, они доступны даже тогда, когда планировщик перестал отвечать пользовательским процессам. Реальный порядок действий выглядит так: заранее включить служебные клавиши, при зависании снять дампы задач, уйти в управляемую перезагрузку и разобрать в journalctl следы предыдущей загрузки.
Чем полное зависание отличается от медленной но живой работы
Высокая нагрузка и зависание на глаз неотличимы, а разделяются за минуту. У задыхающегося, но живого сервера консоль отвечает с задержкой, ssh в итоге пускает, переключение на вторую текстовую консоль срабатывает. У зависшей системы не отвечает никто: индикатор клавиатуры игнорирует нажатия, экран показывает застывшую картину, установленный сеанс ssh обрывается посреди команды, а новый молчит до таймаута.
Быстрый тест с другой машины уточняет сценарий. Ping продолжает отвечать, потому что ICMP обрабатывается сетевой подсистемой ядра в контексте обработки прерываний, без участия планировщика. Порт 22 молчит, когда процессор занят ядром или очередь задач не двигается: принять соединение должен процесс sshd, а ему не достаётся процессорного времени. Сочетание живого пинга и молчащего порта означает, что машина не выключена, а именно зависла, и внутри неё ещё можно побывать.
Перед жёстким сбросом у администратора есть окно для сбора улик, и в ядре для этого существует механизм Magic SysRq. Он не зависит от здоровья пользовательской части и отзывается даже на полностью неработоспособной системе. Чем точнее дамп, тем короче путь до виновника, тем меньше шансов, что инцидент повторится следующей ночью.
Включение Magic SysRq и расшифровка служебных клавиш ядра
Механизм активируется сочетанием Alt и PrintScreen с третьей клавишей-буквой: PrintScreen это и есть SysRq, на ноутбуках иногда нужен ещё Fn. Проверить, разрешён ли механизм, можно так:
cat /proc/sys/kernel/sysrq
Ноль отключает всё, единица разрешает любые команды, промежуточные значения работают как битовая маска: 16 разрешает аварийный сброс буферов, 32 перемонтирование в состояние только чтение, 64 сигналы процессам, 128 перезагрузку и выключение. В Ubuntu по умолчанию стоит маска 176, в которую входят перезагрузка, перемонтирование и сброс буферов, но не дампы. Для диагностики удобнее открыть всё:
sudo sysctl -w kernel.sysrq=1
Чтобы настройка пережила перезагрузку, она прописывается строкой kernel.sysrq = 1 в файле /etc/sysctl.d/99-magic-sysrq.conf. Дальше сами клавиши. Для диагностики важны три: t выводит полный список задач с их состояниями, w выводит заблокированные задачи со стеками ядра, m печатает сводку по памяти. Аварийный набор: e отправляет всем процессам кроме init сигнал SIGTERM, i делает то же сигналом SIGKILL, f вызывает oom killer для самого прожорливого процесса, s сбрасывает изменённые буферы файловых систем, u перемонтирует их в состояние только чтение, b перезагружает машину немедленно.
На серийной консоли клавиши посылаются иначе: сначала сигнал BREAK, затем буква. Сервер с настроенной консолью ttyS0 принимает Magic SysRq даже без клавиатуры, что делает серийный вход самым надёжным способом дотянуться до зависшей машины.
Возникает и резонный вопрос: почему бы не держать механизм выключенным, как делают дистрибутивы по умолчанию. Ответ в балансе. Клавиши срабатывают из любой консоли, без пароля и прав, и случайное нажатие способно перезагрузить машину. На сервере с ограниченным физическим доступом риск минимален, а польза от управляемого завершения без живого сеанса перевешивает любые опасения.
Экстренная перезагрузка через REISUB без потери файловых систем
Когда окно для диагностики закрыто и системе всё равно чем закончить, применяется каноническая последовательность из шести клавиш, известная по слову REISUB. Расшифровка выглядит так:
- R снимает клавиатуру из raw режима и возвращает терминалы под управление ядра;
- E отправляет всем процессам кроме init сигнал SIGTERM с шансом на аккуратное завершение;
- I отправляет всем процессам кроме init сигнал SIGKILL и снимает даже зависшие задачи;
- S сбрасывает все изменённые буферы файловых систем на диски;
- U перемонтирует все смонтированные файловые системы в состояние только чтение;
- B выполняет немедленную перезагрузку без остановки служб.
Между клавишами делается пауза в несколько секунд, а перед S выдерживается до минуты: сброс буферов на перегруженном вводе-выводе занимает время. Частая ошибка это скорость: клавиши прожимаются подряд, и сброс не успевает завершиться до перезагрузки. Пауза часть протокола, а не пожелание. Серийная консоль добавляет свою особенность: BREAK отправляется отдельным сигналом, и терминальная программа должна уметь его посылать.
Итог последовательности стоит понимать правильно: перезагрузка происходит принудительно, но файловые системы после неё чистые, журналы ext4 закрыты корректно, и после старта не запускается длинная проверка e2fsck. Перед началом последовательности стоит успеть снять дампы, о них ниже.
Снятие дампов задач клавишами t и w перед уходом в перезагрузку
Дамп задач это самое ценное, что можно вытащить из зависшей машины. Alt+SysRq+t вываливает полный список процессов с состояниями, Alt+SysRq+w выводит только задачи в непрерываемом состоянии D вместе со стеками ядра. Стек показывает, в какой функции ядро застряло: драйвер хранения, сетевой модуль, файловая система. Одна строка стека часто заменяет сутки гаданий.
Дампы попадают в кольцевой буфер printk, откуда их собирает journald. Попадут ли они на экран, зависит от console_loglevel: первое число в файле /proc/sys/kernel/printk задаёт, какие сообщения достойны консоли, а в журнал пишется всё независимо от неё. Ограничение честно проговаривается заранее: при жёсткой перезагрузке хвост сообщений, не долетевший до диска, теряется вместе с оперативной памятью. Для систем, где зависания повторяются, сообщения ядра дублируются наружу модулем netconsole на приёмную машину в реальном времени:
sudo modprobe netconsole netconsole=Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. /eth0,514@192.0.2.5/12:34:56:78:9a:bc
Если хотя бы один сеанс ssh ещё принимает команды, тот же дамп запускается записью буквы в служебный файл ядра:
echo t | sudo tee /proc/sysrq-trigger
Оба способа равнозначны по эффекту: в журнале появляется таблица задач на момент зависания. Разница в том, что файл sysrq-trigger работает без клавиатуры и из любого живого сеанса, а клавиши работают даже там, где пользовательская часть уже не отвечает.
Полезно снять и сводку по памяти: Alt+SysRq+m печатает страницы по состояниям, включая изменённые и ожидающие записи. Если свободной памяти на нуле, а изменённых страниц миллионы, картина трешинга подтверждается ещё до разбора журнала, и поиск сужается до потребителя памяти.
Анализ предыдущей загрузки через journalctl от lockup до oom killer
После управляемой перезагрузки начинается разбор. Хвост журнала предыдущей загрузки открывается командой с флагом минус один:
journalctl -b -1 -e
Когда загрузок много и индекс минус один сбивает с толку, нумерация открывается командой journalctl --list-boots: она печатает список загрузок с границами по времени, и по нему выбирается нужный индекс для флага -b.
Ошибки и предупреждения ядра прошлой загрузки собираются одной строкой:
journalctl -k -b -1 -p warning --no-pager | grep -iE "soft lockup|hung|oom|stall|nobody cared"
Что искать в выводе. Строка BUG: soft lockup с именем задачи означает, что процессор надолго закрутился в цикле ядра, обычно с выключенными прерываниями; порог мягкого детектора по умолчанию чуть больше двадцати секунд. Запись INFO: task blocked for more than 120 seconds приходит от детектора зависших задач, и 120 это значение по умолчанию параметра kernel.hung_task_timeout_secs. Сигнатура Out of memory: Killed process вместе со строкой invoked oom-killer говорит, что памяти не было вовсе, и первым обычно снимается самый крупный процесс. Строки rcu detected stalls указывают на перегруженный планировщик. Сообщение irq N: nobody cared выдаёт шторм прерываний от неисправного устройства.
Метки времени в этих записях указывают момент заклинивания, и они сверяются с графиками мониторинга: всплеск загрузки часто виден за минуты до точки невозврата. Для служб журнал фильтруется по юниту, например journalctl -b -1 -u ssh покажет, что писала служба перед молчанием. Метка _SYSTEMD_UNIT в записях позволяет найти, кто из служб генерировал поток сообщений перед зависанием, и это часто отвечает на вопрос о виновнике ещё до анализа стеков.
Типичные виновники стопроцентной загрузки и их следы в журнале
Если журнал полон записей oom-killer, а нагрузка росла стремительно, причина в памяти: ядро тратит почти всё время на переливание страниц между оперативной памятью и диском, и процессор заметно нагружает kswapd. Лечится лимитами на службы и контейнеры, а первые симптомы видны заранее в выводе vmstat по колонкам si и so, которые в здоровой системе держатся у нуля.
Строка soft lockup почти всегда указывает на конкретный модуль ядра: сеть, хранение, графический драйвер. После обновления ядра или прошивки устройства такие зависания нередко уходят сами. Номер версии на момент инцидента подскажет uname -r, а сравнение с текущей версией покажет, менялось ли что-то в подсистеме.
В облаке у стопроцентной загрузки бывает не только локальный виновник. Гипервизор ничего не пишет в журнал гостевой системы, но отбираемое время видно в top как столбец st: высокий steal означает, что соседние машины на том же физическом узле забрали процессор. Гостю в этом случае помогает только расширение лимита процессорных долей или переезд на другой узел.
Отдельная категория это задачи в состоянии D, когда след клавиши w показывает десятки процессов, застрявших в подсистеме ввода-вывода. Здесь виноват не процессор, а накопитель: зависшие запросы блокируют всё, что пишет, а вместе с ним и сетевые службы, которым нужна запись. Сочетание живого пинга и молчащего порта 22 при высоком load average чаще всего объясняется именно так.
Контейнер или служба без лимитов занимает весь узел целиком, и принадлежность видна в метках юнита у сообщений журнала. Лечится лимитами в cgroup: у systemd это CPUQuota и MemoryMax в юните, у контейнерных сред соответствующие флаги запуска. Лимит, выставленный после инцидента, проверяется под нагрузкой заранее, а не в следующую ночь.
Профилактика зависаний и настройки на будущее
Журнал переводится в постоянный режим строкой Storage=persistent в /etc/systemd/journald.conf, иначе после перезагрузки видна только текущая загрузка. Служебные клавиши закрепляются в sysctl, netconsole прописывается в автозагрузку через каталоги /etc/modules-load.d и /etc/modprobe.d, чтобы работали с первого старта системы.
Для серверов, где простой недопустим, ядро настраивается на остановку при зависании: kernel.softlockup_panic = 1 и kernel.hung_task_panic = 1 переводят зависание в управляемый сброс, а связка с kexec оставляет после себя полный дамп памяти для разбора. Аппаратный watchdog включается в systemd строкой RuntimeWatchdogSec = 30 в /etc/systemd/system.conf: если система перестаёт подавать признаки жизни, контроллер перезагружает машину сам, без участия человека.
Мониторинг прощает медленное нарастание нагрузки, но не резкие скачки: алерты на load average, память и очередь ввода-вывода с порогами заранее отсекают большинство сценариев, которые заканчиваются жёстким сбросом.
Сама последовательность REISUB стоит хоть раз прогнать на тестовой машине: в реальном инциденте руки должны знать порядок без подсказок. Там же проверяется, работает ли BREAK на серийной консоли и ловит ли приёмная сторона поток netconsole. Тренировка на стенде снимает половину вопросов, которые обычно возникают в три часа ночи.
Зависание это всегда чей-то след, и почти всегда этот след лежит в журнале. Задача инженера проста: доехать до него, не стерев улики по дороге.