10 KiB
Вопросы: Docker
Опираются на кейс: CASE.md. Источники — VK Cloud.md, Реалист банк.md.
В чём разница между образом (image) и контейнером?
Образ — неизменяемый шаблон: слоёная файловая система + метаданные (какая команда запускается по умолчанию, какие переменные окружения и т.д.), результат docker build. Контейнер — запущенный (или остановленный) экземпляр образа с добавленным поверх write-слоем, где живут любые runtime-изменения — по сути процесс на хосте плюс собственная изолированная файловая система, сеть и т.д. Один образ может породить много независимых контейнеров.
В чём отличия виртуализации от контейнеризации?
Виртуализация эмулирует отдельное «железо» через гипервизор — у каждой VM своё полноценное ядро ОС, изоляция максимальная, но накладные расходы выше (полный загруженный образ ОС, медленнее старт). Контейнеризация — все контейнеры хоста делят одно и то же ядро хоста; изоляция достигается через namespaces (что процесс видит — свои PID, сеть, файловую систему) и ограничение ресурсов через cgroups (сколько он может использовать), см. ../linux-bash/QUESTIONS.md. Контейнеры легче и стартуют за секунды, но изоляция менее строгая (общее ядро — потенциальная поверхность атаки при уязвимостях в ядре).
Как Docker понимает, что контейнер умер, и помечает его как exit?
Внутри Docker Engine работает containerd — реальный рантайм, управляющий процессами контейнеров. Он отслеживает главный процесс контейнера (PID 1 внутри контейнерного namespace); как только этот процесс завершается (штатно или с ошибкой), это фиксируется как событие завершения, и статус контейнера меняется на Exited с соответствующим exit-кодом (docker inspect --format '{{.State.ExitCode}}').
Что такое Docker runtime и зачем нужен containerd?
Runtime — компонент, который непосредственно создаёт и управляет изолированными процессами контейнеров на уровне ОС (namespaces, cgroups, файловая система из слоёв образа). containerd — низкоуровневый рантайм, на котором построен сам Docker Engine: Docker CLI/демон — это удобная надстройка (сборка образов, compose, registry-взаимодействие) поверх containerd, который непосредственно исполняет работу по управлению жизненным циклом контейнеров. Это разделение позволяет другим системам (например, Kubernetes через CRI) использовать containerd напрямую, без полного Docker Engine.
Как зайти внутрь контейнера?
docker exec -it <container> sh # или bash, если есть в образе
exec запускает новый процесс внутри уже работающего контейнера (в отличие от docker run, который создаёт новый контейнер). Если контейнер уже остановлен, exec не сработает — нужно либо запустить его заново, либо использовать docker cp для файлов без запуска процесса внутри.
Как скопировать файл в работающий контейнер?
docker cp ./local-file.txt <container>:/path/in/container
docker cp <container>:/path/in/container ./local-file.txt # и в обратную сторону, из контейнера на хост
Если убить процесс Docker (сам демон) — запущенные контейнеры сдохнут?
Нет, не обязательно — сами контейнеры управляются containerd, а не напрямую демоном dockerd; при перезапуске dockerd уже запущенные контейнеры продолжают работать (это осознанное архитектурное решение, начиная с версии Docker, где Docker Engine был разделён с containerd — раньше, в старых версиях, где всё было завязано на монолитный демон, перезапуск демона действительно ронял контейнеры). После рестарта dockerd он заново подключается к уже работающим контейнерам через containerd и восстанавливает их видимость в docker ps.
Как работает сеть в Docker?
По умолчанию Docker создаёт bridge-сеть (docker0 для отдельных контейнеров, отдельная сеть на проект в Docker Compose), контейнеры получают виртуальные сетевые интерфейсы, подключённые к этому мосту, и приватные IP внутри него. Docker поднимает встроенный DNS внутри пользовательских (non-default) bridge-сетей — контейнеры резолвят друг друга по имени сервиса/контейнера, а не по IP. Наружу трафик пробрасывается через -p host_port:container_port, что настраивает NAT-правила (iptables) для перенаправления с порта хоста на IP контейнера внутри моста.
Много ли процессов внутри одного контейнера? У контейнеров есть PID?
Технически контейнер может запускать сколько угодно процессов (например, если ENTRYPOINT — скрипт, который стартует несколько дочерних процессов), но общепринятая практика — один основной процесс на контейнер (принцип "один контейнер — одна ответственность"), чтобы жизненный цикл контейнера чётко соответствовал жизненному циклу этого процесса. У контейнера есть PID — как у любого обычного процесса хоста (см. docker inspect --format '{{.State.Pid}}' в кейсе), плюс внутри контейнера, благодаря PID namespace, главный процесс видит себя как PID 1 в своём изолированном пространстве.
Entrypoint vs CMD — в чём разница?
ENTRYPOINT — фиксированная главная команда контейнера, CMD — аргументы к ней по умолчанию, которые можно переопределить, просто передав другие аргументы в docker run image <новые аргументы>. Если задан только CMD без ENTRYPOINT, вся команда целиком переопределяется тем, что передано в docker run. Практическая польза разделения — можно сделать образ, у которого фиксирована сама программа (ENTRYPOINT ["myapp"]), но легко менять флаги запуска без пересборки образа.
Разница docker ps, docker ps -a, docker logs, docker images?
docker ps — только работающие контейнеры. docker ps -a — вообще все контейнеры, включая остановленные/завершившиеся. docker logs <container> — стандартный вывод (stdout/stderr) процесса внутри контейнера, накопленный с момента старта (-f — читать в реальном времени, как tail -f). docker images — список локально скачанных/собранных образов (в отличие от контейнеров — это шаблоны, а не запущенные экземпляры).
Как сохранить логи из контейнера?
docker logs <container> > output.log — перенаправить stdout вывод команды в файл на хосте. Если логи пишутся не в stdout, а в файл внутри контейнера — docker cp <container>:/path/to/log.file ./log.file. Для постоянного сохранения логов за пределами жизни контейнера в проде обычно настраивают logging driver (например, json-file с ротацией, или отправку в централизованную систему — см. ../elk/CASE.md) вместо ручного копирования.