Архитектура Docker
Общая картина
В предыдущей главе Docker выглядел как одна команда: docker run .... Но внутри это не просто локальная программа, которая сама всё делает. Когда ты набираешь команду в терминале, она уходит в Docker Engine, а уже он создаёт контейнеры, хранит образы, общается с registry (реестром образов) и управляет сетью.
Зачем это понимать? Когда docker run не сработал, причина может быть на разных уровнях: командная строка Docker (CLI) не смогла достучаться до демона, демон не смог скачать образ, реестр недоступен, контейнер создался, но процесс внутри сразу завершился. Архитектура помогает не гадать, а понимать, где искать проблему.
Важная идея: Docker - это несколько слоёв, а не одна команда.
Когда что-то ломается, важно понять уровень: клиент, демон, registry, образ, container runtime или процесс внутри контейнера. Container runtime — это компонент, который непосредственно запускает контейнерный процесс с нужной изоляцией. Один и тот же симптом “не запустилось” может означать разные проблемы на разных слоях.
Docker работает по архитектуре клиент-сервер. Когда вы вводите docker run nginx, ваш CLI-клиент отправляет запрос демону Docker Engine, а тот уже делает всю работу: скачивает образ, создаёт контейнер, запускает процесс.
+----------------------------------------------------------+
| Docker Host |
| |
| +---------+ REST API +--------------------------+ |
| | Docker | -----------> | Docker Daemon | |
| | CLI | | (dockerd) | |
| +---------+ | | |
| | +--------+ +--------+ | |
| | | con1 | | con2 | | |
| | +--------+ +--------+ | |
| +--------------------------+ |
| | |
| +-----------v--------------+ |
| | Local Image Cache | |
| +--------------------------+ |
+----------------------------------------------------------+
^
| docker pull/push
v
+--------------------+
| Docker Registry |
| (Docker Hub, |
| Nexus, etc.) |
+--------------------+
Docker Daemon (dockerd)
Docker Daemon — это демон Docker: фоновый процесс, который делает всю реальную работу. Он:
- Слушает API-запросы (по умолчанию на Unix-сокете
/var/run/docker.sock) - Управляет образами, контейнерами, сетями и Docker volumes
- Делегирует запуск контейнеров низкоуровневому container runtime
Демон работает с правами root. Именно поэтому добавление пользователя в группу docker фактически даёт ему права root — он может монтировать любые директории хоста в контейнер.
# Проверить статус демона
systemctl status docker
# Логи демона
journalctl -u docker -f
containerd и runc
Демон Docker не запускает контейнеры напрямую. Он принимает высокоуровневую команду пользователя, а запуск процесса делегирует container runtime.
Цепочка выглядит так:
docker run
↓
dockerd
↓
containerd
↓
containerd-shim
↓
runc
↓
процесс контейнера в пространствах имён и cgroup
- dockerd — демон Docker: API, команды Docker, сети, volumes, build, интеграция с registry.
- containerd — высокоуровневый container runtime: управляет образами, snapshots (снимками файловой системы), containers и задачами запуска.
- containerd-shim — процесс-прослойка для каждого контейнера: держит stdin/stdout/stderr и статус процесса, поэтому контейнер может жить даже если
dockerdперезапустили. - runc — низкоуровневый OCI runtime: создаёт Linux namespaces, применяет cgroups, mounts (точки монтирования), capabilities и запускает главный процесс контейнера.
runc делает самую низкоуровневую часть: превращает описание контейнера в обычный Linux-процесс с изоляцией. После старта контейнера runc обычно завершает свою работу, а за процессом дальше следит containerd-shim и containerd.
Capabilities здесь означают отдельные привилегии ядра Linux для процесса. Это не полный root-доступ, а набор конкретных разрешений, например на сетевые или административные операции. Docker может оставить контейнеру только нужные capabilities и убрать лишние.
Такое разделение важно для Kubernetes: ему не нужен весь Docker CLI и Docker daemon. Kubernetes может использовать containerd напрямую через CRI и запускать те же OCI-совместимые образы.
В обычной работе ты почти никогда не вызываешь containerd или runc руками. Для пользователя входная точка — docker, для Kubernetes — kubelet, но под капотом всё равно используются эти компоненты запуска.
Docker CLI
Docker CLI — это клиент командной строки, который вы используете в терминале. Он общается с демоном через REST API.
# CLI читает переменную DOCKER_HOST для адреса демона
# По умолчанию: unix:///var/run/docker.sock
echo $DOCKER_HOST
# Можно подключиться к удалённому демону
DOCKER_HOST=tcp://remote-host:2376 docker ps
Все команды docker — это HTTP-запросы к демону. Например, docker ps — это GET /containers/json.
Docker Image
Образ (image) — это неизменяемый шаблон для создания контейнеров. Ключевые свойства:
Слоёная структура
Образ состоит из слоёв (layers). Каждый слой — это набор изменений файловой системы. Слои неизменяемы и используют Union File System (overlay2 в современном Docker).
┌─────────────────────────────┐
│ Layer 4: COPY app.py . │ ← ваш код
├─────────────────────────────┤
│ Layer 3: RUN pip install │ ← зависимости
├─────────────────────────────┤
│ Layer 2: WORKDIR /app │ ← рабочая директория
├─────────────────────────────┤
│ Layer 1: FROM python:3.11 │ ← базовый образ
└─────────────────────────────┘
Каждый слой идентифицируется SHA256-хешем своего содержимого. Это позволяет переиспользовать слои: если два образа используют FROM ubuntu:22.04, этот слой скачивается и хранится один раз.
Content-addressable storage
Слои хранятся в /var/lib/docker/overlay2/. Когда Docker скачивает образ, он проверяет, есть ли нужный слой локально по его хешу. Если есть — не скачивает снова.
# Посмотреть слои образа
docker history nginx:alpine
Детальную информацию об image Docker тоже умеет показывать, но этот инструмент удобнее разбирать позже, после docker run: тогда уже понятно, какой объект мы проверяем и какое состояние хотим увидеть.
Docker Container
Контейнер — это запущенный экземпляр образа. При создании контейнера Docker добавляет поверх слоёв образа тонкий записываемый слой (container layer).
┌─────────────────────────────┐
│ Container layer (R/W) │ ← изменения во время работы
├─────────────────────────────┤
│ Image Layer 4 (R/O) │
├─────────────────────────────┤
│ Image Layer 3 (R/O) │
├─────────────────────────────┤
│ Image Layer 2 (R/O) │
├─────────────────────────────┤
│ Image Layer 1 (R/O) │
└─────────────────────────────┘
Все изменения, которые делает контейнер (создание файлов, изменение конфигурации), попадают только в его записываемый слой. Образ остаётся нетронутым. Когда контейнер удаляется (docker rm), его записываемый слой тоже удаляется.
Именно поэтому контейнеры stateless by design: они задуманы как объекты без постоянного локального состояния. Любые данные, которые нужно сохранить, должны быть вынесены в volumes.
Важная идея: image должен быть неизменяемым шаблоном, а важные данные не должны жить только внутри writable layer контейнера.
Если данные нужны после пересоздания контейнера, им нужен volume, bind mount или внешнее хранилище. Правка “внутри контейнера” годится для диагностики, но плохо подходит как постоянное изменение.
Docker Registry
Registry — это хранилище образов. Когда вы делаете docker pull nginx, Docker обращается к реестру и скачивает слои.
Структура имени образа
registry.example.com/namespace/repository:tag
│ │ │ │ │ │
│ │ └────────┘ │ │
│ │ имя образа │ │
│ │ │ │
└───────────────────┘ └──┘
адрес реестра тег
(если не указан — docker.io) (если не указан — latest)
Примеры:
- nginx → docker.io/library/nginx:latest
- nginx:alpine → docker.io/library/nginx:alpine
- myregistry.com/myteam/myapp:v1.2.3
Docker Hub
hub.docker.com — публичный реестр Docker. Содержит официальные образы (nginx, postgres, python, node) и миллионы образов от сообщества.
Приватные реестры
В корпоративной среде используют приватные реестры:
- Nexus Repository Manager — популярный в enterprise
- Harbor — open-source реестр с RBAC и сканированием уязвимостей
- GitLab Container Registry — встроен в GitLab
- AWS ECR, GCP Artifact Registry — облачные
В нашей платформе образы доступны через Nexus-прокси. Это значит, что docker pull nginx:alpine фактически скачивает образ с Nexus, который кэшировал его из Docker Hub.
Жизненный цикл контейнера
docker create → created
│
docker start → running ←── docker restart
│
docker stop → stopped (exited)
│
docker start → running (снова)
│
docker rm → удалён
Дополнительные состояния:
- paused — процессы заморожены (SIGSTOP), docker pause/docker unpause
- dead — контейнер не удалось удалить (редко)
Networking в Docker
По умолчанию Docker создаёт три сети:
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# abc123 bridge bridge local
# def456 host host local
# ghi789 none null local
- bridge — дефолтная сеть. Контейнеры в ней изолированы от хоста, но могут общаться друг с другом и выходить в интернет через NAT.
- host — контейнер использует сетевой стек хоста напрямую. Нет изоляции, нет NAT.
- none — нет сети вообще.
Подробнее о сетях — в лабораторной работе 5.
Storage в Docker
Docker предоставляет три способа хранить данные:
- Container layer — записываемый слой контейнера. Удаляется с контейнером.
- Bind mount — монтируем директорию хоста в контейнер:
-v /host/path:/container/path - Named volume — Docker создаёт и управляет хранилищем:
-v mydata:/container/path
Named volumes — рекомендуемый способ для production. Они не зависят от структуры директорий хоста и работают одинаково на любой ОС.
Подробнее — в лабораторной работе 6.
Итого
Архитектура Docker строится на четырёх компонентах:
- CLI — интерфейс пользователя
- Daemon (dockerd) — сервер, управляющий всем
- Image — неизменяемый шаблон из слоёв
- Registry — хранилище образов
В следующих лабораторных мы на практике изучим каждый компонент.
Как читать команды Docker CLI
Почти все команды имеют структуру:
docker <object> <action> [options]
Примеры:
docker container ls
docker image ls
docker volume ls
docker network ls
Короткие формы тоже встречаются:
docker ps
docker images
В учебных задачах можно использовать обе формы, но важно понимать объект: ты управляешь контейнером, image, volume или network.
Docker daemon и права доступа
Docker CLI общается с daemon через socket, обычно /var/run/docker.sock. Пользователь, который может писать в этот socket, фактически может управлять контейнерами на хосте.
ls -l /var/run/docker.sock
docker info
На учебной VM команды обычно выполняются от пользователя с нужными правами. В production добавление пользователя в группу docker равносильно выдаче очень широких привилегий: через Docker можно смонтировать файловую систему хоста или запустить privileged-контейнер.
docker info и docker version
docker version
docker info
version показывает версии клиента и сервера. info показывает поля Docker Engine: storage driver, cgroup driver, default runtime, root directory, количество images и containers. В обычных лабах это обзорные команды, но при troubleshooting (поиске причины проблемы) они помогают понять окружение.
У Docker daemon есть собственные настройки. Обычно они лежат в /etc/docker/daemon.json и применяются ко всему Docker Engine на сервере, а не к одному контейнеру. В начале курса не нужно настраивать этот файл вручную, но важно понимать, что такие настройки существуют.
Примеры того, что может настраиваться на уровне daemon:
| Настройка | За что отвечает |
|---|---|
| storage driver | как Docker хранит слои images и writable layer контейнеров |
| logging driver | как Docker сохраняет вывод контейнеров, который потом виден через docker logs |
| registry mirrors | откуда Docker скачивает images, если используется внутренний proxy или mirror |
| cgroup driver | как Docker взаимодействует с Linux cgroups для ограничений CPU/RAM |
Например, overlay2 — это storage driver. Он связан с OverlayFS в Linux: Docker накладывает read-only слои image и writable layer контейнера друг на друга так, чтобы процесс внутри контейнера видел их как одну файловую систему.
Storage driver и writable layer
Современный Docker на Linux часто использует overlay2. Он объединяет read-only слои image и writable layer контейнера в одну файловую систему, которую видит процесс.
Практический эффект:
docker run --name demo alpine sh -c 'echo hello > /file.txt'
docker commit demo demo:snapshot
docker commit может создать image из текущего состояния контейнера, но это плохая практика для нормальной разработки. Повторяемость должна быть в Dockerfile, а не в ручных изменениях контейнера.
Почему лучше Dockerfile, а не ручная настройка
Ручной путь:
docker run -it ubuntu bash
apt update
apt install nginx
Проблема: никто не знает точную историю команд. Dockerfile делает сборку воспроизводимой:
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx
Именно поэтому после CLI-лабы курс переходит к Dockerfile: контейнеры полезны только тогда, когда их можно повторить.
Как связывать симптомы с компонентами Docker
| Симптом | Где смотреть |
|---|---|
permission denied к Docker |
права на /var/run/docker.sock, группа пользователя |
| image не скачивается | registry, tag, сеть, auth |
| порт занят | published ports, docker ps, ss -lntp |
| контейнер сразу Exited | logs, exit code, command/CMD |
| данные пропали | был ли volume или bind mount |
| контейнеры не видят друг друга | network, DNS, user-defined bridge |
Эта таблица помогает в лабораторных не прыгать между темами. Сначала определи слой проблемы: daemon, image, container lifecycle, network или storage.
