Модуль 1 / 18
Контейнерна модель і архітектура Docker Engine
Зміст
1. Цілі та результат модуля
Після модуля ви зможете пояснити контейнер як ізольований процес Linux, простежити запит від Docker CLI до OCI runtime, розрізняти image і container та діагностувати завершений контейнер за фактами.
- контейнер і віртуальна машина — різні моделі ізоляції
- Docker Engine, containerd і runc розв’язують різні задачі
- namespaces обмежують видимість, cgroups — споживання ресурсів
- writable layer не є постійним сховищем
- життєвий цикл контейнера визначає головний процес
2. Теорія: контейнер — це процес, а не віртуальна машина
Контейнер не емулює окремий комп’ютер і зазвичай не завантажує власне ядро. На Linux його процеси виконуються спільним ядром хоста, але отримують ізольоване представлення частини системних ресурсів. Image додає файлову систему й метадані запуску; runtime створює процес із потрібними namespaces, cgroups і security-налаштуваннями.
Віртуальна машина має гостьову ОС і власне ядро. Контейнер запускається швидше й має менший overhead, але поділяє kernel attack surface із хостом. Тому контейнеризація сама по собі не дорівнює повноцінній межі безпеки.
- image — незмінний шаблон шарів і конфігурації
- container — екземпляр image з runtime-конфігурацією та writable layer
- process — реальна одиниця виконання для ядра хоста
- volume — дані з життєвим циклом, незалежним від writable layer
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 — супровід запущеного контейнера
4. Теорія: image і файлова система контейнера
Image складається з адресованих за вмістом read-only шарів і конфігурації. Під час create Docker монтує root filesystem із шарів та додає writable layer конкретного контейнера. Зміна файла не переписує image.
docker stop зберігає writable layer, а docker rm видаляє його разом із контейнером. Важливі дані розміщують у volume або зовнішньому сховищі. Ручний docker commit не замінює відтворюваний Dockerfile і pipeline збирання.
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-файлів.
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 не зміг коректно завершити видалення
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.
8. Лабораторна робота: lifecycle та ізоляція
Зафіксуйте версії клієнта й сервера, context, storage driver і cgroup version. Виконайте create і start окремо, потім дослідіть процеси, ресурси та writable layer.
docker version
docker info
docker context show
docker system df
docker container ls --alldocker 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-m1docker 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.
9. Діагностика: контейнер одразу завершився
Процес вправи пише причину в stderr і повертає код 42. Не запускайте його повторно навмання: відновіть timeline і відокремте помилку застосунку від помилки runtime.
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. Контроль, критерії засвоєння та шпаргалка
- Чому контейнер не є маленькою VM?
- Що розділяють dockerd, containerd і runc?
- Чим image layers відрізняються від writable layer?
- Чому Running не означає готовність сервісу?
- Що буде після завершення PID 1 з кодом 0?
- Які evidence потрібні до видалення failed container?
- Чому --privileged не виправлення Permission denied?
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.