Контейнеры и виртуальные машины: namespaces, cgroups

↑ Этап 2 · Контейнеры: Docker, docker-compose, Makefile

Обязательно~3 мин чтенияУровень: junior

Зачем это на собесе

Главный вопрос о контейнерах: «чем отличается контейнер от виртуальной машины» и из чего он состоит на уровне ядра.

Виртуальная машина и контейнер

Виртуальная машинаКонтейнер
Изоляциягипервизор, отдельное ядро гостяпроцессы на общем ядре хоста
Размергигабайты (полная ОС)десятки–сотни мегабайт
Стартдесятки секунд–минутыдоли секунды
Плотностьединицы–десятки на хостсотни
Безопасностьсильная граница (гипервизор)слабее: общее ядро, уязвимость ядра затрагивает всех
ОС внутрилюбаятот же тип ядра (Linux-контейнер на Linux-ядре)

Контейнер — это обычный процесс Linux, изолированный и ограниченный средствами ядра. Контейнеров «как объекта» в ядре нет: это сочетание namespaces + cgroups + файловая система образа (overlay) + capabilities/seccomp.

Namespaces — изоляция видимости

NamespaceЧто изолирует
pidсвоё дерево процессов: внутри контейнера процесс имеет PID 1
netсвои интерфейсы, IP, таблица маршрутов, порты, firewall
mntсвоя точка монтирования, корневая ФС
utshostname и domainname
ipcразделяемая память, очереди сообщений
userсопоставление UID/GID: root в контейнере ≠ root на хосте (rootless)
cgroupсвоё представление дерева cgroups
timeотдельные часы (новые ядра)
lsns                               # список namespaces
sudo unshare --fork --pid --mount-proc --uts bash    # собственный PID/UTS namespace
ls -l /proc/<pid>/ns/              # namespaces процесса
sudo nsenter -t <pid> -n ip a      # зайти в сетевой namespace контейнера

cgroups — ограничение ресурсов

Control groups ограничивают и учитывают ресурсы группы процессов: CPU (cpu.max, cpu.weight), память (memory.max), I/O, число процессов (pids.max).

docker run --memory=256m --cpus=1.5 --pids-limit=200 nginx
cat /sys/fs/cgroup/system.slice/docker-<id>.scope/memory.max      # cgroup v2
systemd-cgtop
  • превышение памяти → OOM kill процесса контейнера (код 137);
  • превышение CPU → троттлинг (процесс замедляется, а не убивается): метрика container_cpu_cfs_throttled_seconds_total;
  • cgroup v2 — единая иерархия, по умолчанию в современных дистрибутивах.

Файловая система: слои и overlay

Образ — набор read-only слоёв; контейнер добавляет сверху слой записи (union/overlay FS, overlay2). Копирование при записи (copy-on-write): изменённый файл копируется в верхний слой. Данные верхнего слоя исчезают вместе с контейнером → для состояния нужны volumes.

chroot/pivot_root — смена корня файловой системы процесса.

Безопасность

  • Capabilities: контейнер получает урезанный набор привилегий root (например, нет CAP_SYS_ADMIN);
  • seccomp: профиль запрещённых системных вызовов (Docker по умолчанию блокирует ~44 из ~300+);
  • AppArmor/SELinux: обязательный контроль доступа;
  • rootless и user namespaces: процесс в контейнере не root на хосте;
  • --privileged снимает ограничения — нельзя в проде без крайней необходимости.

Что контейнер НЕ изолирует

Ядро (общее), часы (до time ns), /proc/sys (частично), dmesg, некоторые устройства. Поэтому для жёсткой изоляции нужны песочницы: gVisor (перехват syscalls), Kata Containers / Firecracker (микро-ВМ).

Когда что использовать

  • Контейнеры: микросервисы, CI, воспроизводимые окружения, высокая плотность;
  • ВМ: разные ОС и ядра, жёсткая изоляция арендаторов, legacy-системы, драйверы ядра;
  • Комбинация: Kubernetes на ВМ (типично в облаке).

Вопросы с ответами