Skip to main content

Що таке Docker namespace і cgroups?

Linux namespaces і cgroups це два kernel-механізми, що роблять container можливими. Без них у тебе процеси; з ними у тебе container. Docker переважно це інструмент для налаштування цих фіч на масштабі.

Теорія

TL;DR

  • Namespaces відповідають на «що цей процес бачить?». Сім типів: PID, mount, network, IPC, UTS, user, cgroup.
  • cgroups відповідають на «що цей процес може використовувати?». Лімітують CPU, пам'ять, I/O, PID.
  • Обидва це kernel-фічі, що передували Docker роками (LXC популяризував; Docker зробив mainstream).
  • Container концептуально: unshare() для створення namespaces + cgroups-config + chroot (або pivot_root) у rootfs + exec() твого бінаря.
  • Сучасний Docker на Linux використовує cgroups v2 (unified hierarchy). Legacy v1 мала окремі ієрархії на controller.

Namespaces — сім типів

NamespaceЩо ізолює
PIDprocess ID — container має свій PID 1
mount (mnt)mount points — container має свою в'юху filesystem
network (net)мережеві інтерфейси, routing-таблиці, сокети, порти
IPCshared memory, семафори, message queues
UTShostname і domain name
userUID/GID (з remap)
cgroupв'юха cgroup root (cgroups v2)

Кожен namespace це kernel-ресурс, до якого можна приєднати процес через unshare(2) або clone(2). Docker їх створює при старті container.

Верифікація namespaces у running container

bash
# Namespaces container (кожен має унікальний inode) $ docker run -it --rm alpine sh / # ls -la /proc/self/ns lrwxrwxrwx ... cgroup -> 'cgroup:[4026532840]' lrwxrwxrwx ... ipc -> 'ipc:[4026532838]' lrwxrwxrwx ... mnt -> 'mnt:[4026532836]' lrwxrwxrwx ... net -> 'net:[4026532842]' lrwxrwxrwx ... pid -> 'pid:[4026532839]' lrwxrwxrwx ... user -> 'user:[4026531837]' lrwxrwxrwx ... uts -> 'uts:[4026532837]' # Порівняй з host (різні inode для усього, окрім, можливо, user) $ ls -la /proc/self/ns

Різні inode = різні namespaces = ізольовані в'юхи.

cgroups — що вони лімітують

У cgroups v2 (unified hierarchy, сучасна норма) controllers включають:

cpu — CPU-час (cpus, cpu.weight) memory — RAM і swap-використання io — block-I/O bandwidth і IOPS pids — кількість процесів rdma — RDMA-bandwidth hugetlb — huge pages
bash
# Всередині container з --memory=256m $ docker run --rm --memory=256m alpine cat /sys/fs/cgroup/memory.max 268435456 # 256 * 1024 * 1024 байт $ docker run --rm --cpus=0.5 alpine cat /sys/fs/cgroup/cpu.max 50000 100000 # 50мс quota на 100мс period (= 0.5 CPU)

Docker транслює --memory, --cpus тощо у cgroup-файли у /sys/fs/cgroup/....

Як docker run використовує обидва

docker run --memory=256m --cpus=1 myapp ├── containerd → runc │ │ │ ├── unshare(CLONE_NEW{PID,NS,NET,IPC,UTS,USER,CGROUP}) │ │ → 7 свіжих namespaces │ │ │ ├── write cgroup-файли │ │ → memory.max=256MB, cpu.max=100000 100000 │ │ │ ├── pivot_root у image rootfs │ │ │ └── exec(your-binary) └── ти бачиш 'container'

Namespaces дають приватні в'юхи; cgroups лімітують, що може робити; pivot_root + image-шари дають кастомну filesystem; exec крутить твій бінар як PID 1 у цьому маленькому світі.

User namespace: security-фронтир

bash
# Дефолт (без user namespace remap): root всередині = root на host (якщо не capability-restricted) $ docker run --rm alpine id uid=0(root) gid=0(root) # Всередині container ти root; kernel знає. # З userns-remap: root всередині мапиться на non-root UID на host $ # /etc/docker/daemon.json: { "userns-remap": "default" } $ docker run --rm alpine id uid=0(root) gid=0(root) # Всередині, ще root. Але на host процеси як some-non-root-UID.

User namespace remap це одне з найсильніших container-hardening. Container-escape більше не означає root на host.

cgroups v1 vs v2

  • v1 (legacy): окремі ієрархії на controller (/sys/fs/cgroup/memory, /sys/fs/cgroup/cpu тощо). Кожен процес належить одному cgroup на controller.
  • v2 (modern): єдина unified-ієрархія. Кожен cgroup контролює усі enabled-ресурси. Простіше, послідовніше.
  • Більшість сучасних Linux-дистро (Fedora, Debian 11+, Ubuntu 22+) дефолтно v2. Docker обробляє обидві.

Перевір, яка у твого host:

