Главная/Материалы/Материал

Архитектура Docker

Что происходит между командой docker run и работающим процессом.

Общая схема

text
   ваш терминал
        │
        │  docker run -d --name api образ
        ▼
┌───────────────┐
│  docker (CLI) │  преобразует аргументы в HTTP-запросы
└───────┬───────┘
        │  HTTP через /var/run/docker.sock
        ▼
┌───────────────────────────────────────────────┐
│  dockerd (демон, работает от root)            │
│  ─────────────────────────────────────────    │
│  образы, тома, сети, сборка, API              │
└───────┬───────────────────────────────────────┘
        │  gRPC
        ▼
┌───────────────────────────────────────────────┐
│  containerd                                   │
│  жизненный цикл container'ов, снимки, задачи  │
└───────┬───────────────────────────────────────┘
        │  создаёт по одному на container
        ▼
┌───────────────┐
│  containerd-  │  остаётся жить: держит stdio,
│  shim         │  код возврата, переживает
└───────┬───────┘  перезапуск демона
        │  вызывает один раз и ждёт
        ▼
┌───────────────┐
│     runc      │  настраивает namespaces, cgroups,
└───────┬───────┘  seccomp — и ЗАВЕРШАЕТСЯ
        │  exec
        ▼
┌───────────────┐
│ ваш процесс   │  PID 1 в своём namespace
└───────────────┘

Как читать

Стрелки — вызовы, а не «содержится внутри». Ни один уровень не выполняется внутри другого: это отдельные процессы, общающиеся через сокеты.

Что здесь неочевидно

runc завершается. После настройки окружения он делает exec — то есть заменяет себя вашим процессом. В списке процессов работающего container'а runc отсутствует:

bash
pgrep -c runc          # 0 при десятке работающих container'ов

Родитель вашего процесса — shim, а не демон. Отсюда следует свойство, которое иначе выглядит магией: перезапуск dockerd не убивает container'ы. Некому убивать — они ему не принадлежат.

bash
PID=$(docker inspect имя --format '{{.State.Pid}}')
ps -o ppid= -p "$PID"     # PID shim'а, не dockerd

CLI не делает ничего. Всё, что он умеет, — составить HTTP-запрос:

bash
curl -s --unix-socket /var/run/docker.sock \
     http://localhost/v1.44/containers/json | python3 -m json.tool | head

Отсюда следствие про безопасность: доступ к сокету равен правам root на хосте — можно попросить демон создать container с любыми параметрами.

Один docker run — это два запроса

text
POST /containers/create   ──►  container существует, не запущен
                               (Pid = 0)
POST /containers/ID/start ──►  container работает
                               (Pid = настоящий)

Между ними container можно осмотреть:

bash
ID=$(docker create --name проба образ)
docker inspect проба --format '{{.State.Status}} {{.State.Pid}}'   # created 0
docker start проба
docker inspect проба --format '{{.State.Status}} {{.State.Pid}}'   # running 12345

Где что искать при отказе

СимптомУровеньКак смотреть
CLI не отвечаетСокет или демонsystemctl status docker
Ошибка при созданииДемонjournalctl -u docker --since '5 min ago'
Container не стартуетruncdocker logs, код возврата 125–127
Процесс упалПриложениеdocker logs, код возврата приложения
Процесс убитЯдроdmesg, docker events --filter event=oom

Последняя строка — та, где docker logs бесполезен: запись делает ядро, а не процесс.

Подробнее

Урок 17.1. Engine API и сокет
Урок 17.2. containerd, shim, runc


Навигация

Все диаграммы
Раздел 17. Docker internals
Главное оглавление

Markdown на GitHub ↗