сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Linux и bash

Диагностика в Linux: ss, strace, lsof

Диагностика: чем смотреть

Этот блок ближе всего к живой работе: вам описывают симптом и ждут порядок действий с конкретными командами. Ответ «посмотрю логи» не проходит.

Стержень: инструмент выбирается по симптому - занят порт смотрят по сокетам, зависший без нагрузки процесс по системным вызовам, исчезнувшее место по дескрипторам.

// Формулировки: «кто занял порт?», «процесс не отвечает, процессор ноль», «после ротации логи пишутся в никуда»

Кто занял порт и кто держит файл

Вопрос «кто слушает порт» решается одной командой, показывающей слушающие сокеты вместе с процессом-владельцем. Сокет - это конец сетевого соединения со стороны программы; слушающий сокет и есть открытый порт, на котором сервис ждёт входящих. Вот кусок настоящего вывода с моего сервера: строки с адресом 0.0.0.0:443 и 0.0.0.0:22 - это службы, доступные снаружи, а строка 127.0.0.53%lo:53 - служба, слушающая только на самой машине.

И вот тут прячется половина случаев «снаружи не подключается». Адрес 0.0.0.0 означает «на всех сетевых интерфейсах», а 127.0.0.1 - «только внутри этой машины». Если сервис слушает второй адрес, снаружи к нему не подключиться никогда, и правка правил межсетевого экрана не поможет: чинить надо настройку самого сервиса.

// Похожая команда отвечает на вопрос «кто держит файл»: она же находит удалённые файлы, из-за которых не освобождается место, - у них в списке открытых файлов процесса стоит пометка об удалении. Если владельца порта найти не удалось вовсе, порт может держать не процесс, а остаток закрытого соединения, который система придерживает пару минут.

адрес прослушивания
0.0.0.0 - отовсюду, 127.0.0.1 - только с этой машины
владелец сокета
процесс, который держит порт; ищется одной командой, а не перебором

Процесс висит, а процессор ноль

Нулевая нагрузка на процессор говорит важную вещь: процесс не крутится, а ждёт. Значит, вопрос не «что он считает», а «чего он ждёт», и отвечает на него последний системный вызов - обращение процесса к ядру.

Инструмент для этого - трассировка системных вызовов живого процесса: она покажет, стоит ли он на чтении из сокета, на блокировке или на записи. Второй источник - файл в системной папке процесса, где ядро пишет имя функции, в которой заснул поток.

// А профилировщик тут не поможет: он ищет горячий код, а горячего кода нет вовсе, процесс спит. Это частая ошибка - взять инструмент под другой симптом. Дальше действия ветвятся: если ждёт сокет, проверяю соединение и таймауты второй стороны; если ждёт блокировку, ищу, кто её держит, и нет ли взаимной блокировки; если диск, смотрю статистику устройств и системные сообщения.

трассировка системных вызовов
показывает, на каком обращении к ядру стоит живой процесс
профилировщик не для ожидания
он ищет горячий код; у спящего процесса его нет

Ротация логов: куда уходят строки

Замер, объясняющий формулировку «после ротации логи пишутся в никуда». Открыл файл на запись, записал строку - номер файла 4484922. Дальше сделал то, что по умолчанию делает штатная программа ротации: переименовал файл и создал на его месте новый пустой (номер 4484923). Записал вторую строку и посмотрел, где она оказалась.

В новом файле - пусто. Обе строки оказались в переименованном. Причина в том, что открытый дескриптор привязан к самому файлу, а не к имени: переименование для процесса вообще ничего не значит, он продолжает писать туда же, куда писал.

// Отсюда два рабочих варианта в настройке ротации. Первый - копировать содержимое и обрезать файл на месте, не создавая новый: дескриптор остаётся валидным. Минус - между копированием и обрезкой можно потерять строки. Второй - после ротации послать сервису сигнал переоткрыть лог: так делает, например, nginx. И проверять надо не настройку, а факт: после ротации место должно освободиться, а новый файл - расти.

переименование не меняет дескриптор
процесс пишет в тот же файл, как бы тот ни назывался
два способа ротации
копировать и обрезать на месте либо сигналом заставить переоткрыть лог

Как отвечать: «Сервис не отвечает, процессор ноль. С чего начнёшь?»

Раз процессор ноль, процесс не работает, а ждёт - значит, интересен последний системный вызов, то есть последнее обращение к ядру. Ставлю трассировку системных вызовов на этот процесс и смотрю файл, где ядро пишет функцию, в которой заснул поток: обычно сразу видно, стоит ли он на чтении из сокета, на блокировке или на вводе-выводе. Дальше ветвлюсь по картине. Если сеть - проверяю соединение, отвечает ли вторая сторона, какие у клиента таймауты. Если блокировка - ищу, кто её держит, и нет ли взаимной блокировки двух потоков. Если диск - смотрю статистику по устройствам и системные сообщения ядра, там обычно видно проблемное устройство. Профилировщик в такой ситуации бесполезен: он ищет горячий код, а его нет, процесс спит. И параллельно проверяю банальное - на каком адресе сервис слушает: если он поднялся только на локальном адресе, снаружи к нему не подключится никто, сколько ни правь межсетевой экран.

Ответ показывает выбор инструмента под симптом и ветвление по результату, а не заученный список утилит.