bash
stat -fc %T /sys/fs/cgroup # 'cgroup2fs' = v2, 'tmpfs' = v1

Типові помилки

Сприймати namespaces як security-межі

Namespaces ховають речі від процесу; вони не зупиняють процес з правильними capabilities від break-out. У комбінації з capabilities (default-drop CAP_SYS_ADMIN тощо) і seccomp (syscall-фільтрація) утворюють захист, але самі namespaces не impenetrable. Реальна ізоляція потребує повного Docker security-стеку (або microVM як Kata/Firecracker).

Забути, що cgroups лімітують пам'ять, але не OOM-поведінку

bash
docker run --memory=256m greedy-app # Коли greedy-app намагається алокувати 257-й MB, kernel OOM-kill.

OOM-kill процеси всередині cgroup виходять 137. Твій supervisor / restart-policy вирішує, що далі.

Плутати user namespace з --user

  • --user 1000:1000 = крути процес як конкретний UID всередині user-namespace container (все одно root всередині, якщо не remap).
  • userns-remap = remap UID з container на host. Окрема рукоятка.

Припускати, що PID-namespaces працюють як container скрізь

Kubernetes-pod можуть ділити PID-namespaces між container (флаг shareProcessNamespace: true). У звичайному Docker два docker run container завжди мають окремі PID-namespaces. Інший mental-model.

Реальне застосування

  • Кожен container, скрізь. Namespaces і cgroups лежать в основі кожного Docker, Podman, K8s-pod, Lambda-функції (через Firecracker microVM), Cloud Run-task.
  • Hardened multi-tenant: user namespace remap + dropped capabilities + seccomp-профіль + read-only filesystem → strong-ish ізоляція.
  • Resource governance: cgroup-ліміти запобігають заморюванню одним tenant іншими на shared-infra.
  • Debugging container-внутрішнього: lsns, nsenter, /proc/<pid>/ns/* для inspect або join namespaces ззовні.

Питання для поглиблення

Q: Чи namespaces або cgroups Linux-специфічні?


A: Так. Обидва це Linux kernel-фічі. Docker на Mac/Windows крутить Linux VM під капотом саме тому.

Q: Що таке unshare?


A: Syscall (і CLI-tool), що створює новий namespace і приєднує calling-процес. unshare --pid --fork --mount-proc /bin/bash дає тобі shell у свіжих PID + mount namespaces, найпростіше «побудуй container руками» демо.

Q: Яка різниця між cgroups і ulimits?


A: ulimits (resource-ліміти через PAM, setrlimit) per-process. cgroups застосовуються до дерева процесів і переживають exec. Обидва можуть лімітувати ресурси; cgroups це сучасна, ієрархічна kernel-відповідь.

Q: Чи можу побачити, у якому cgroup host-процес?


A: Так: cat /proc/<pid>/cgroup. Для Docker-процесу це вказує у /sys/fs/cgroup/<docker-cgroup-path>.

Q: (Senior) Як побудувати мінімальний container руками лише через namespaces + cgroups?


A: unshare --pid --net --mount --uts --ipc --user --fork chroot /path/to/rootfs /bin/sh дає тобі процес з ізольованими namespaces і кастомним rootfs, голий кістяк container. Додай cgroups руками, пишучи у /sys/fs/cgroup/<your-cgroup>/.... Це приблизно те, що runc робить за тебе, автоматизовано і OCI-spec-сумісно. Корисна вправа, щоб демістифікувати, що таке container.

Приклади

Демонстрація namespace-ізоляції

bash
# Host $ ps -ef | wc -l 312 # Всередині container $ docker run --rm alpine ps -ef | wc -l 2 # Container бачить лише свої процеси (PID 1 + сам ps)

Той самий kernel, дві в'юхи, ось і PID-namespace у дії.

Демонстрація cgroups

bash
# Memory-жадібна програма $ docker run --rm --memory=64m alpine sh -c 'dd if=/dev/zero of=/dev/null bs=1G count=1' Killed # Container OOM-kill на 64MB ліміті. $ docker run --rm --cpus=0.1 alpine sh -c 'time -- timeout 5 sh -c "yes > /dev/null"' real 0m 5.00s user 0m 0.50s sys 0m 0.00s # user time = 0.5s у 5s wall = 10% CPU = cap

Kernel enforce'їть, Docker лише ставить параметри.

Mapping --user vs userns-remap

bash
# --user: змінює UID всередині (без remap) $ docker run --rm --user 1000:1000 alpine id uid=1000 gid=1000 # Зовні все одно UID 1000 (без ізоляції між in/out namespaces). # З userns-remap (daemon-level config): # /etc/docker/daemon.json: { "userns-remap": "default" } $ docker run --rm alpine id uid=0(root) gid=0(root) # Всередині root. $ ps -ef | grep <container-pid> UID 165536 ... # Але на host той самий процес UID 165536 (offset).

Друге значно сильніша ізоляція. Дуже рекомендовано для multi-tenant кластерів.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів