Что такое namespaces и cgroups в Linux? Как они связаны с контейнерами?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Namespaces и cgroups: фундамент контейнеризации в Linux
Этот вопрос затрагивает саму суть технологии контейнеров, таких как Docker и Kubernetes. Namespaces и cgroups — это два фундаментальных механизма ядра Linux, которые в совокупности создают изоляцию и контроль ресурсов, необходимые для работы контейнеров. Они не были созданы специально для контейнеров, но стали их технологическим фундаментом.
Linux Namespaces: изоляция процессов
Namespaces — это механизм ядра Linux для изолирования и виртуализации системных ресурсов для группы процессов. Каждый namespace создаёт собственный, изолированный взгляд на систему. Основные типы namespace:
- PID (Process ID): Изолирует дерево процессов. Процессы в одном PID namespace видят только свои и дочерние процессы. PID 1 в контейнере — это его init-процесс, а не системный
systemdилиinit. - Network (net): Создаёт изолированное сетевое стек (интерфейсы, таблицы маршрутизации, правила iptables). Контейнер получает свой собственный loopback-интерфейс (
lo) и, при необходимости, виртуальные сетевые интерфейсы (например,eth0внутри контейнера, привязанный к veth-паре на хосте). - Mount (mnt): Изолирует точки монтирования файловых систем. Процесс в контейнере "видит" только своё собственное корневое дерево файлов (rootfs), а не всю файловую систему хоста.
- UTS (Unix Timesharing System): Позволяет контейнеру иметь собственное имя хоста (
hostname) и доменное имя (domainname), независимое от хостовой системы. - IPC (Inter-Process Communication): Изолирует ресурсы межпроцессного взаимодействия, такие как очереди сообщений (message queues), сегменты разделяемой памяти и семафоры.
- User: Изолирует пространство UID/GID. Пользователь с UID 1000 внутри контейнера может быть отображён на UID 10000 на хосте, что повышает безопасность.
- Cgroup: Изолирует представление о cgroup (появился позже).
Пример создания простой изоляции с помощью namespaces:
# Запуск новой оболочки (sh) в новых PID, UTS, IPC и Mount namespaces.
# unshare - это утилита для создания namespaces.
sudo unshare --pid --uts --ipc --mount --fork /bin/sh
# В этой новой оболочке:
hostname mycontainer # Меняем hostname только внутри этого namespace
mount -t tmpfs none /tmp # Монтируем tmpfs, видимый только здесь
ps aux # Увидим очень ограниченный список процессов
Это примитивный "контейнер", изолированный от основной системы.
Linux Control Groups (cgroups): управление и лимитирование ресурсов
Если namespaces отвечают на вопрос "Что я вижу?", то cgroups отвечают на вопрос "Сколько ресурсов я могу использовать?".
Cgroups (control groups) — это механизм ядра для ограничения, учёта и изоляции потребления ресурсов (CPU, память, диск, сеть) группой процессов. Основные подсистемы (controllers) cgroups v1/v2:
- cpu / cpuacct: Ограничивает использование процессорного времени и ведёт его учёт. Позволяет назначать shares, quotas и periods (например, через CFS).
- memory: Устанавливает лимиты на использование оперативной памяти и подкачки (swap), контролирует кэш.
- blkio: Ограничивает ввод-вывод на блочных устройствах (дисках).
- pids: Ограничивает количество процессов в группе.
- devices: Управляет доступом к устройствам (разрешён/запрещён).
- net_cls / net_prio: Помечает сетевые пакеты для управления трафиком (например, через
tc).
Пример использования cgroups для ограничения памяти:
# Создаём новую cgroup в подсистеме memory
sudo mkdir /sys/fs/cgroup/memory/my_container_group
# Устанавливаем лимит в 100 МБ
echo "100M" | sudo tee /sys/fs/cgroup/memory/my_container_group/memory.limit_in_bytes
# Запускаем процесс и помещаем его PID в эту cgroup
echo $$ | sudo tee /sys/fs/cgroup/memory/my_container_group/cgroup.procs
# Теперь процесс и его потомки не смогут использовать больше 100 МБ RAM.
Связь с контейнерами (Docker, containerd, Podman)
Современные среды выполнения контейнеров (runc, containerd) используют namespaces и cgroups как строительные блоки.
-
Создание изолированной среды: При запуске контейнера
docker run, среда выполнения (например,runc) через системный вызовclone()илиunshare()создаёт новый набор namespaces для будущих процессов контейнера. Спецификация OCI Runtime явно определяет, какие namespaces должны быть созданы. -
Настройка ограничений ресурсов: На основе флагов
--memory,--cpus,--blkio-weightсреда выполнения создаёт и настраивает cgroup для контейнера (обычно в/sys/fs/cgroup/...), помещая в неё PID основного процесса контейнера (PID 1внутри namespace). Это обеспечивает соблюдение лимитов. -
Иерархия и композиция: Контейнер — это не один namespace или cgroup, а их композиция. Каждый контейнер имеет свой набор изолированных namespaces и принадлежит к своей управляющей cgroup. Инструменты вроде
docker execиспользуют системные вызовы (setns()) для "вхождения" в namespaces существующего контейнера. -
Образ и файловая система: Механизм mount namespace в сочетании с pivot_root или chroot позволяет смонтировать корневую файловую систему образа контейнера (например, слои OverlayFS) в изолированное окружение.
Таким образом, контейнер в Linux — это, по сути, процесс (или группа процессов), запущенный в собственном наборе namespaces и ограниченный cgroups. Docker и другие движки предоставляют удобный пользовательский интерфейс, управление образами, сетевыми слоями и оркестрацией поверх этих низкоуровневых примитивов ядра. Понимание namespaces и cgroups критически важно для глубокой отладки, профилирования и обеспечения безопасности контейнерных сред.