С чего начать Первые шаги трек · 15 ступеней
Linux Labs

cgroups и namespaces — как устроены контейнеры

5 мин Linux · cgroups v2

Изоляция (namespaces) и лимиты ресурсов (cgroups) — фундамент контейнеров и systemd.

Шпаргалка

6 команд Версия для печати
  • lsns namespaces (изоляция)
  • systemd-cgls дерево cgroups
  • systemd-cgtop потребление ресурсов по cgroup
  • sudo nsenter -t PID -a bash войти во все namespaces процесса
  • sudo systemctl set-property nginx.service MemoryMax=512M CPUQuota=50% TasksMax=100
  • systemctl show nginx.service -p MemoryCurrent -p MemoryMax

Обзор

Контейнеры (Docker) — обычные процессы Linux, которых ядро держит двумя механизмами:

  • namespaces — изоляция: процесс видит свой набор PID, сети, точек монтирования, пользователей, имени хоста (как будто отдельная система);
  • cgroups — лимиты и учёт ресурсов: CPU, память, I/O для группы процессов.

Этим же пользуется systemd (каждая служба — в своей cgroup).

Рецепты

3

namespaces

bash
lsns                              # все namespaces и какие процессы в них
lsns -t net                       # только сетевые namespaces
ls -l /proc/$$/ns/                # namespaces текущей оболочки
sudo nsenter -t PID -n ss -tlnp   # выполнить в СЕТЕВОМ namespace процесса PID
sudo nsenter -t PID -a bash       # войти во ВСЕ namespaces процесса (как «внутрь контейнера»)
unshare --net --pid --fork bash   # запустить шелл в новых namespace (демо изоляции)
ip netns list                     # именованные сетевые namespaces (см. ip)

Типы: pid (процессы), net (сеть/интерфейсы), mnt (монтирования), user (UID), uts (hostname), ipc, cgroup.

cgroups (v2)

bash
systemd-cgls                      # дерево cgroups (кто где)
systemd-cgtop                     # потребление ресурсов по cgroup (как top)
cat /sys/fs/cgroup/cgroup.controllers   # доступные контроллеры
systemctl status nginx            # внизу — CGroup и текущее потребление

# лимиты службе — через systemd (правильный способ):
sudo systemctl set-property nginx.service MemoryMax=512M CPUQuota=50% IOWeight=50
systemctl show nginx -p MemoryMax -p CPUQuota -p MemoryCurrent
# или в юните [Service]: MemoryMax=512M / CPUQuota=50% / TasksMax=100

CPUQuota=50% — половина одного ядра; MemoryMax= — жёсткий потолок: при превышении OOM случается внутри группы этой службы (см. oom-killer); TasksMax= — лимит числа процессов.

Срезы и деление процессора

systemd раскладывает процессы по дереву групп. Службы лежат в срезе (slice) system.slice, сеансы пользователей — в user.slice; группе служб можно дать свой срез строкой Slice=shop.slice в юните. Чей процесс — видно в строке CGroup у systemctl status и в ps -o unit:

text
student@ubuntu:~$ ps -o pid,unit,comm -p 1211,$$
    PID UNIT                            COMMAND
   1021 session-c1.scope                bash
   1211 ssh.service                     sshd

В образе тренажёра у system.slice и user.slice включён контроллер cpu (cat /sys/fs/cgroup/system.slice/cgroup.subtree_control). Когда процессор нужен нескольким группам, ядро сначала делит его между группами по весу CPUWeight (по умолчанию 100), а уже внутри группы — между процессами по приоритету nice. Опыт на одном процессоре, по два stress-ng --cpu 1:

Где процессы Доли процессора
две службы, CPUWeight=100 и CPUWeight=900 9.7 % и 88.7 %
две службы, в одной nice -n 19 49.1 % и 49.1 %
одна служба, у одного процесса nice -n 19 96.6 % и 1.4 %

nice действует только между соседями по группе. Чтобы одна служба уступала другой, ей понижают вес или ставят потолок:

bash
systemctl show app -p CPUWeight -p Slice
sudo systemctl set-property app CPUWeight=50     # сразу и навсегда
sudo systemctl set-property app CPUQuota=20%     # не больше 20 % CPU
cat /sys/fs/cgroup/system.slice/app.service/cpu.weight

Неполадки

4

Процесс «убит», в логах memory cgroup / oom-kill

Служба упёрлась в MemoryMax своей cgroup — ядро убило её (cgroup-OOM, не глобальный). systemctl show svc -p MemoryMax -p MemoryCurrent; подними лимит или почини утечку (см. oom-killer).

Служба «съедает» весь CPU/диск

Ограничь через systemd: CPUQuota=, IOWeight=/IOReadBandwidthMax=. Это cgroups, а не ulimit (тот — на процесс/пользователя, иные вещи).

Контейнер не видит другие процессы/сеть — это норма

Так задумано (namespaces изолируют). Чтобы «заглянуть внутрь» — nsenter -t PID -a в нужные namespaces процесса.

Где реально потребление по службам

systemd-cgtop (как top, но по cgroup/службам) — сразу видно, какая служба грузит.

См. также

  • veth — сетевые пространства имён и пара veth на практике.
  • docker — Базовая работа с контейнерами — запуск, логи, exec, образы, compose, чистка.
  • systemd — Init-система и менеджер служб (PID 1): юниты, цели, зависимости, журналы.
  • ulimit — Лимиты на процессы — открытые файлы, число процессов; limits.conf и systemd.
Загрузка…