На чём валятся

  • Ищут владельца порта перебором процессов вместо команды по сокетам.
  • Ставят профилировщик на процесс, который простаивает в ожидании: горячего кода там нет.
  • Настраивают ротацию и не проверяют факт - обе строки ушли в переименованный файл, новый остался пустым.
  • Забывают посмотреть адрес прослушивания и чинят межсетевой экран вместо настройки сервиса.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 6, остальные разбираются в тренажёре.

  1. #dvo_linux_debug1 / 5
    Процесс завис: CPU 0%, ответов нет, логи молчат. Чем понять, где именно он стоит?
    A)perf top по этому PID
    B)top в режиме потоков (клавиша H)
    C)strace -p PID и /proc/PID/wchan
    D)kill -QUIT и разбор дампа потоков
    показать ответ и разбор
    +C)strace -p PID и /proc/PID/wchan

    // разбор: Нулевой CPU означает, что процесс не крутится, а ждёт, — значит, интересен последний системный вызов. strace -p покажет, на чём он стоит (read из сокета, futex, flock), /proc/PID/wchan — функцию ядра, в которой заснул поток. Профилировщики ищут горячий код и при простое показывают пустоту.

  2. #dvo_linux_debug2 / 5
    После ротации логов сервис пишет в никуда, и место на диске не освободилось. Почему?
    A)logrotate переименовал файл, а процесс держит дескриптор на старый inode
    B)logrotate сжал файл, и процесс дописывает в архив
    C)На новый файл не хватило прав, запись отвергается
    D)Файл открыт в режиме дописывания, а после ротации нужен режим перезаписи
    показать ответ и разбор
    +A)logrotate переименовал файл, а процесс держит дескриптор на старый inode

    // разбор: Приложение держит открытым дескриптор на inode, а не на имя. logrotate по умолчанию переименовывает файл и создаёт новый — процесс продолжает писать в старый inode: в архив либо в удалённый файл, чьи блоки не освободятся, пока открыт fd. Поэтому в конфиге ставят copytruncate (копия и обрезка на месте) или postrotate с сигналом приложению переоткрыть лог, как USR1 у nginx.

  3. #dvo_linux_debug3 / 5
    Сервис под нагрузкой начал сыпать «Too many open files». Куда смотреть первым?
    A)На разделе кончились inode, из-за этого и не открываются файлы
    B)Это утечка памяти под нагрузкой, надо смотреть рост RSS
    C)В лимит дескрипторов: ulimit -n, /proc/PID/limits, LimitNOFILE юнита
    D)Кончились сетевые порты, надо расширить ephemeral-диапазон
    показать ответ и разбор
    +C)В лимит дескрипторов: ulimit -n, /proc/PID/limits, LimitNOFILE юнита

    // разбор: «Too many open files» — процесс упёрся в лимит открытых дескрипторов (RLIMIT_NOFILE), а это и файлы, и сокеты, и pipes. Проверяют текущий лимит (ulimit -n или /proc/PID/limits), сколько открыто (ls /proc/PID/fd | wc -l, lsof) и нет ли утечки (fd растёт и не закрывается). Поднимают лимит: для сервиса systemd — LimitNOFILE= в юните, а не ulimit из интерактивного шелла (демона он не касается).

  4. #dvo_linux_debug4 / 5
    Процесс периодически кто-то убивает: он просто исчезает без паники в своих логах. Как узнать, кто и почему?
    A)В dmesg / journalctl -k — там ядро пишет про OOM и сигналы
    B)Повесить strace на этот процесс уже после того, как он умер
    C)Смотреть только в лог самого приложения, там будет причина
    D)Если приложение ничего не записало, копать причину уже негде
    показать ответ и разбор
    +A)В dmesg / journalctl -k — там ядро пишет про OOM и сигналы

    // разбор: Когда процесс убивают SIGKILL (в т.ч. OOM-killer), сам он ничего не логирует — сигнал не перехватывается. Но ядро оставляет запись: dmesg -T или journalctl -k покажут «Out of memory: Killed process PID (name)» с деталями (сколько памяти, из какой cgroup). Если это не OOM, а внешний kill — помогает auditd (правило на kill). Начинают всегда с dmesg.

  5. #dvo_linux_debug5 / 5
    Приложение под нагрузкой упирается в потолок; профилировать в отладчике на проде нельзя. Чем найти горячий код с минимальным оверхедом?
    A)Повесить strace на весь процесс и крутить его в цикле под нагрузкой
    B)Обвесить подозрительные места print-ами и разбирать по логам
    C)Подключить gdb и пройтись степпингом по инструкциям на проде
    D)perf (perf top / record + flame graph) — семплирует по счётчикам CPU
    показать ответ и разбор
    +D)perf (perf top / record + flame graph) — семплирует по счётчикам CPU

    // разбор: На проде нужен низкооверхедный семплирующий профайлер. perf собирает стеки по таймеру/PMU-счётчикам (perf record -g, perf top), из чего строят flame graph — сразу видно, где процессор проводит время. strace, наоборот, перехватывает каждый системный вызов и замедляет цель в разы — он про «что вызывает», а не «где горит CPU». gdb со степпингом на проде недопустим. Для конкретных языков есть свои семплеры (py-spy, async-profiler), но perf — универсальный системный.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.