← К программе курса

Модуль 1 / 18

Контейнерная модель и архитектура Docker Engine

DOCKER

Содержание
01

1. Цели и результат модуля

После модуля вы сможете объяснить контейнер как изолированный процесс Linux, проследить запрос от Docker CLI до OCI runtime, различать image и container и диагностировать завершившийся контейнер по фактам.

  • контейнер и виртуальная машина — разные модели изоляции
  • Docker Engine, containerd и runc решают разные задачи
  • namespaces ограничивают видимость, cgroups — потребление ресурсов
  • writable layer не является постоянным хранилищем
  • жизненный цикл контейнера определяется главным процессом
02

2. Теория: контейнер — это процесс, а не виртуальная машина

Контейнер не эмулирует отдельный компьютер и обычно не загружает собственное ядро. На Linux его процессы выполняются общим ядром хоста, но получают изолированное представление части системных ресурсов. Image добавляет файловую систему и метаданные запуска; runtime создаёт процесс с нужными namespaces, cgroups и security-настройками.

Виртуальная машина имеет гостевую ОС и собственное ядро. Контейнер запускается быстрее и имеет меньший overhead, но разделяет kernel attack surface с хостом. Поэтому контейнеризация сама по себе не равна полноценной границе безопасности.

  • image — неизменяемый шаблон слоёв и конфигурации
  • container — экземпляр image с runtime-конфигурацией и writable layer
  • process — реальная единица выполнения для ядра хоста
  • volume — данные с жизненным циклом, независимым от writable layer
03

3. Теория: архитектура Docker Engine

Docker CLI отправляет запрос в Docker API. Docker daemon управляет images, containers, networks и volumes и передаёт управление жизненным циклом в containerd. По умолчанию containerd использует runc как OCI runtime для создания контейнерного процесса.

После запуска runc завершается. containerd shim сохраняет связь с работающим контейнером, его вводом-выводом и exit status. Встроенный containerd Docker Engine принадлежит Engine: его внутренний socket и namespace не являются стабильным общим API.

  • Docker CLI — клиент API
  • dockerd — control plane одного Engine
  • containerd — lifecycle и runtime content
  • runc — реализация OCI Runtime Specification
  • shim — сопровождение запущенного контейнера
04

4. Теория: image и файловая система контейнера

Image состоит из адресуемых по содержимому read-only слоёв и конфигурации. При create Docker монтирует root filesystem из слоёв и добавляет writable layer конкретного контейнера. Изменение файла не переписывает image.

docker stop сохраняет writable layer, а docker rm удаляет его вместе с контейнером. Важные данные размещают в volume или внешнем хранилище. Ручной docker commit не заменяет воспроизводимый Dockerfile и pipeline сборки.

05

5. Теория: namespaces, cgroups и security

PID namespace меняет видимое дерево процессов, mount — точки монтирования, network — интерфейсы и маршруты, UTS — hostname, IPC — объекты обмена, user namespace — отображение UID/GID. Namespace ограничивает видимость, но не задаёт ресурсный лимит.

Cgroups учитывают и ограничивают CPU, memory и PIDs. Дополнительную защиту дают capabilities, seccomp, AppArmor или SELinux, read-only filesystem и запуск без root. --pid=host, --network=host и особенно --privileged ослабляют изоляцию и требуют отдельного обоснования.

docker stats показывает удобные runtime-метрики. На Linux CLI вычитает page cache из memory usage, поэтому его число может отличаться от сырого Docker API или cgroup-файлов.

06

6. Теория: жизненный цикл и PID 1

docker run концептуально объединяет pull при необходимости, create и start. Контейнер работает, пока работает главный процесс. Завершение PID 1 останавливает контейнер, даже если приложение пыталось отправить дочерний процесс в фон.

docker stop посылает stop signal, обычно SIGTERM, ждёт timeout, затем использует SIGKILL. Приложение должно перестать принимать работу и завершиться в пределах бюджета. Exec-form запуска и корректный init делают доставку сигналов и сбор zombie-процессов предсказуемыми.

  • created — конфигурация есть, процесс не запущен
  • running — главный процесс выполняется
  • paused — процессы заморожены
  • exited — процесс завершён, metadata сохранены
  • dead — daemon не смог корректно завершить удаление
