Как устроен контейнер: namespaces, cgroups и OCI runtime
Автор: Казачкин Даниил Михайлович · Обновлено
Контейнер Linux — процесс или группа процессов с настроенным окружением, изоляцией и ограничениями ядра. В отличие от отдельной виртуальной машины ему обычно не требуется…
Контейнер Linux — процесс или группа процессов с настроенным окружением, изоляцией и ограничениями ядра. В отличие от отдельной виртуальной машины ему обычно не требуется собственное гостевое ядро. Разберём три разных механизма и посмотрим признаки namespaces без изменения системных настроек.
Что изолирует namespace
PID namespace меняет видимость процессов и их номера; network namespace даёт своё представление интерфейсов и сокетов; mount namespace — дерево монтирований. UTS относится к имени хоста, user namespace — к отображению идентификаторов пользователей. Эти механизмы можно комбинировать, поэтому слово «контейнер» не обещает одинаковый набор границ в любой конфигурации. Перечень Linux namespaces.
Сравните отдельные запуски:
docker run --rm alpine:3.22 sh -c 'echo "hostname:"; hostname; echo "PID1:"; cat /proc/1/comm; echo "namespaces:"; ls -l /proc/self/ns'
docker run --rm alpine:3.22 sh -c 'echo "hostname:"; hostname; echo "PID1:"; cat /proc/1/comm; echo "namespaces:"; ls -l /proc/self/ns'Видимое имя и некоторые namespace-ссылки отличаются. Не фиксируйте случайные числовые inode в ожидаемом выводе: они характеризуют этот запуск. /proc/1/comm показывает первый процесс данного PID-пространства, а не обязательно первый процесс всей машины. Это объясняет, почему команда ps внутри контейнера не показывает все службы хоста.
Что ограничивает cgroup
Cgroup группирует процессы для учёта и управления ресурсами. Лимит памяти, CPU quota и число процессов — иные свойства, чем видимость файлов или сети. Процесс может быть изолирован по namespace и при этом не иметь подходящего memory limit. Либо несколько разных процессов хоста могут находиться в одной ресурсной группе без привычного контейнерного интерфейса.
На cgroup v2 можно наблюдать контроллеры и счётчики через /sys/fs/cgroup. Их наличие и видимый корень зависят от конфигурации хоста и cgroup namespace. Не пишите туда произвольные значения в учебном контейнере. Документация ядра описывает memory и cpu отдельно: throttling не равен завершению OOM.
Для прикладного опыта задайте docker run --rm --memory 64m --cpus 0.5 alpine:3.22 sh -c 'cat /proc/self/cgroup'. Вывод обозначает членство, но не является измерением фактического расхода. Его надо дополнить stats и счётчиками, как в [уроке лимитов](/lessons/without-university/docker-containerization/docker-developer-30).
Где Docker, containerd и runc
CLI отправляет запросы Engine. В типичной современной конфигурации Docker использует containerd для управления жизненным циклом контейнеров, а низкоуровневый runtime, например runc, настраивает процесс согласно OCI runtime specification. Это роли компонентов, а не обещание, что единственная возможная реализация всегда состоит из этих трёх имён.
OCI также описывает формат образов и распространение артефактов. Совместимость формата не означает одинаковый интерфейс всех инструментов или одинаковые политики безопасности. Область стандартов OCI. Dockerfile и Compose остаются отдельными удобными интерфейсами создания и запуска приложения.
Граница виртуализации
Linux-контейнеры на Linux обычно разделяют ядро хоста. Docker Desktop на другой ОС предоставляет Linux-окружение через VM; внутри него контейнеры снова используют Linux-механизмы. Именно поэтому контейнер и VM могут сосуществовать, а не обязательно исключать друг друга. Для отдельной усиленной границы полезно изучить [исследование Firecracker](/research/firecracker-isolation-density), учитывая его специализированный предмет.
Контрольная схема
Нарисуйте процесс приложения, его PID/network/mount namespaces и cgroup. Для вопросов «какие процессы видит?», «сколько памяти разрешено?», «почему localhost другой?» укажите ответственный механизм. Затем объясните, почему доступ к Docker socket способен разрушить ожидаемую изоляцию независимо от малого memory limit. Эта схема помогает выбрать правильный инструмент диагностики вместо случайного добавления privileged.