Контейнеры и виртуальные машины: namespaces, cgroups
↑ Этап 2 · Контейнеры: Docker, docker-compose, Makefile
Зачем это на собесе
Главный вопрос о контейнерах: «чем отличается контейнер от виртуальной машины» и из чего он состоит на уровне ядра.
Виртуальная машина и контейнер
| Виртуальная машина | Контейнер | |
|---|---|---|
| Изоляция | гипервизор, отдельное ядро гостя | процессы на общем ядре хоста |
| Размер | гигабайты (полная ОС) | десятки–сотни мегабайт |
| Старт | десятки секунд–минуты | доли секунды |
| Плотность | единицы–десятки на хост | сотни |
| Безопасность | сильная граница (гипервизор) | слабее: общее ядро, уязвимость ядра затрагивает всех |
| ОС внутри | любая | тот же тип ядра (Linux-контейнер на Linux-ядре) |
Контейнер — это обычный процесс Linux, изолированный и ограниченный средствами ядра. Контейнеров «как объекта» в ядре нет: это сочетание namespaces + cgroups + файловая система образа (overlay) + capabilities/seccomp.
Namespaces — изоляция видимости
| Namespace | Что изолирует |
|---|---|
pid | своё дерево процессов: внутри контейнера процесс имеет PID 1 |
net | свои интерфейсы, IP, таблица маршрутов, порты, firewall |
mnt | своя точка монтирования, корневая ФС |
uts | hostname и 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 на ВМ (типично в облаке).
Вопросы с ответами
Чем контейнер отличается от виртуальной машины?
Контейнер — изолированный процесс на общем ядре хоста (namespaces и cgroups), лёгкий и быстрый. ВМ эмулирует железо и запускает собственное ядро под гипервизором: тяжелее, но изоляция сильнее.
Какие технологии ядра лежат в основе контейнеров?
Namespaces (изоляция видимости), cgroups (лимиты ресурсов), overlay-файловые системы (слои образа), capabilities и seccomp (ограничение привилегий).
Что произойдёт, если контейнер превысит лимит памяти и CPU?
По памяти — OOM killer убьёт процесс (код 137); по CPU — процесс будет троттлиться (замедляться) без завершения.