07

7. Методические указания

  • Начните с docker context show: клиент может быть подключён не к локальному Engine
  • Перед командой сформулируйте ожидаемую смену state
  • До docker rm собирайте logs, inspect и events
  • В автоматизации используйте --format или JSON, а не табличный вывод
  • Не применяйте --privileged и не монтируйте docker.sock в этой лаборатории
  • Cleanup выполняйте только после проверки точных имён

Лаборатория рассчитана на Linux Engine. Docker Desktop размещает Engine внутри управляемой VM, поэтому host PID и пути файловой системы относятся к этой VM, а не напрямую к macOS или Windows.

08

8. Лабораторная работа: lifecycle и изоляция

Зафиксируйте версии клиента и сервера, context, storage driver и cgroup version. Выполните create и start отдельно, затем исследуйте процессы, ресурсы и writable layer.

terminal
docker version
docker info
docker context show
docker system df
docker container ls --all
terminal
docker pull alpine:3.21
docker create --name docker-m1 alpine:3.21 sh -c 'trap "exit 0" TERM; while :; do sleep 1; done'
docker start docker-m1
docker container ls --filter name=docker-m1
docker inspect docker-m1
docker top docker-m1
docker exec docker-m1 sh -c 'printf "pid=%s host=%s\n" "$$" "$(hostname)"'
docker stats docker-m1 --no-stream
docker stop --timeout 10 docker-m1
docker inspect --format '{{json .State}}' docker-m1
terminal
docker start docker-m1
docker inspect --format 'container_pid={{.State.Pid}}' docker-m1
docker exec docker-m1 ps
docker exec docker-m1 cat /proc/1/status
docker exec docker-m1 sh -c 'printf "created in writable layer\n" > /lesson.txt'
docker diff docker-m1
docker stop docker-m1
docker start docker-m1
docker exec docker-m1 cat /lesson.txt
docker rm --force docker-m1

Критерий: вы наблюдали created → running → exited, получили exitCode 0, увидели разные host/container PID и доказали, почему файл переживает stop/start, но исчезает вместе с container.

09

9. Диагностика: контейнер сразу завершился

Процесс упражнения пишет причину в stderr и возвращает код 42. Не запускайте его повторно вслепую: восстановите timeline и отделите ошибку приложения от ошибки runtime.

terminal
docker run --name docker-m1-failure alpine:3.21 sh -c 'printf "fatal: configuration rejected\n" >&2; exit 42'
docker container ls --all --filter name=docker-m1-failure
docker logs docker-m1-failure
docker inspect --format 'status={{.State.Status}} exit={{.State.ExitCode}} error={{.State.Error}} finished={{.State.FinishedAt}}' docker-m1-failure
docker events --since 10m --until 0s --filter container=docker-m1-failure
docker rm docker-m1-failure
  • docker ps -a показывает существование и грубый state
  • inspect .State содержит exit code, timestamps, OOMKilled и runtime error
  • logs читает stdout/stderr, если logging driver это поддерживает
  • events связывает create, start, die и destroy
  • exit code 42 задан приложением, а не Docker daemon
10

10. Контроль, критерии освоения и шпаргалка

  • Почему контейнер не является маленькой VM?
  • Что разделяют dockerd, containerd и runc?
  • Чем image layers отличаются от writable layer?
  • Почему Running не означает готовность сервиса?
  • Что будет после завершения PID 1 с кодом 0?
  • Какие evidence нужны до удаления failed container?
  • Почему --privileged не исправление Permission denied?
terminal
docker container ls --all
docker image ls alpine:3.21
docker system df
docker container prune --filter until=24h

Модуль освоен, если вы объясняете путь CLI → API → dockerd → containerd → OCI runtime, прогнозируете жизненный цикл процесса и данных и собираете evidence до cleanup.