Контейнеры и оркестрация
Образы и контейнеры Docker, сетевое взаимодействие, базовые объекты Kubernetes и OpenShift.
20 вопросов
JuniorТеорияОчень частоЧто такое контейнеры и в чём их главное преимущество перед запуском прямо на хосте?
Что такое контейнеры и в чём их главное преимущество перед запуском прямо на хосте?
Контейнер упаковывает приложение с его зависимостями и запускает как изолированный процесс, разделяющий ядро хоста, — используя namespaces для изоляции и cgroups для ограничения ресурсов. Главное преимущество — согласованность и плотность: один и тот же образ работает одинаково в разных средах с куда меньшими накладными расходами и быстрее запускается, чем полноценная виртуальная машина.
Типичные ошибки
- ✗Называть контейнер ВМ — он делит ядро хоста, а не грузит своё
- ✗Думать, что контейнеры дают более сильную изоляцию, чем ВМ (она обычно слабее — общее ядро)
- ✗Считать, что контейнер может бесплатно эмулировать другую архитектуру CPU
Уточняющие вопросы
- →Какие возможности ядра Linux (namespaces, cgroups) делают изоляцию контейнеров возможной?
- →Когда вы всё же выберете виртуальную машину вместо контейнера?
JuniorТеорияОчень частоВ чём разница между Docker-образом и Docker-контейнером?
В чём разница между Docker-образом и Docker-контейнером?
Образ — это неизменяемый слоистый шаблон: упакованная ФС плюс метаданные из Dockerfile. Контейнер — это запущенный (или остановленный) экземпляр образа: движок добавляет сверху тонкий слой для записи и запускает процесс. Один образ даёт много изолированных контейнеров, а записи идут в слой контейнера и исчезают при его удалении.
Типичные ошибки
- ✗Говорить, что контейнер только для чтения, а образ записываемый — всё наоборот
- ✗Думать, что изменения внутри контейнера автоматически сохраняются в образ
- ✗Считать, что каждый контейнер запускает своё ядро как ВМ, а не делит ядро хоста
Уточняющие вопросы
- →Как слои образа обеспечивают кэширование и совместное использование между образами?
- →Что происходит с данными внутри контейнера при его удалении и как volume это меняет?
JuniorТеорияОчень частоЧто такое Linux namespace и какие namespaces изолируют контейнер?
Что такое Linux namespace и какие namespaces изолируют контейнер?
namespace — это возможность ядра дать группе процессов свой изолированный экземпляр одного глобального ресурса, чтобы она видела только своё. Контейнер сочетает несколько: PID (процессы), net (интерфейсы), mnt (монтирования), uts (hostname), ipc и user (UID).
Типичные ошибки
- ✗Путать namespace (изолирует вид) с cgroup (ограничивает ресурсы)
- ✗Думать, что один namespace покрывает всё, а не несколько — по одному на ресурс
- ✗Считать, что namespace грузит отдельное ядро, а не делит ядро хоста
Уточняющие вопросы
- →Как user namespace позволяет root контейнера маппиться на непривилегированный UID хоста?
- →Почему PID namespace делает первый процесс контейнера процессом PID 1?
MiddleТеорияОчень частоЧто такое Pod в Kubernetes и как Deployment и Service связаны с ним?
Что такое Pod в Kubernetes и как Deployment и Service связаны с ним?
Pod — это наименьшая развёртываемая единица: один и более совместно размещённых контейнеров, делящих namespace и хранилище. Deployment через ReplicaSet держит заданное число одинаковых Pod и делает плавающие обновления. Service даёт эфемерным Pod один стабильный виртуальный IP и DNS-имя, балансируя между ними.
Типичные ошибки
- ✗Приравнивать Pod к одному контейнеру, а не к одному и более, делящим namespace
- ✗Думать, что у Pod стабильный IP, а не что стабильную точку даёт Service
- ✗Путать Deployment (управляет репликами и выкатками) с разовым Job
Уточняющие вопросы
- →Почему контейнеры в одном Pod достигают друг друга через
localhost? - →Как Service выбирает, на какие Pod маршрутизировать, и какова роль меток (labels)?
JuniorТеорияЧастоЧто такое реестр контейнеров и что происходит при docker push и docker pull?
Что такое реестр контейнеров и что происходит при docker push и docker pull?
Реестр хранит и раздаёт образы, адресуемые как repository:tag. docker push загружает манифест и каждый слой, пропуская те, что уже есть в реестре по дайджесту. docker pull берёт манифест, затем скачивает и собирает недостающие слои.
Типичные ошибки
- ✗Думать, что push/pull передают исходники, а не готовые слои образа
- ✗Считать, что каждый слой передаётся заново, даже если он уже есть в реестре
- ✗Забывать, что приватный реестр требует docker login до push или pull
Уточняющие вопросы
- →Как адресация по содержимому позволяет push и pull пропускать уже существующие слои?
- →В чём разница ссылки на образ по тегу и по дайджесту?
JuniorТеорияЧастоЧем COPY отличается от ADD в Dockerfile и что предпочесть?
Чем COPY отличается от ADD в Dockerfile и что предпочесть?
COPY копирует файлы и каталоги из контекста в образ. ADD делает то же плюс два эффекта: авто-распаковывает tar-архив и умеет качать по URL. Предпочитайте COPY; берите ADD только ради распаковки tar, а URL качайте через RUN curl.
Типичные ошибки
- ✗Менять их местами — распаковывает tar и качает URL именно ADD, а не COPY
- ✗Использовать ADD для простого копирования, где COPY яснее и безопаснее
- ✗Качать URL через ADD вместо RUN curl, который можно проверить
Уточняющие вопросы
- →Почему скачивание URL через ADD вредит переиспользованию кэша и воспроизводимости?
- →Когда авто-распаковка tar у ADD действительно уместна?
MiddleТеорияЧастоЧто ограничивают cgroups и чем это отличается от того, что изолируют namespaces?
Что ограничивают cgroups и чем это отличается от того, что изолируют namespaces?
cgroups управляют тем, сколько группа процессов может потребить — ограничивают CPU, память, PID и I/O. Namespaces управляют тем, что она видит. Превышение memory cgroup ведёт к OOM kill; достижение лимита CPU троттлит процесс, а не убивает его.
Типичные ошибки
- ✗Менять роли — namespaces изолируют вид, cgroups ограничивают ресурсы
- ✗Думать, что достижение лимита CPU убивает процесс, а не троттлит его
- ✗Считать, что превышение лимита памяти тихо игнорируется, а не ведёт к OOM kill
Уточняющие вопросы
- →Как рантайм контейнера отображает флаг
--memoryна memory cgroup? - →Что изменилось между cgroup v1 и v2 в организации лимитов?
MiddleКодЧастоПерепишите этот Dockerfile для Node, применив best practices контейнеров.
Перепишите этот Dockerfile для Node, применив best practices контейнеров.
Запините базу (node:20.11-slim), затем COPY package*.json и npm ci --omit=dev до COPY . ., чтобы правка только кода переиспользовала кэш слоя зависимостей. Добавьте .dockerignore для node_modules/.git, чтобы контекст был мал, и завершите USER node для запуска от non-root.
Типичные ошибки
- ✗Копировать код до манифеста — npm install перезапускается при каждой правке кода
- ✗Оставлять базу на плавающем теге
node/latestвместо пина - ✗Работать от root и слать node_modules в контекст без .dockerignore
Уточняющие вопросы
- →Почему
npm ciпредпочтительнееnpm installв воспроизводимой сборке? - →Что даёт пин базового образа по дайджесту сверх пина по тегу?
MiddleПроизводительностьЧастоОбраз весит 1,2 ГБ. Какие приёмы его уменьшат и в каком порядке?
Образ весит 1,2 ГБ. Какие приёмы его уменьшат и в каком порядке?
Начните с крупнейших выигрышей: multi-stage-сборка, чтобы компилятор и SDK не попадали в финальный образ, и меньшая база (slim, alpine, distroless). Затем чистите слои — объединяйте RUN и чистите кэш пакетов в том же слое. Добавьте .dockerignore и уберите непоставляемые зависимости.
Типичные ошибки
- ✗Удалять файлы в позднем слое — байты остаются в раннем слое, что их добавил
- ✗Думать, что сплющивание или быстрое железо уменьшают образ, а не меньшая база
- ✗Разбивать каждую команду в свой слой вместо объединения и очистки в одном
Уточняющие вопросы
- →Почему
rmв позднем слое RUN не возвращает место, добавленное ранним слоем? - →Что убирает distroless против alpine и чем каждый жертвует?
MiddleКодЧастоНапишите multi-stage Dockerfile, дающий минимальный финальный образ для Go-приложения.
Напишите multi-stage Dockerfile, дающий минимальный финальный образ для Go-приложения.
Сборщик на golang:1.22 копирует go.mod/go.sum, делает go mod download, затем собирает статический бинарник. Финальная стадия — FROM gcr.io/distroless/static и делает COPY --from=builder только бинарника, от non-root. Образ без компилятора и кода, поэтому весит единицы мегабайт.
Типичные ошибки
- ✗Удалять тулчейн в позднем слое вместо отдельной финальной стадии
- ✗Копировать код до go.mod/go.sum — кэш загрузки модулей ломается при правках
- ✗Думать, что Go-бинарнику нужен тулчейн в рантайме для исполнения
Уточняющие вопросы
- →Почему статически слинкованный Go-бинарник работает на
scratch/distroless без libc? - →Чем отличалась бы аналогичная multi-stage-сборка Node, раз у неё нет статического бинарника?
JuniorТеорияИногдаКак контейнер изолирует процесс через namespaces и cgroups, разделяя ядро хоста?
Как контейнер изолирует процесс через namespaces и cgroups, разделяя ядро хоста?
Контейнер — это обычный процесс хоста, ограниченный namespaces: каждый даёт приватный вид одного ресурса ядра (PID, монтирования, сеть), а cgroups ограничивают его CPU, память и I/O. Он делит ядро хоста без гостевой ОС, поэтому остаётся лёгким.
Типичные ошибки
- ✗Называть контейнер ВМ — это процесс хоста, делящий ядро, а не гостевая ОС
- ✗Менять роли местами — namespaces изолируют вид, cgroups ограничивают ресурсы
- ✗Думать, что изоляция сильнее, чем у ВМ, тогда как общее ядро делает её слабее
Уточняющие вопросы
- →Какие конкретно namespaces изолируют контейнер и что виртуализирует каждый?
- →Что происходит, когда процесс превышает лимит памяти в своём cgroup?
JuniorТеорияИногдаЧто делают основные инструкции Dockerfile — FROM, RUN, COPY, ENTRYPOINT и CMD?
Что делают основные инструкции Dockerfile — FROM, RUN, COPY, ENTRYPOINT и CMD?
FROM задаёт базовый образ для дальнейших слоёв. RUN выполняет команду при сборке и фиксирует новый слой. COPY добавляет файлы из контекста. ENTRYPOINT фиксирует исполняемый файл, запускаемый при старте, а CMD даёт аргументы по умолчанию, переопределяемые docker run.
Типичные ошибки
- ✗Путать RUN (этап сборки) с командой запуска контейнера
- ✗Думать, что COPY выполняет команду, а не добавляет файлы из контекста
- ✗Путать ENTRYPOINT (фиксированный исполняемый файл) с CMD (переопределяемые умолчания)
Уточняющие вопросы
- →Почему каждый RUN создаёт отдельный слой и когда их стоит объединять?
- →В чём разница exec-формы и shell-формы у CMD?
JuniorТеорияИногдаЧто такое слой образа и как union-файловая система (overlay) объединяет слои?
Что такое слой образа и как union-файловая система (overlay) объединяет слои?
Каждая меняющая ФС инструкция Dockerfile добавляет один read-only слой, опознаваемый по дайджесту. Union-ФС, например overlay, складывает их в один вид, где верхний файл перекрывает путь ниже; контейнер добавляет тонкий записываемый слой.
Типичные ошибки
- ✗Думать, что каждый слой — полная копия образа, а не адресуемый по содержимому дифф
- ✗Считать, что записываемый слой — часть образа, а не добавлен в рантайме
- ✗Полагать, что слои нельзя разделять или кэшировать между образами
Уточняющие вопросы
- →Как адресация по содержимому позволяет двум образам делить общую базу?
- →Почему удаление файла в позднем слое не уменьшает образ?
MiddleТеорияИногдаКак контейнеры Docker общаются по умолчанию и как опубликовать порт?
Как контейнеры Docker общаются по умолчанию и как опубликовать порт?
По умолчанию контейнер попадает в сеть bridge; контейнеры на пользовательском bridge достигают друг друга по имени контейнера через встроенный DNS Docker. Чтобы открыть порт контейнера хосту, его публикуют: -p 8080:80 пробрасывает порт хоста 8080 на порт контейнера 80. Режим host снимает изоляцию и напрямую делит сетевой стек хоста.
Типичные ошибки
- ✗Путать порядок
-p host:container— порт хоста идёт первым - ✗Ждать разрешения по имени на дефолтном bridge вместо пользовательской сети
- ✗Думать, что режим
hostусиливает изоляцию, тогда как он убирает сетевой namespace
Уточняющие вопросы
- →Почему DNS по имени работает на пользовательском bridge, но не на дефолтном
bridge? - →Во что разрешается
host.docker.internalи когда он нужен?
MiddleДебаггингИногдаКонтейнер сразу выходит с кодом 0 — определите проблему запуска.
Контейнер сразу выходит с кодом 0 — определите проблему запуска.
Код выхода 0 значит, что главный процесс успешно завершился — он не упал. CMD запускает разовую миграцию, которая отрабатывает и возвращается, а контейнер живёт лишь пока жив его PID 1. Сделайте PID 1 долгоживущим сервером, а миграцию вынесите отдельным init-шагом.
Типичные ошибки
- ✗Читать выход 0 как падение, а не как чистое завершение главного процесса
- ✗Делать PID 1 разовой командой вместо долгоживущего сервера
- ✗Держать контейнер живым через sleep вместо запуска самого сервиса как CMD
Уточняющие вопросы
- →Как код выхода 127 вместо 0 изменил бы вашу диагностику?
- →Где запускать миграцию в Kubernetes-деплое — init-контейнер или Job?
MiddleТеорияИногдаКак кэш сборки решает переиспользовать слой и как упорядочить Dockerfile ради попаданий в кэш?
Как кэш сборки решает переиспользовать слой и как упорядочить Dockerfile ради попаданий в кэш?
Билдер опирает ключ кэша слоя на текст инструкции плюс, для COPY/ADD, сумму копируемых файлов. Первый промах инвалидирует все слои после него. Поэтому от стабильного к изменчивому: ставьте зависимости до копирования кода, чтобы правка не ломала слой зависимостей.
Типичные ошибки
- ✗Копировать весь код до установки зависимостей — слой зависимостей ломается при каждой правке
- ✗Думать, что промах инвалидирует лишь ту инструкцию, а не все слои после неё
- ✗Считать, что кэш опирается на метки времени, а не на текст инструкции и суммы файлов
Уточняющие вопросы
- →Почему
COPY package.jsonдоCOPY . .переживает правку только исходников? - →Как файл
.dockerignoreменяет то, что хешируетCOPY . .?
MiddleТеорияИногдаЧем ENTRYPOINT отличается от CMD в Dockerfile и как они сочетаются?
Чем ENTRYPOINT отличается от CMD в Dockerfile и как они сочетаются?
ENTRYPOINT задаёт исполняемый файл, который контейнер запускает всегда; CMD даёт аргументы по умолчанию. В exec-форме CMD дописывается как аргументы к ENTRYPOINT. Аргументы после имени образа в docker run переопределяют CMD, но не ENTRYPOINT. Так ENTRYPOINT фиксирует программу, а CMD даёт переопределяемые умолчания.
Типичные ошибки
- ✗Менять роли местами — ENTRYPOINT это фиксированная программа, CMD это аргументы по умолчанию
- ✗Думать, что аргументы
docker runпереопределяют ENTRYPOINT, а не CMD - ✗Считать, что они выполняются на этапе сборки, а не при старте контейнера
Уточняющие вопросы
- →Чем exec-форма ENTRYPOINT отличается от shell-формы и почему предпочесть exec?
- →Как
docker run --entrypointменяет поведение по умолчанию?
MiddleТеорияИногдаЧто такое сканирование образов контейнеров и где оно встраивается в пайплайн?
Что такое сканирование образов контейнеров и где оно встраивается в пайплайн?
Сканер осматривает слои образа, перечисляет установленные пакеты ОС и библиотек и сверяет их версии с базами CVE, отмечая известные уязвимости. Запускайте его в CI, валите на high или critical и пересканируйте в реестре, ведь новые CVE появляются после отправки образа.
Типичные ошибки
- ✗Думать, что скан — это монитор трафика в рантайме, а не проверка версий пакетов по CVE
- ✗Сканировать лишь раз при сборке и не пересканировать по мере новых CVE
- ✗Считать, что сборку не надо валить на находках high или critical
Уточняющие вопросы
- →Почему образ нужно пересканировать в реестре, даже если он прошёл при сборке?
- →Как не давать сканеру блокировать релизы на неустранимых CVE базового образа?
MiddleДизайнИногдаПолиглот-организация выпускает сервисы на Go, Java и Node. Каждая команда пишет свой Dockerfile, а образы весят 800 МБ — 1,5 ГБ, тащат компиляторы и пакетные менеджеры в прод и валят сканер безопасности на CVE тулчейна. Сборки медленные, и каждая команда пинит свой базовый образ. Вас просят предложить стандартную стратегию сборки, которая уменьшит образы, уберёт сборочный инструментарий из рантайма и останется единой для трёх языков, не навязывая всем один runtime. Что предложите и почему это работает?
Полиглот-организация выпускает сервисы на Go, Java и Node. Каждая команда пишет свой Dockerfile, а образы весят 800 МБ — 1,5 ГБ, тащат компиляторы и пакетные менеджеры в прод и валят сканер безопасности на CVE тулчейна. Сборки медленные, и каждая команда пинит свой базовый образ. Вас просят предложить стандартную стратегию сборки, которая уменьшит образы, уберёт сборочный инструментарий из рантайма и останется единой для трёх языков, не навязывая всем один runtime. Что предложите и почему это работает?
Стандартизируйте multi-stage-сборки: стадия-сборщик несёт SDK и собирает артефакт, затем тонкая финальная делает COPY --from=builder только его на минимальную или distroless-базу. Образ рантайма без компилятора и пакетного менеджера, поэтому мал и проходит сканер.
Типичные ошибки
- ✗Думать, что сплющивание слоёв убирает инструментарий — оно оставляет файлы, лишь в одном слое
- ✗Поставлять SDK в прод вместо копирования вперёд только собранного артефакта
- ✗Считать, что быстрое CI-железо уменьшает финальный образ, а не стратегия сборки
Уточняющие вопросы
- →Как
COPY --fromпозволяет финальной стадии взять лишь артефакт и отбросить тулчейн? - →Когда distroless-база предпочтительнее alpine и чем она жертвует?
MiddleТеорияРедкоЧто такое rootless Docker и в чём его главное преимущество для безопасности?
Что такое rootless Docker и в чём его главное преимущество для безопасности?
Rootless Docker запускает демон и контейнеры от непривилегированного пользователя, маппя root контейнера (UID 0) на непривилегированный UID хоста через user namespace. Выгода — меньший радиус поражения: побег или компрометация демона оказываются обычным юзером хоста, а не root. Часть функций ограничена.
Типичные ошибки
- ✗Думать, что демон всё ещё идёт от root хоста, тогда как rootless уводит его от root
- ✗Путать rootless с простым добавлением строки USER внутри образа
- ✗Считать, что это бесплатно, а не ограничивает часть привилегированных функций
Уточняющие вопросы
- →Как user namespace маппит UID 0 контейнера на непривилегированный UID хоста?
- →Какие функции Docker становятся сложнее или недоступны в rootless-режиме?