Материал открыт для чтения

Войдите, чтобы сохранять прогресс и результаты.

Войти
Docker: Команды, контейнеры и сборка образов

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

чтение ~14 минУрок 2 из 17

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

module docker basics Image02

Общая картина

В предыдущей главе 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 предоставляет три способа хранить данные:

  1. Container layer — записываемый слой контейнера. Удаляется с контейнером.
  2. Bind mount — монтируем директорию хоста в контейнер: -v /host/path:/container/path
  3. 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.

Материал изучен?

Войдите, чтобы сохранить прогресс и перейти к практической лаборатории.

Войти и продолжить