Вопросы по Docker на собеседовании инженера
Docker спрашивают у всех, кто ближе к эксплуатации, чем к чистому коду. Проверяют модель: почему контейнер это процесс, а не маленькая виртуалка, куда девается слой записи при пересоздании и почему секрет, удалённый следующей инструкцией, всё ещё лежит в образе.
Что спрашивают
- +Образы и слои: кэш сборки, дайджест против плавающего тега, почему удаление файла не убирает его из образа
- +Dockerfile: порядок инструкций, многостадийная сборка, ENTRYPOINT и CMD, exec-форма против shell-формы
- +Рантайм: изоляция namespaces и cgroups, лимиты памяти и код 137, чем опасны privileged и проброс сокета демона
- +Данные и сеть: тома против bind-mount, почему localhost внутри контейнера это он сам, что на самом деле делает EXPOSE
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- Dockerfile и сборка8
- Рантайм: изоляция и лимиты8
- Образы и слои7
- Сети и compose7
- Тома и данные7
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- Образы и слои Docker7 вопросов
- Dockerfile: кэш, ENTRYPOINT, multi-stage8 вопросов
- Рантайм Docker: изоляция, лимиты, код 1378 вопросов
- Тома и данные в Docker7 вопросов
- Сети Docker и compose7 вопросов
Примеры вопросов с разбором
- Почему манифест зависимостей копируют и ставят отдельной инструкцией, до COPY всего кода?A)Иначе установка пойдёт от root и сломает права на файлыB)Так сборщик распараллелит установку и копированиеC)Менеджер пакетов не видит файлы, скопированные позжеD)Правка кода не должна сбрасывать кэш слоя с зависимостями
показать ответ и разбор
+D)Правка кода не должна сбрасывать кэш слоя с зависимостями// разбор: Кэш слоя живёт, пока не изменились его входные данные. Если сначала скопировать весь проект, то любая правка исходника ломает кэш и тянет за собой переустановку зависимостей — минуты на каждую сборку. Копирование только манифеста, установка, затем копирование кода даёт стабильный тяжёлый слой снизу и дешёвый лёгкий сверху.
- Чем контейнер принципиально отличается от виртуальной машины?A)У контейнера своя урезанная ОС, а у ВМ — полнаяB)Контейнер работает без файловой системы, всё держит в памятиC)Процесс на ядре хоста в namespaces и cgroupsD)Контейнер запускается только из образа, а ВМ — из снапшота диска
показать ответ и разбор
+C)Процесс на ядре хоста в namespaces и cgroups// разбор: Контейнер не поднимает ядро и не эмулирует железо: это обычный процесс хоста, которому подменили видимость мира (namespaces: PID, сеть, точки монтирования, пользователи) и ограничили ресурсы (cgroups). Отсюда старт за миллисекунды и малый оверхед — и отсюда же граница безопасности слабее, чем у ВМ: ядро общее, дыра в нём касается всех контейнеров сразу.
- В compose приложение ходит к базе по адресу localhost:5432 и получает отказ. Как правильно?A)Обращаться по имени сервиса: db:5432B)Публиковать порт базы и ходить через адрес хостаC)Прописать IP контейнера базы в конфиг приложенияD)Запустить оба процесса в одном контейнере
показать ответ и разбор
+A)Обращаться по имени сервиса: db:5432// разбор: У каждого контейнера свой сетевой namespace: localhost внутри — это он сам, а не сосед. В сети compose работает встроенный DNS, который резолвит имя сервиса в адрес контейнера, поэтому строку подключения пишут как db:5432. IP не годится: он меняется при пересоздании. Публикация портов нужна только для доступа снаружи, между собой контейнеры общаются напрямую.
- Контейнер стартует и сразу останавливается со статусом Exited (0). Логов почти нет. Первая гипотеза?A)Не хватило памяти по лимиту, процесс убитB)Главный процесс вышел: например, демонизировалсяC)Образ собран под другую архитектуруD)Не смонтирован том с конфигом, приложение вышло по ошибке
показать ответ и разбор
+B)Главный процесс вышел: например, демонизировался// разбор: Контейнер живёт ровно столько, сколько живёт его главный процесс. Классика: приложение внутри уходит в фон (демонизируется), стартовый скрипт заканчивается, и контейнер честно завершается нулём. Такое же поведение у команды, которая отработала и вышла. Лечится запуском в переднем плане — daemon off у nginx, foreground-режим у сервиса.
- Пересоздали контейнер с базой — данные пропали. Что было сделано не так?A)Не выставлен restart policy, контейнер поднялся с нуляB)Образ пересобран, и слои перезаписали каталог данныхC)Данные лежали в слое записи контейнера, а не на томеD)Не сделан commit контейнера перед удалением
показать ответ и разбор
+C)Данные лежали в слое записи контейнера, а не на томе// разбор: Контейнер добавляет поверх слоёв образа тонкий слой записи — он живёт ровно столько же, сколько сам контейнер. Всё, что пишется в него, исчезает при rm. Состояние выносят на том (named volume) или bind-mount, а сам контейнер остаётся одноразовым. Заодно это позволяет спокойно катить новую версию образа поверх тех же данных.
- В образе ENTRYPOINT ["app"] и CMD ["--config=/etc/app.yml"]. Что выполнится при docker run образ --debug?A)app --debug: аргументы run заменяют CMDB)app --config=/etc/app.yml --debug: аргументы дописываютсяC)--debug: команда run заменяет ENTRYPOINT целикомD)app --config=/etc/app.yml: аргументы run без --entrypoint игнорируются
показать ответ и разбор
+A)app --debug: аргументы run заменяют CMD// разбор: ENTRYPOINT задаёт саму команду, CMD — аргументы по умолчанию к ней. Всё, что передано в docker run после образа, подставляется вместо CMD, а ENTRYPOINT остаётся. Потому в образах с ENTRYPOINT удобно держать в CMD дефолтные флаги: запуск без аргументов работает как задумано, а с аргументами — переопределяется одной строкой. Заменить сам ENTRYPOINT можно только флагом --entrypoint.
- Собрали и запушили app:latest, но на части нод крутится вчерашняя сборка. Как это чинят на уровне выкатки?A)Пинить образ по дайджесту или уникальному тегу сборкиB)Чистить кэш образов на нодах перед каждым релизомC)Перезапускать демон, чтобы он перечитал реестрD)Ставить тег latest дважды: до и после публикации
показать ответ и разбор
+A)Пинить образ по дайджесту или уникальному тегу сборки// разбор: Тег — движущаяся метка: она указывает на новый дайджест, но нода с уже скачанным latest не полезет за обновлением, если политика загрузки это позволяет. Дайджест (sha256:...) неизменен, уникальный тег вида app:2026.07.22-a1b2c3 тоже — по ним видно, что именно крутится, и откат становится осмысленным. Latest в проде оставляют разве что для локальных экспериментов.
- В Dockerfile указан EXPOSE 8080, но снаружи порт недоступен. Что делает EXPOSE?A)Открывает порт на хосте после запуска контейнераB)Это метаданные образа: публикацию делает -p или -P при запускеC)Разрешает контейнеру слушать этот порт, без него bind запрещёнD)Пробрасывает порт между контейнерами одной сети
показать ответ и разбор
+B)Это метаданные образа: публикацию делает -p или -P при запуске// разбор: EXPOSE — декларация в метаданных: она подсказывает, какой порт слушает приложение, и используется флагом -P для публикации на случайные порты хоста. Сама по себе она ничего не открывает. Наружу порт выводит -p host:container при запуске (или ports в compose). Между контейнерами одной сети связь идёт напрямую по порту приложения, без публикации.
- Контейнер завершился с кодом 137. Что это говорит и чем уточнить причину?A)Приложение вернуло 137 из своего кода выходаB)Провалился healthcheck, и оркестратор снял контейнерC)Ошибка в ENTRYPOINT: команда не найденаD)Процесс убит SIGKILL; смотреть флаг OOMKilled в inspect
показать ответ и разбор
+D)Процесс убит SIGKILL; смотреть флаг OOMKilled в inspect// разбор: 137 = 128 + 9, то есть процесс добит SIGKILL. Чаще всего это OOM по лимиту памяти, но так же выглядит и добивание после истечения grace-периода остановки, когда приложение не обработало SIGTERM. Различают по docker inspect: State.OOMKilled true и записи ядра в dmesg против обычной остановки. Дальше — либо лимит и утечка, либо корректная обработка сигнала.
это 9 из 37
Ещё 28 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Чем контейнер отличается от виртуальной машины?
Контейнер это процесс на ядре хоста, которому подменили видимость системы через namespaces и ограничили ресурсы через cgroups. Виртуалка поднимает своё ядро на гипервизоре. Отсюда мгновенный старт у контейнера и более слабая граница безопасности.
Что означает код выхода 137?
Процесс добит сигналом KILL: 128 плюс 9. Чаще всего это нехватка памяти по лимиту, но так же выглядит добивание после таймаута остановки, когда приложение не обработало SIGTERM. Различают по флагу OOMKilled в inspect.