Как пользоваться подборкой
Большая часть вопросов в моих подборках — из собеседований, которые я проходил сам. Часть добавил для подготовки. Ответы и то, что могут спросить дальше, — мой разбор, а не дословная расшифровка интервью.
Здесь полезнее не зубрить команды, а проговорить диагностику: что могло сломаться, чем проверю и что ожидаю увидеть. Сначала попробуйте ответить сами, потом сравните с разбором. Незнакомую команду можно проверить на своём стенде. Сами разговоры можно посмотреть в архиве собеседований.
Подборки вопросов: Linux · DevOps · SRE · DevSecOps · MLOps · AIOps
Блок 1. Загрузка и systemd
1. Что происходит от включения питания до приглашения login?
Прошивка (BIOS или UEFI) проходит POST и передаёт управление загрузчику — обычно GRUB. GRUB читает свой конфиг, находит ядро и initramfs, грузит их в память. Ядро инициализирует железо, монтирует временный корень из initramfs, где лежат модули для доступа к настоящему корню — драйверы дисков, LVM, mdadm, шифрование. Дальше switch_root на настоящий корень и запуск PID 1, то есть systemd. Systemd поднимает цели и юниты по зависимостям, доводит систему до multi-user.target или graphical.target, и getty выводит приглашение.
Обычно путаются на роли initramfs — говорят «это ядро». Стоит уметь объяснить, зачем он вообще нужен: без него ядро не сможет прочитать корень на LVM или на зашифрованном разделе.
Что могут спросить дальше: а что сломается, если пересобрать ядро без модуля контроллера дисков и не обновить initramfs? — kernel panic на этапе монтирования корня, машина уйдёт в цикл перезагрузок.
2. Чем Wants= отличается от Requires= в юните systemd?
Requires= подтягивает запуск зависимости. Вместе с After= её неудачный запуск не даст запуститься нашему юниту; явная остановка или перезапуск зависимости тоже распространяются на него. Но это не правило «зависимость перестала быть active — всегда гасим всё следом»: самостоятельная деактивация и пропущенный запуск по Condition могут вести себя иначе. Wants= — мягкая: systemd попытается запустить зависимость, но её падение на наш юнит не влияет. Обе директивы задают только состав, а не порядок: порядок — это отдельно After= и Before=. Самая частая ошибка в реальных конфигах — написать Requires=postgresql.service и ждать, что сервис стартует после базы. Он стартует одновременно с ней, потому что After= забыли.
Смешивают состав и порядок. Важно разделить две вещи: Requires подтягивает зависимость, а After задаёт очерёдность.
Что могут спросить дальше: а чем BindsTo= отличается от Requires=? — BindsTo= вместе с After= связывает и активное состояние: если зависимость внезапно исчезла или стала inactive, наш юнит не должен оставаться активным. Например, так привязывают сервис к устройству.
3. Сервис не стартует. Ваши действия по шагам?
Сначала systemctl status имя — код возврата, последние строки лога, PID. Дальше journalctl -u имя -n 100 --no-pager, а если нужен предыдущий запуск — -b -1. Потом проверяю сам юнит: systemctl cat имя покажет итоговый файл вместе с drop-in из /etc/systemd/system/имя.d/, про которые часто забывают. Если ошибка невнятная, запускаю бинарь руками от нужного пользователя с теми же переменными окружения: systemd-run --uid=... --pty или просто su. Классика — процесс не видит переменные, потому что systemd не читает профиль пользователя.
Забывают про drop-in каталоги и правят основной юнит в /lib, который потом перезатрёт пакетный менеджер. И не делают systemctl daemon-reload после правки.
Что могут спросить дальше: сервис пишет «Permission denied», а права на файл верные. Что ещё? — SELinux или AppArmor, ProtectSystem/ReadOnlyPaths в самом юните, либо неверный SupplementaryGroups.
4. Что такое target и чем он заменил runlevel?
Target — это группирующий юнит, точка синхронизации. Вместо жёсткой лестницы уровней 0–6 systemd описывает состояния графом зависимостей: multi-user.target примерно соответствует старому runlevel 3, graphical.target — пятому, и он в свою очередь требует multi-user. Плюс есть служебные цели вроде network-online.target и basic.target. Текущую цель по умолчанию смотрят через systemctl get-default, меняют через set-default. Главное отличие от runlevel — параллельный запуск: systemd поднимает всё, что не связано зависимостями, одновременно.
Говорят «target это то же самое, что runlevel». Отличие принципиальное: runlevel — последовательность, target — граф.
Что могут спросить дальше: чем network.target отличается от network-online.target? — первый означает «сетевая подсистема настроена», второй «адрес получен и сеть реально доступна»; сервисам, которым нужен коннект на старте, нужен второй.
5. Сервис падает, systemd перезапускает его по кругу. Что делать?
Сначала посмотреть, почему падает: journalctl -u имя и код выхода в статусе. Параллельно приструнить сам цикл — в юните есть StartLimitIntervalSec и StartLimitBurst: они ограничивают число запусков за интервал. Значения проверяю в настройках юнита и менеджера: их могли переопределить. При достижении лимита автоматические попытки прекращаются. Если нужно дать сервису шанс, задают RestartSec= — обычную фиксированную паузу. Для нарастающей задержки в systemd 254+ нужны ещё RestartSteps= и RestartMaxDelaySec=; один RestartSec backoff не создаёт. На проде важнее другое: бесконечный рестарт часто маскирует проблему в зависимостях — недоступную базу или неготовую сеть, — и лечится это условием готовности, а не увеличением числа попыток.
Отвечают только «поставить Restart=no». Хороший ответ разделяет: как остановить шторм сейчас и почему падает на самом деле.
Что могут спросить дальше: как systemd поймёт, что сервис действительно готов, а не просто запустился? — Type=notify и sd_notify из приложения, либо ExecStartPost с проверкой готовности.
6. Как ограничить сервису ресурсы средствами systemd?
Через cgroups, которыми systemd управляет напрямую. В юните задаются MemoryMax= (жёсткий потолок, при превышении процесс убивает OOM внутри cgroup), MemoryHigh= (мягкий порог, включается агрессивный reclaim), CPUQuota= в процентах от ядра, TasksMax= против форк-бомб, IOWeight= для дисковых приоритетов. Посмотреть фактическое потребление — systemd-cgtop. Прелесть в том, что лимит применяется ко всему дереву процессов сервиса, а не к одному PID, поэтому дочерние процессы не убегают из-под ограничения.
Путают MemoryMax и MemoryHigh и не знают, что лимит накрывает всё дерево процессов, а не только главный.
Что могут спросить дальше: что произойдёт при упоре в MemoryMax? — OOM внутри cgroup убьёт процесс сервиса, при этом остальная система не пострадает; в логе будет запись от ядра.
7. Чем journald отличается от syslog и как сделать логи персистентными?
journald хранит структурированный бинарный лог с полями: юнит, PID, приоритет, произвольные ключи от приложения. Отсюда фильтры вида journalctl -u nginx -p err --since "1 hour ago", которые в текстовом syslog делались бы через grep. По умолчанию во многих дистрибутивах журнал живёт в /run/log/journal, то есть в tmpfs, и после перезагрузки исчезает — именно поэтому «после ребута логов нет». Чтобы сохранять, создают /var/log/journal и ставят Storage=persistent в journald.conf, плюс ограничивают размер через SystemMaxUse=. Syslog при этом никуда не делся: journald умеет пересылать записи дальше, в rsyslog и в централизованный сбор.
Не знают про tmpfs и удивляются пропаже логов после перезагрузки. Второй провал — не ограничивают размер журнала, и он съедает /var.
Что могут спросить дальше: как вычистить журнал, не трогая файлы руками? — journalctl --vacuum-size=1G или --vacuum-time=7d.
Блок 2. Файловая система, права, диски
8. df показывает свободное место, но запись не идёт. Почему?
Три классические причины. Первая — кончились inode: df -i покажет 100% при свободных гигабайтах, типично для каталогов с миллионами мелких файлов, кешей и почтовых очередей. Вторая — квота пользователя или проекта: свободное место на файловой системе есть, но конкретному владельцу больше писать нельзя. Третья — ограничения самой записи: файловая система стала read-only, нет прав или достигнут лимит размера файла. Отдельный похожий случай — удалённый открытый файл: он объясняет, почему df показывает больше занятого, чем du, а не свободный диск; проверяется через lsof +L1.
Останавливаются на первой причине. Перечислите варианты и скажите, какой командой каждый проверить.
Что могут спросить дальше: как освободить место, занятое удалённым открытым файлом, не перезапуская сервис? — если это именно лог и потеря его содержимого допустима, можно освободить блоки через открытый дескриптор в /proc/PID/fd. Так нельзя поступать с файлом базы или произвольными данными. Лучше штатно попросить приложение переоткрыть лог; если такой возможности нет — запланировать перезапуск.
9. Что означают права rwx на каталоге?
Для каталога смысл другой, чем для файла. r — можно прочитать список имён, то есть сделать ls. x — можно пройти через каталог и обратиться к записи по имени; разрешения самого файла и остальные ограничения проверяются отдельно. w вместе с x — можно создавать, удалять и переименовывать записи. Отсюда контринтуитивные следствия: с правом x, но без r, можно прочитать файл по известному имени, если права самого файла это разрешают, но нельзя получить список имён каталога. И наоборот, право на удаление файла определяется правами на каталог, а не на сам файл — поэтому чужой файл в каталоге, куда у вас есть запись, удалить можно.
Не переносите значение rwx для обычного файла на каталог: именно здесь меняется смысл прав, особенно права на удаление.
Что могут спросить дальше: а как запретить удаление чужих файлов в общем каталоге? — sticky bit, как на /tmp: обычный пользователь сможет удалить свой файл; удаление также доступно владельцу каталога и процессу с необходимыми привилегиями.
10. Что такое setuid, setgid и sticky bit?
setuid на исполняемом файле означает, что процесс запустится с правами владельца файла, а не запустившего: так работает passwd, которому нужен доступ к /etc/shadow. setgid на файле — то же самое для группы, а на каталоге даёт другое поведение: все создаваемые внутри файлы наследуют группу каталога, что удобно для общих папок команды. Sticky bit на каталоге ограничивает удаление и переименование чужих файлов: это может делать владелец файла, владелец каталога или привилегированный процесс — классика /tmp. Найти все setuid-бинари: find / -perm -4000 -type f.
Не знают, что setgid на каталоге и на файле делают разные вещи. И не упоминают, что setuid — первое, что проверяют при разборе компрометации.
Что могут спросить дальше: почему setuid не работает на скриптах? — Linux игнорирует этот бит у скриптов из-за гонки между проверкой прав и запуском интерпретатора.
11. Чем hard link отличается от symlink?
Жёсткая ссылка — это ещё одно имя для той же inode: у файла просто растёт счётчик ссылок, и данные исчезают только когда счётчик обнулился и никто не держит дескриптор. Она не может пересекать границу файловой системы и не указывает на каталоги. Символическая ссылка — отдельный маленький файл, внутри которого лежит путь; она спокойно живёт на другой файловой системе, может указывать на каталог и превращается в «битую», если цель переименовали. Практический вывод: бэкап, сделанный жёсткими ссылками, экономит место, но правка файла меняет его во всех «копиях».
Говорят «symlink это ярлык», но не могут объяснить, почему hard link не работает между разделами: у каждой файловой системы своя нумерация inode.
Что могут спросить дальше: что покажет ls -l для жёсткой ссылки? — другое имя, но те же данные о файле и счётчик ссылок. ls -i покажет одинаковый inode у обоих имён: это подтверждает жёсткую связь, а не отличает «оригинал» от «копии». Все такие имена равноправны.
12. Как расширить файловую систему на LVM, не уронив сервис?
Порядок снизу вверх. Добавляем диск или расширяем существующий, потом pvresize, чтобы LVM увидел новый размер физического тома. Дальше lvextend -L +50G /dev/vg/lv. И только затем растёт сама файловая система: resize2fs для ext4 или xfs_growfs для XFS — обе умеют делать это на смонтированном разделе, простой не нужен. Удобно сразу lvextend -r, который вызывает ресайз файловой системы за вас. Обратная операция — уменьшение — на XFS невозможна вообще, а на ext4 требует размонтирования и делается только с бэкапом.
Путают порядок и пытаются сначала растянуть файловую систему. И почти никто не помнит, что XFS не уменьшается — а это важное архитектурное ограничение.
Что могут спросить дальше: а если LV на диске, который уже расширили в гипервизоре, но система не видит новый размер? — перечитать шину: echo 1 > /sys/class/block/sdX/device/rescan.
13. Что делает опция монтирования noatime и зачем она нужна?
При строгом учёте atime чтение файла обновляет время последнего доступа и добавляет запись метаданных. Но на Linux часто по умолчанию действует relatime, а не обновление при каждом чтении. На нагруженных серверах с большим количеством мелких файлов это заметная лишняя нагрузка на диск. noatime отключает обновление полностью, relatime — компромисс, обновляет только если предыдущее значение старше суток или старше времени модификации; во многих дистрибутивах relatime стоит по умолчанию. Отключать atime нельзя, если на машине есть софт, который на него завязан, — например, некоторые почтовые системы отличают прочитанную почту именно по atime.
Ответ «ускоряет диск» без объяснения механизма. И не знают про relatime, считая, что выбор только между atime и noatime.
Что могут спросить дальше: где ещё стоит посмотреть опции монтирования при разборе производительности? — barrier/nobarrier и discard: последний включает онлайн-TRIM, который на некоторых SSD даёт заметные паузы.
14. Как найти, что именно занимает место на диске?
Сверху вниз. df -h — какая файловая система заполнена, df -i — не в inode ли дело. Дальше du -xh --max-depth=1 / и спуск по самому толстому каталогу; ключ -x обязателен, иначе du уйдёт в другие смонтированные файловые системы и в /proc. Для интерактивного разбора удобен ncdu. Отдельно проверяю два места, которые du покажет неправильно: удалённые открытые файлы через lsof +L1 и содержимое каталогов, поверх которых что-то смонтировано — они видны только после размонтирования или через bind-mount корня.
Забывают -x и потом удивляются, почему сумма du не сходится с df. Второй провал — не проверяют удалённые открытые файлы.
Что могут спросить дальше: du показал 40 ГБ, df говорит 80 ГБ занято. Где разница? — почти наверняка удалённые файлы, которые держит процесс, либо данные под точкой монтирования.
Блок 3. Процессы, память, производительность
15. Процесс висит в состоянии D. Что это значит и что с ним делать?
D — это uninterruptible sleep: процесс ушёл в системный вызов и ждёт ответа от ядра, чаще всего от блочного устройства или от сетевой файловой системы. Он не реагирует ни на какие сигналы, включая kill -9, потому что сигнал доставляется при выходе из системного вызова, а выхода не происходит. Лечится не убийством процесса, а устранением причины: подвисший NFS-сервер, отвалившийся LUN, умирающий диск. Смотрю dmesg на предмет ошибок ввода-вывода и таймаутов, cat /proc/PID/stack — где именно застряли, iostat -x 1 — не упёрлись ли в диск. Много процессов в D разом почти всегда означают проблему на уровне хранилища, а не на уровне приложения.
Пытаются добить его через kill -9 и делают вывод, что «система сломана». Сигнал можно отправить, но завершение отложится, пока процесс не выйдет из непрерываемого ожидания.
Что могут спросить дальше: а как процессы в D влияют на load average? — прямо: в Linux LA считает и D-состояния тоже, поэтому подвисшее хранилище даёт LA под сотню при нулевом CPU.
16. Load average 40 на 8 ядрах. Всё плохо?
Не обязательно. Load average в Linux — это среднее число задач в состояниях R (готов бежать или бежит) и D (ждёт ввода-вывода) за 1, 5 и 15 минут. Из-за D-состояний метрика перестаёт быть про процессор: LA 40 при нулевой загрузке CPU означает, что сорок задач ждут диск или сеть, и лечить надо хранилище. И наоборот: LA 8 на 8 ядрах при полной загрузке CPU — это ровно рабочая ситуация. Поэтому LA я всегда читаю в паре: top или vmstat 1 покажет распределение по us/sy/wa/id, и уже колонка wa отделяет процессорную нагрузку от дисковой.
Классика — сравнивают LA с числом ядер и на этом останавливаются. Ключевая деталь, которую ждут: в Linux в LA входит iowait, в отличие от классического Unix.
Что могут спросить дальше: три числа LA: 40, 12, 5. О чём это говорит? — нагрузка резко выросла только что; если бы порядок был обратный, пик уже прошёл и система разгружается.
17. Чем RSS отличается от VSZ и сколько памяти реально ест процесс?
VSZ — размер виртуального адресного пространства: всё, что процесс замапил, включая ещё не тронутые страницы, файлы через mmap и разделяемые библиотеки. Он может быть в разы больше физической памяти машины и сам по себе ни о чём не говорит. RSS — сколько страниц реально лежит в физической памяти прямо сейчас, но и он завышает: разделяемые библиотеки посчитаны целиком в каждом процессе, который их использует. Честная метрика — PSS из /proc/PID/smaps_rollup: общая память делится между процессами пропорционально. Для вопроса «сколько освободится, если убить процесс» смотрят USS.
Складывают RSS всех процессов, получают больше, чем есть памяти в машине, и не могут это объяснить.
Что могут спросить дальше: почему сумма RSS может превышать физическую память? — из-за многократного учёта разделяемых страниц; PSS такой проблемы не имеет.
18. Как работает OOM killer и как он выбирает жертву?
Когда ядро не может выделить страницу и не помогает вытеснение, оно вызывает OOM killer. Тот считает для каждого процесса oom_score, отталкиваясь в первую очередь от объёма занятой памяти, и корректирует его на oom_score_adj из /proc/PID/ — диапазон от −1000 (неприкосновенный) до +1000. Убивается процесс с максимальным счётом, то есть обычно самый жирный, и это чаще всего именно ваша база или бэкенд, а не виновник утечки. Следы всегда остаются в dmesg и в journal: строка Out of memory: Killed process с полной таблицей кандидатов. Отдельно есть cgroup-OOM: при упоре в MemoryMax= сервиса убивают внутри его cgroup, остальная система не страдает.
Не знают, что событие логируется в dmesg, и часами ищут, «почему процесс сам упал без ошибки». Второй провал — считают, что OOM killer срабатывает при исчерпании swap, а не физической памяти.
Что могут спросить дальше: как защитить от OOM конкретный процесс? — echo -1000 в /proc/PID/oom_score_adj либо OOMScoreAdjust= в юните systemd; но чаще правильнее ограничить обжору, а не защищать жертву.
19. free -m показывает 200 МБ свободно из 32 ГБ. Пора паниковать?
Нет, и это самый частый ложный алярм. Linux по определению занимает всю незанятую память под page cache — кеш прочитанных с диска данных, — потому что простаивающая память бесполезна. Эта память освобождается мгновенно, как только она понадобится приложению. Смотреть нужно не на колонку free, а на available: она учитывает то, что можно отдать без свопа. Настоящие признаки нехватки другие: рост si/so в vmstat 1, то есть активный своп, всплеск pgscan и pgsteal, срабатывания OOM в dmesg. И отдельно — не путать buffers/cache с утечкой в приложении.
Реагируют на free вместо available. Ещё встречается ритуальное drop_caches на проде — оно ничего не чинит, только выкидывает горячий кеш и делает хуже.
Что могут спросить дальше: когда page cache всё-таки становится проблемой? — когда грязных страниц много и dirty_ratio упирается в потолок: тогда запись превращается в синхронную и приложение встаёт колом.
20. Процесс жрёт CPU. Как понять, чем именно он занят?
Иду сверху вниз. top -H -p PID — какой именно поток горит, потому что в многопоточном приложении виноват обычно один. cat /proc/PID/status и wchan — не спит ли он в ядре. Дальше два разных инструмента: strace -c -p PID покажет, если время уходит на системные вызовы (частый случай — миллионы мелких read или futex), а perf top -p PID покажет горячие функции, если время уходит в user space. Для JVM и питона есть свои профайлеры и дампы стека. strace на проде применяю аккуратно: он тормозит процесс кратно, поэтому вешаю на несколько секунд, а не оставляю висеть.
Ограничиваются top и делают вывод «приложение плохое». Ждут, что вы отделите user-время от системного и назовёте разный инструмент под каждый случай.
Что могут спросить дальше: почему strace опасен на проде? — он останавливает процесс на каждом системном вызове через ptrace; на нагруженном сервисе это может дать деградацию в разы.
21. Что такое зомби-процесс и надо ли его убивать?
Зомби — это уже завершившийся процесс, запись о котором осталась в таблице, потому что родитель не забрал код возврата через wait(). Он не занимает ни памяти, ни CPU, только слот в таблице процессов. Убить его нельзя — он уже мёртв; лечится либо тем, что родитель наконец сделает wait, либо перезапуском родителя, после чего зомби усыновит PID 1 и приберёт. Опасны не единичные зомби, а их непрерывный рост: это баг в родительском процессе, и рано или поздно кончатся PID. Отдельно стоит различать сироту — процесс, чей родитель умер: он живой и его просто перевешивают на init.
Пытаются убить зомби через kill и делают вывод, что процесс «не убивается». И путают зомби с сиротой.
Что могут спросить дальше: чем в контейнере опасно отсутствие нормального PID 1? — приложение в роли PID 1 не собирает потомков, зомби копятся; поэтому используют tini или --init.
Блок 4. Сеть на уровне хоста
22. Приложение не открывается по домену. Разложите диагностику по шагам.
Иду по слоям и после каждого шага знаю, что именно проверил. DNS: dig +short домен и сравнение с ожидаемым адресом, отдельно проверяю резолв на самой машине, потому что /etc/hosts и nsswitch могут врать. Дальше связность до адреса: ping ничего не доказывает, если ICMP закрыт, поэтому сразу curl -v или nc -vz host port — открывается ли TCP-соединение. Если соединение не устанавливается, смотрю с той стороны: слушает ли процесс нужный интерфейс через ss -lntp — типичная ошибка, когда сервис прибит к 127.0.0.1 вместо 0.0.0.0. Если слушает, то фильтрация: iptables -L -n -v или nft list ruleset, счётчики покажут, растут ли дропы. И только в конце — логи приложения и TLS, если проблема на уровне сертификата.
Прыгают сразу в логи приложения или, наоборот, бесконечно пингуют. Ждут именно упорядоченную проверку с явным выводом на каждом шаге.
Что могут спросить дальше: curl говорит Connection refused, а не таймаут. О чём это? — до хоста дошли и получили RST: порт никто не слушает или соединение отбил локальный firewall с REJECT; таймаут же обычно означает молчаливый DROP или потерю пакетов по дороге.
23. Как посмотреть, кто слушает порт, и почему ss, а не netstat?
ss -lntup: listening, TCP, UDP, без резолва имён, с указанием процесса. netstat из пакета net-tools во многих дистрибутивах уже не ставится по умолчанию и работает медленнее, потому что читает /proc/net/* целиком, тогда как ss берёт данные через netlink и умеет фильтры вида ss -o state established dport = :443. Если процесс не показывается, значит команда запущена не от root. Для вопроса «кто держит порт 80» рядом полезен lsof -i :80, а для картины «сколько соединений в каком состоянии» — ss -s.
Ответ «netstat -tulpn» без объяснений принимают, но следом почти всегда спрашивают про ss. Без нужных прав данные о чужом процессе могут не отображаться; это не означает, что порт никто не слушает.
Что могут спросить дальше: сервис слушает, а снаружи не подключиться. Что смотреть первым? — адрес привязки: 127.0.0.1 против 0.0.0.0, и только потом firewall.
24. Что такое conntrack и как он роняет прод?
Это таблица отслеживания соединений в netfilter: чтобы понимать, что пакет относится к уже установленной сессии, ядро держит запись на каждое соединение. Размер таблицы ограничен nf_conntrack_max. Когда она заполняется, новые соединения просто отбрасываются, а в dmesg появляется nf_conntrack: table full, dropping packet. Симптом со стороны приложения выглядит загадочно: сервер не нагружен, но часть клиентов не подключается. Смотрю текущее заполнение в /proc/sys/net/netfilter/nf_conntrack_count, лечу либо увеличением max и хеш-таблицы, либо уменьшением таймаутов, либо через NOTRACK для трафика, которому отслеживание не нужно.
Не знают о существовании таблицы вообще и ищут проблему в приложении. Второй провал — поднимают nf_conntrack_max, не увеличив hashsize, и получают деградацию из-за длинных цепочек.
Что могут спросить дальше: почему на балансировщике таблица кончается быстрее? — каждое клиентское соединение плюс каждое соединение к бэкенду занимают отдельные записи, а TIME_WAIT держит их ещё десятки секунд после закрытия.
25. Десятки тысяч сокетов в TIME_WAIT. Это проблема?
Обычно нет. TIME_WAIT — нормальная фаза закрытия на стороне, которая закрыла соединение первой: ядро держит сокет 2×MSL, то есть около 60 секунд, чтобы не перепутать задержавшиеся пакеты с новым соединением на том же порту. Само по себе большое их число говорит лишь о высокой скорости открытия и закрытия соединений. Проблемой это становится в двух случаях: на исходящих соединениях кончаются локальные порты из ip_local_port_range, и тогда помогает расширение диапазона, keep-alive и переиспользование соединений; либо переполняется conntrack. А вот net.ipv4.tcp_tw_recycle трогать нельзя — его выпилили из ядра именно потому, что он ломал клиентов за NAT.
Сразу лезут крутить sysctl, чаще всего копипастой из старой статьи с tcp_tw_recycle. Сначала объясните, какая сторона соединения находится в этом состоянии и почему.
Что могут спросить дальше: чем tcp_tw_reuse отличается от tw_recycle? — reuse позволяет переиспользовать сокет в TIME_WAIT для нового исходящего соединения и это безопасно, recycle делал предположения о временных метках клиента и ломался за NAT.
26. Как читать таблицу маршрутизации и зачем нужен policy routing?
ip route показывает основную таблицу: default gateway, подключённые сети, конкретные маршруты. Выбор идёт по самому длинному совпадающему префиксу, а не по порядку строк. Проверять надо не глазами, а через ip route get 8.8.8.8 — ядро прямо скажет, какой маршрут, интерфейс и исходный адрес оно выберет. Policy routing нужен, когда решение зависит не только от адреса назначения: например, у машины два провайдера, и ответы должны уходить в тот же интерфейс, откуда пришёл запрос. Тогда заводят отдельные таблицы в /etc/iproute2/rt_tables и правила ip rule по исходному адресу или метке пакета.
Классическая беда двух аплинков — ответ уходит через default gateway другого провайдера и режется антиспуфингом; человек ищет проблему в firewall.
Что могут спросить дальше: как убедиться, с какого исходного адреса пойдёт пакет? — ip route get выведет src; его же можно зафиксировать через параметр src у маршрута.
27. Соединение устанавливается, но большие ответы не доходят. Куда смотреть?
Похоже на проблему с MTU. Рукопожатие и мелкие пакеты проходят, а как только появляется полноразмерный сегмент, он не пролезает в туннель или в канал с меньшим MTU. Если по пути кто-то режет ICMP тип 3 код 4, механизм Path MTU Discovery не работает: отправитель не узнаёт, что надо уменьшить пакет, и соединение просто зависает. Проверяю пингом с запретом фрагментации: ping -M do -s 1472 адрес и подбираю размер. Лечится либо приведением MTU в порядок на интерфейсе, либо MSS clamping на маршрутизаторе: --clamp-mss-to-pmtu.
Симптом описывают как «сеть моргает» и ищут потери, хотя потерь нет. Признак, по которому узнают MTU: висят именно большие передачи, ssh коннектится, а вывод команды с длинным ответом обрывается.
Что могут спросить дальше: где чаще всего встречается? — в туннелях: VPN, GRE, IPIP съедают часть кадра, а MTU внутри туннеля забывают уменьшить.
28. Как понять, теряются пакеты в сети или тормозит приложение?
Разделяю ответственность инструментами. Со стороны сети — mtr вместо traceroute: он показывает потери и задержку по каждому хопу за длительный интервал, при этом потери на промежуточном хопе при чистом последнем обычно означают лишь rate limit на ICMP, а не реальную проблему. На самом хосте — ip -s link и ethtool -S на предмет errors и drops, netstat -s для ретрансмитов TCP. Со стороны приложения — время до первого байта в curl -w: быстрое TCP-рукопожатие при медленном ответе сужает поиск, но не исключает сеть: потери и MTU-проблемы могут проявиться уже при передаче данных. Финальный аргумент, если спорим со смежниками, — tcpdump с двух сторон одновременно: видно, ушёл ли пакет и дошёл ли он.
Опираются на один ping и делают вывод. Хороший ответ явно отделяет метрику сети от метрики приложения и заканчивается словами «снять дамп с обеих сторон».
Что могут спросить дальше: curl показывает большой time_namelookup. Что это? — задержка на этапе DNS; причиной может быть и сеть до резолвера: недоступный резолвер, таймаут на первом сервере в resolv.conf, поиск по search-доменам.
Блок 5. Разбор инцидентов
29. «Сервер тормозит». Ваши первые пять минут?
Сначала уточняю, что значит тормозит и с какого момента: тормозит для всех или для одного клиента, деградация или полный отказ, что менялось последним. Дальше стандартный обход за минуту: uptime — три числа LA и понимание, растёт нагрузка или спадает; vmstat 1 5 — распределение по us/sy/wa/id и колонки si/so, чтобы сразу отделить процессор от диска и от свопа; top с сортировкой по CPU и по памяти — есть ли один очевидный виновник; df -h и df -i — не кончилось ли место; dmesg -T | tail — OOM, ошибки диска, сетевые дропы. За эти пять минут я не чиню, а сужаю до одной подсистемы: CPU, память, диск, сеть или само приложение. И параллельно смотрю, что деплоили за последние сутки, потому что это самая частая причина.
Начинают перезагружать сервис до того, как поняли причину, и убивают улики. Ждут проговорённой последовательности и явной фразы «сначала снимаю состояние, потом чиню».
Что могут спросить дальше: а если ничего из этого не показало аномалий? — значит проблема выше по стеку: смежный сервис, база, DNS или балансировщик; тогда смотрю время ответа зависимостей со стороны приложения.
30. Ночью кончилось место на /var. Что делаете?
Сначала быстро вернуть сервис в работу, потом разбираться. Смотрю df -h /var и df -i /var, потом du -xh --max-depth=1 /var и спускаюсь по самому толстому каталогу. Типичные виновники: журнал systemd без ограничения, логи приложения без ротации, кеш пакетного менеджера, дампы ядра, забытые бэкапы. Быстрые безопасные действия — journalctl --vacuum-size=500M, чистка кеша пакетов, ротация логов вручную через logrotate -f. Отдельно проверяю удалённые открытые файлы через lsof +L1: если место держит удалённый лог, никакая чистка не поможет, нужен перезапуск процесса. Что нельзя делать — удалять активный лог командой rm: место не освободится, а приложение продолжит писать в никуда.
Удаляют большой лог через rm, видят, что df не изменился, и не понимают почему. И не ставят после инцидента ограничение размера, поэтому через месяц всё повторяется.
Что могут спросить дальше: как сделать так, чтобы это не повторилось? — SystemMaxUse в journald, logrotate с maxsize, алерт на заполнение на 80% и отдельный раздел под /var, чтобы переполнение не убивало корень.
31. Приложение отвечает медленно, но CPU, память и диск в норме. Куда смотреть?
Значит ждём чего-то внешнего. Проверяю зависимости: время ответа базы, кеша, соседних сервисов, DNS. Дальше — исчерпание не самых очевидных ресурсов: лимит открытых файлов (ulimit -n и /proc/PID/limits, в логах это выглядит как Too many open files), переполнение очереди на приём соединений (ss -lnt покажет Send-Q как размер backlog и Recv-Q как текущую очередь, плюс счётчик listen overflow в netstat -s), исчерпание пула соединений или потоков внутри самого приложения, блокировки в базе. Отдельно — не упёрлись ли в лимиты cgroup, если сервис живёт под ограничением systemd или в контейнере: снаружи ресурсы свободны, а внутри квота выбрана.
Смотрят только классическую четвёрку CPU/RAM/диск/сеть и упираются в тупик. В ответе стоит упомянуть лимиты дескрипторов, backlog и cgroup-квоты.
Что могут спросить дальше: сервис отвечает быстро на localhost, но медленно снаружи. Где искать? — балансировщик, TLS-рукопожатие, MTU или разрешение имён на клиентской стороне.
32. Машина не отвечает по SSH. Действия?
Сначала отделяю недоступность машины от недоступности sshd. Проверяю, отвечает ли что-то ещё на этом хосте — другой порт, мониторинг, метрики. Если хост в сети, но порт 22 молчит, вероятны варианты: sshd упал или не смог запуститься после правки конфига, firewall закрыл порт, кончились дескрипторы или память и новый форк не создаётся, корень заполнен и sshd не может создать сессию. Если хост не отвечает вовсе — иду в консоль гипервизора или в IPMI: там видно kernel panic, приглашение emergency mode или сообщение о недоступном корне. Перезагрузка вслепую — последнее средство, потому что она уничтожает состояние, по которому и восстанавливается причина.
Сразу перезагружают. Ждут упоминания консоли вне сети как единственного надёжного канала и понимания, что «не пингуется» и «не пускает по ssh» — разные диагнозы.
Что могут спросить дальше: как заранее подстелить соломки? — держать доступ в консоль гипервизора, ставить второй sshd на другом порту и резервировать root-квоту в файловой системе, чтобы заполнение диска не блокировало вход.
33. После обновления пакетов сервис сломался. Как откатываться?
Сначала подтверждаю связь: journalctl и история пакетного менеджера — dnf history или /var/log/apt/history.log — покажут, что и когда обновилось. Дальше выбираю способ отката. В rpm-мире есть dnf history undo, что честно возвращает предыдущий набор версий. В deb-мире надёжнее явная установка нужной версии из кеша /var/cache/apt/archives с последующим apt-mark hold, чтобы следующее обновление не вернуло проблему. Отдельно проверяю, не в пакете ли дело вовсе, а в конфиге: при обновлении мог появиться .rpmnew или dpkg мог спросить про конфликт конфигурации и оставить новый файл. И сразу фиксирую версию в системе управления конфигурацией, иначе через неделю всё повторится на соседних машинах.
Откатывают на одной машине и забывают зафиксировать версию, из-за чего проблема возвращается. Ещё частый провал — не проверяют .rpmnew и .dpkg-dist.
Что могут спросить дальше: а если откат невозможен, потому что схема базы уже мигрировала? — тогда откат приложения ломает данные; может потребоваться исправление вперёд вместо отката. На будущее — обратно совместимые миграции и проверенная процедура восстановления.
34. Как доказать, что проблема не на вашей стороне?
Собираю симметричные измерения. Со своей стороны — что сервис отвечает локально: curl на 127.0.0.1 с таймингами, метрики времени обработки внутри приложения. Со стороны сети — mtr в обе стороны и tcpdump одновременно на своём хосте и на хосте клиента: видно, ушёл ли ответ и дошёл ли запрос. Со стороны зависимостей — время ответа базы и смежных сервисов за тот же интервал. Дальше сопоставляю таймлайн: когда началось, что менялось у нас, что менялось у них. Важна интонация: цель не победить в споре, а сузить область, поэтому вывод формулирую как «запрос до нас не доходит, вот дамп», а не «у вас всё сломано».
Приносят один график и утверждение. Ждут именно парного измерения с двух сторон и сопоставления по времени.
Что могут спросить дальше: клиент говорит, что видит таймауты, а в ваших логах запросов нет вообще. О чём это? — почти наверняка запросы не доходят: балансировщик, DNS, firewall или неверный адрес; смотреть надо дамп на входящем интерфейсе.
35. Что должно быть в постмортеме?
Таймлайн с точным временем: когда началось, когда заметили, когда поняли, когда починили. Влияние в измеримых величинах — сколько минут, какая доля запросов, какие клиенты. Причина, разобранная до уровня механизма, а не до фамилии. Что помогло обнаружить и что помешало обнаружить раньше. И список действий с владельцем и сроком, разделённый на два типа: устраняющие причину и уменьшающие время обнаружения. Ключевое условие — постмортем безобвинительный: как только он становится инструментом поиска виноватого, люди начинают скрывать детали и ценность документа исчезает.
Пишут «виноват Вася, будем внимательнее». Разберите механизм отказа и предложите конкретные проверяемые действия. «Теперь будем внимательнее» механизм не меняет.
Что могут спросить дальше: что делать, если причина так и не найдена? — так и написать, зафиксировать гипотезы и добавить телеметрию, которой не хватило; честный постмортем без причины полезнее выдуманной причины.
Блок 6. Доступы, безопасность, эксплуатация
36. Permission denied (publickey). Как чинить?
Сначала смотрю глазами клиента: ssh -vvv покажет, какие ключи предлагались и на каком этапе сервер отказал. Дальше со стороны сервера — journalctl -u sshd, там причина обычно написана прямым текстом. Типичные виновники: права на домашний каталог и на ~/.ssh шире, чем ожидает sshd, и он молча отвергает ключ (нужны 700 на каталог и 600 на authorized_keys, и домашний каталог не должен быть доступен на запись группе); ключ положен не тому пользователю; в sshd_config стоит AllowUsers или PubkeyAuthentication no; на системах с SELinux у файла неверный контекст, лечится restorecon -R -v ~/.ssh; или клиент предлагает не тот ключ, потому что в агенте их слишком много и сервер обрывает после нескольких попыток.
Проверяют только содержимое authorized_keys и не смотрят права на домашний каталог. Ещё забывают, что после правки sshd_config нужно перечитать конфиг и обязательно оставить открытой вторую сессию.
Что могут спросить дальше: почему опасно перезапускать sshd вслепую при правке конфига? — при ошибке в конфиге демон не поднимется и вы потеряете доступ; поэтому сначала sshd -t, потом reload, и вторая сессия открыта до конца проверки.
37. Чем sudo отличается от su и что такое setuid?
su открывает сессию другого пользователя целиком и требует его пароль; после этого всё, что вы делаете, идёт от его имени и в логе не видно, кто именно это был. sudo выполняет одну команду по правилам из /etc/sudoers, спрашивает ваш собственный пароль и пишет в лог, кто и что запустил — поэтому на проде используют именно его, ради разграничения и аудита. setuid — это бит на исполняемом файле, из-за которого процесс запускается с правами владельца файла, а не запускающего; именно так работает passwd, которому нужен доступ к /etc/shadow. Сам sudo тоже setuid-бинарь. Важное следствие: любой лишний setuid-root файл в системе — потенциальный путь к повышению привилегий, поэтому их периодически инвентаризируют через find / -perm -4000.
Ограничиваются формулировкой «sudo безопаснее». Ждут слов про аудит и про то, что sudo ALL без ограничений по сути равен раздаче root.
Что могут спросить дальше: чем NOPASSWD в sudoers опасен и когда оправдан? — оправдан для конкретных команд в автоматизации; опасен как ALL, потому что тогда любая компрометация пользователя сразу даёт root.
38. Что такое capabilities и зачем они, если есть root?
Права root разбиты на отдельные возможности: CAP_NET_BIND_SERVICE — слушать порты ниже 1024, CAP_NET_ADMIN — управлять сетью, CAP_SYS_TIME — менять время, и так далее. Это позволяет выдать процессу ровно то, что ему нужно, вместо полного root. Практический пример: чтобы веб-сервер слушал 80-й порт, не обязательно запускать его от root — достаточно setcap cap_net_bind_service=+ep на бинарь или AmbientCapabilities= в юните systemd. Смотреть текущий набор — getpcaps PID или поле Cap в /proc/PID/status. Тот же механизм лежит в основе контейнеров: docker по умолчанию оставляет процессу урезанный набор, поэтому внутри контейнера «root» заметно слабее настоящего.
Не знают, что root в контейнере ограничен capabilities, и считают его равным хостовому. Второй провал — не подозревают, что setcap слетает при обновлении пакета.
Что могут спросить дальше: почему после обновления пакета сервис перестал биндиться на 80? — новый бинарь пришёл без capabilities, их надо выставлять заново или задавать через systemd, что надёжнее.
39. Как выдать команде доступ на тридцать серверов и не потерять управление?
Ключевой принцип — доступ не раздаётся руками. Учётки и ключи раскатываются из системы управления конфигурацией или из централизованного каталога, чтобы отзыв делался в одном месте, а не обходом тридцати машин. Личные учётные записи, не общий admin: иначе в логе не видно, кто что сделал. Права выдаются через группы и точечные правила sudo, а не полным ALL. Вход по ключам, парольная аутентификация выключена, root по ssh запрещён. Для боевого контура правильнее bastion с записью сессий, чтобы прямых маршрутов на прод не было вовсе. И регулярная ревизия: кто ещё имеет доступ, у кого он остался после смены роли или увольнения.
Отвечают «раскатать authorized_keys ансиблом» и на этом останавливаются. Ждут ещё двух вещей: как отзывается доступ и как в логах видно конкретного человека.
Что могут спросить дальше: сотрудник ушёл. Ваши действия? — удалить из каталога или из группы в системе конфигураций, прогнать её по парку, проверить остаточные ключи и активные сессии, ротировать общие секреты, к которым он имел доступ.
40. SELinux мешает сервису. Что делать кроме setenforce 0?
Сначала подтверждаю, что дело в нём: ausearch -m AVC -ts recent или journalctl | grep AVC покажут отказ с указанием контекста и операции. Дальше самый частый случай — неверный контекст файла, например веб-контент положили в нестандартный каталог: лечится semanage fcontext -a с нужным типом и restorecon -Rv. Второй случай — нужна булева переменная, вроде разрешения веб-серверу ходить по сети: getsebool -a и setsebool -P. И только если ни то, ни другое не подходит, генерируют локальный модуль через audit2allow, обязательно посмотрев глазами, что именно он разрешает. Полное отключение — это не решение, а снятие защиты со всей машины ради одного сервиса.
Отвечают «отключить». Даже если в компании SELinux действительно выключен, на собеседовании ждут, что человек знает нормальный путь и понимает цену отключения.
Что могут спросить дальше: как временно проверить гипотезу, не выключая защиту совсем? — перевести в permissive для конкретного домена через semanage permissive -a: нарушения будут логироваться, но не блокироваться.
41. Как обновлять прод, чтобы ничего не уронить?
Порядок, а не смелость. Обновления сначала едут на стенд, потом на канареечную машину, потом на остальной парк. Версии пакетов зафиксированы и одинаковы во всём парке, иначе воспроизвести проблему невозможно. Перед обновлением снимается снапшот или проверяется, что бэкап действительно восстанавливается, и заранее описан план отката. Ноды выводятся из балансировки по одной, после каждой проверяется здоровье сервиса, и только потом берётся следующая. Обновление ядра и всё, что требует перезагрузки, планируется в окно, а для критичных машин рассматривают live patching. И отдельно — обновления безопасности не откладываются бесконечно ради стабильности: это тоже риск, просто отложенный.
Говорят про «сначала на тесте» и не могут ответить, как выглядит откат и как проверяется здоровье после каждой ноды.
Что могут спросить дальше: что делать, если обновление уже раскатано на весь парк и сломало сервис? — откат по истории пакетного менеджера с фиксацией версии, параллельно поднимать канареечную схему, чтобы это не повторилось.
42. Что вы проверите на свежем сервере перед выкаткой в прод?
Базовый набор, который я обычно держу отдельным чеклистом. Доступ: вход по ключам, парольная аутентификация и root по ssh выключены, личные учётки заведены. Сеть: открыты только нужные порты, правила firewall описаны в конфигурации, а не набраны руками. Время: NTP работает — рассинхронизация часов ломает логи, TLS и распределённые системы. Наблюдаемость: агент мониторинга и сбор логов подключены до, а не после инцидента, журнал персистентный и ограничен по размеру. Обслуживание: настроена ротация логов, автоматические обновления безопасности, свободное место с запасом и алерт на заполнение. Ресурсы: лимиты дескрипторов подняты под нагрузку сервиса. И финально — машина описана в системе управления конфигурацией, чтобы её можно было воспроизвести с нуля.
Перечисляют только firewall и ключи. В список стоит включить наблюдаемость и NTP — без них сложнее заметить сбой и сопоставить события.
Что могут спросить дальше: почему NTP в этом списке? — разъехавшиеся часы дают невозможность сопоставить логи между машинами, ошибки проверки сертификатов и разваливающийся консенсус в кластерах.
Частые вопросы
Сколько вопросов реально задают на собеседовании Linux-администратора?
Обычно 10–20 за час, и почти всегда с уходом вглубь по одному-двум. Ценность подборки не в том, чтобы выучить 42 ответа, а в том, чтобы закрыть пробелы в тех блоках, где вы плаваете.
Нужно ли знать команды наизусть?
Нет. Никто не ждёт, что вы вспомните все ключи find. Ждут, что вы назовёте нужный инструмент и объясните, что рассчитываете увидеть в выводе.
Что делать, если не знаете ответа?
Рассуждать вслух и честно обозначить границу: «с этим не сталкивался, но полез бы смотреть туда-то». Молчание и попытка угадать читаются хуже, чем признание пробела.
Менторство: подготовка к собеседованию по Linux
Менторство — от 100 000 ₽.
Готовимся к интервью: я задаю вопросы, затем вместе разбираем ответы и подготовку.
Состав, сроки и результат согласуем до начала.
«Будни Айтишника» — закрытый чат сообщества.
Общаемся про IT, работу, деньги, свои проекты и жизнь.
Делимся опытом, обсуждаем идеи и поддерживаем друг друга.
Готовитесь к первому собеседованию с нуля — начните с маршрута подготовки на Dev0pser.
Уже работаете и выбираете следующую компанию — пригодится разбор для middle и senior на IT-Para.