linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Процессы и ресурсы/cgroups-v2-deep

kb/processes ── Процессы и ресурсы ── advanced

cgroups v2 - unified hierarchy, PSI, eBPF control

cgroups v2 - один tree вместо отдельных hierarchies на controller. Чистая семантика, новые fields (memory.high, io.cost). PSI показывает resource pressure. eBPF может управлять resources. Default в RHEL 9, Ubuntu 22+.

view as markdownaka: cgroupv2, cgroups-v2, unified-hierarchy, psi, pressure-stall-info

Зачем cgroups v2

[[cgroups|cgroups v1]] (с 2007) - per-controller hierarchy: для каждого resource (cpu, memory, blkio, net_cls...) - отдельное дерево в /sys/fs/cgroup/<controller>/.... Процесс мог быть в разных cgroup'ах в каждой иерархии.

Это породило проблемы:

  • Несогласованность семантики между controllers - cpuset работал одним, memory другим способом
  • Сложно делегировать управление - sub-tree contains только subset controllers
  • Нельзя надёжно описать "лимиты для процесса" - нужно вычислять intersection
  • net_cls/net_prio устарели в пользу eBPF

В 2016 (kernel 4.5) появился cgroups v2 с единым деревом и чистой семантикой. После 5 лет полировки cgroups v2 - default в:

  • systemd 247+ (2020-) - hybrid mode
  • RHEL 9 (2022) - pure v2
  • Ubuntu 21.10+ - pure v2
  • Kubernetes 1.25+ - support
  • Docker 20.10+ - поддержка

Unified hierarchy

Единое дерево в /sys/fs/cgroup/:

/sys/fs/cgroup/
├── cgroup.controllers          # доступные controllers
├── cgroup.subtree_control      # которые включены для children
├── system.slice/
│   ├── cgroup.controllers
│   ├── cpu.weight
│   ├── memory.high
│   ├── memory.max
│   ├── nginx.service/
│   │   ├── cpu.stat
│   │   ├── memory.current
│   │   └── pids.current
│   └── postgresql.service/
│       └── ...
└── user.slice/
    └── user-1000.slice/
        └── session-1.scope/

Каждый узел - directory с одинаковым набором файлов (зависит от включённых controllers).

Процесс находится только в одном узле дерева (через cgroup.procs).

Controllers - что доступно в v2

Controllerv1 эквивалентЧто делает
cpucpu+cpuacctweight, max, period - CPU shares и hard limit
cpusetcpusetпривязка к CPU/NUMA-node
memorymemoryusage, low, high, max, soft limit
ioblkioweight, max BW, BW limits
pidspidsограничение по числу процессов
rdmardmaInfiniBand resources
misc-gpu hpu - кастомизируемые лимиты
hugetlbhugetlbhuge page allocations

v1 controllers, которых нет в v2 (deprecated):

  • net_cls, net_prio → перенесли в [[ebpf-basics|eBPF cgroup-attached programs]]
  • freezer → теперь через cgroup.freeze файл
  • devices → через eBPF (BPF_PROG_TYPE_CGROUP_DEVICE)
  • perf_event, cpuacct → внутри cpu

Включить controller в subtree - писать в cgroup.subtree_control:

echo "+memory +pids" > /sys/fs/cgroup/system.slice/cgroup.subtree_control

Внутри children теперь доступны memory.* и pids.* файлы.

Memory controller - новые fields

В v2 управление памятью более гибкое:

FileСемантика
memory.currentтекущее использование, байты
memory.minreserved, никогда не reclaim'ится (если есть chance)
memory.lowbest-effort protected - reclaim только под pressure
memory.highsoft limit - kernel начинает throttling/reclaim
memory.maxhard limit - превышение = OOM в этой cgroup
memory.swap.maxswap-лимит отдельно
memory.eventscounters: low, high, max, oom, oom_kill
memory.statдетальный breakdown (anon, file, slab, sock, ...)

memory.high - главная новая ручка - в отличие от v1 hard-limit:

  • Превышение → kernel замедляет cgroup (sleep на page-fault), активно reclaim'ит page cache, но не убивает процессы
  • Если давление длится - eventually OOM-killer (oom-killer)

Это даёт graceful backpressure вместо OOMK-цикла.

CPU controller - weight и max

cpu.weight       # 1-10000 (default 100), относительный вес
cpu.max          # "max <quota> <period>", напр. "100000 100000" = 1 CPU
cpu.stat         # usage_usec, user_usec, system_usec, throttled_usec

В v1 было два контролятора: cpu (weight) + cpuacct (statistics). В v2 объединено.

I/O controller - weight, max, cost-based

io.weight        # default 100, относительный
io.max           # rbps/wbps/riops/wiops лимиты per-device
io.stat          # счётчики per-device
io.cost.qos      # cost-based QoS (RHEL 9+)

io.cost (новинка): не лимиты в IOPS/bps, а "cost budget" с учётом device performance characteristics. Лучше адаптируется к разным дискам (NVMe vs HDD).

PSI - Pressure Stall Information

В v1 нельзя было сказать: "перегружена ли система или конкретная cgroup?" Лоад average был один на хост, неточный.

PSI (Linux 4.20+) - три файла на root и каждой cgroup:

  • /proc/pressure/cpu - CPU pressure
  • /proc/pressure/memory - память
  • /proc/pressure/io - I/O

Формат:

cat /sys/fs/cgroup/system.slice/postgresql.service/io.pressure
some avg10=12.34 avg60=8.91 avg300=5.12 total=12345678
full avg10=3.45  avg60=2.10 avg300=1.05 total=3456789
  • some - хоть один task в cgroup ждёт ресурс
  • full - все tasks ждут (cgroup полностью застряла)
  • avg10/60/300 - проценты времени за 10/60/300 секунд
  • total - microseconds total

Применение:

  • Auto-scaling Kubernetes по PSI вместо CPU%
  • Out-of-memory prediction - если memory.pressure full > 50% - вот-вот OOM
  • systemd-oomd использует PSI для proactive killing задолго до [[oom-killer|OOM]]

systemd и cgroup делегирование

systemd - единственный writer в cgroup-дерево по умолчанию:

  • system.slice - системные сервисы
  • user.slice - пользовательские сессии
  • machine.slice - VM/контейнеры (machinectl)

Каждый unit получает свой cgroup. Лимиты задаются unit-свойствами:

[Service]
CPUWeight=200
MemoryHigh=512M
MemoryMax=1G
IOWeight=300
TasksMax=200

Эквивалентно записям в cgroup.subtree_control + *.max/weight.

Делегирование под-tree другому процессу (Docker, k8s):

Delegate=yes

Тогда process может сам создавать sub-cgroups и менять лимиты в своём sub-дереве. Используется для контейнеров.

eBPF + cgroup - программируемый control

v2 интегрируется с [[ebpf-basics|eBPF]] через cgroup-attached программы:

  • BPF_PROG_TYPE_CGROUP_SKB - L3/L4 фильтрация per-cgroup
  • BPF_PROG_TYPE_CGROUP_SOCK - control при создании socket
  • BPF_PROG_TYPE_CGROUP_SOCKOPT - перехват setsockopt/getsockopt
  • BPF_PROG_TYPE_CGROUP_DEVICE - device whitelist
  • BPF_PROG_TYPE_LSM_CGROUP - LSM hook per-cgroup

Заменяет старые v1 controllers (devices, net_cls/net_prio). Cilium использует cgroup-eBPF для service routing per-pod.

Прицепляется через bpftool cgroup attach:

bpftool cgroup attach /sys/fs/cgroup/system.slice/myapp.service \
                     skb_egress my_prog.bpf.o sec egress

v1 vs v2 - сравнение

Свойствоv1v2
Деревоper-controllerunified
Process в нескольких?да (один на controller)нет
Threaded modeнетда (cgroup.type = threaded)
Soft memory limitда (memory.soft_limit_in_bytes)нет (используй memory.high)
OOM behaviorOOM в cgroup+ memory.high throttling
PSIнетда
eBPF integrationминимальнаяпервоклассная
Default in distros (2025)RHEL 7-8, Ubuntu < 21RHEL 9, Ubuntu 21+, всё новое

Hybrid mode - переход

systemd может работать в hybrid mode: v1 для legacy controllers (cpuset, freezer) и v2 для новых. Файл /sys/fs/cgroup/cgroup.controllers показывает только controllers в v2-дереве. Этот режим default в Ubuntu 20.04, RHEL 8.

С systemd.unified_cgroup_hierarchy=1 (kernel cmdline) - чистый v2.

Проверить:

stat -fc %T /sys/fs/cgroup/
# cgroup2fs = pure v2
# tmpfs = hybrid v1+v2

Когда что-то пошло не так

  • cgroup.subtree_control пустой - нет включённых controllers. Не получится создать child с *.max файлами. Сначала включи в parent: echo "+cpu +memory" > cgroup.subtree_control.
  • No space left on device при создании cgroup - kernel.threads-max или pids.max в parent. Проверь.
  • Контейнер не видит лимиты - старый container runtime, который знает только v1. crun/containerd >= 1.5, runc >= 1.0 знают v2.
  • systemd-oomd убивает не то - проверь oomd.conf, по умолчанию оценивает по 50% memory pressure.
  • cpu.weight=1000 не даёт priority - другие cgroups в parent тоже подняли weight. Это относительная scheduling, проверь siblings.
  • PSI везде нули - kernel < 4.20 или PSI не включён (CONFIG_PSI=y). Может быть отключён в cmdline psi=0.
  • kubelet ругается на cgroup driver - mismatch между runtime (cgroupfs vs systemd). Поставь обоих на systemd.

Полезные команды/файлы

  • systemd-cgls - дерево cgroup'ов с unit'ами
  • systemd-cgtop - top по cgroup-resource usage
  • systemctl set-property nginx.service MemoryMax=1G - runtime change
  • cat /sys/fs/cgroup/.../cgroup.procs - какие PID в cgroup
  • cat /proc/<pid>/cgroup - в какой cgroup процесс
  • cgroup.events - notifications (low, high, max, oom, populated)

§ команды

bash
stat -fc %T /sys/fs/cgroup/

cgroup2fs = pure v2. tmpfs = hybrid v1+v2. Видно сразу

bash
systemd-cgls

Дерево cgroup'ов c systemd unit'ами и процессами

bash
systemd-cgtop

Top по cgroup-resource usage (CPU%, memory, I/O)

bash
cat /proc/$$/cgroup

В какой cgroup текущий shell - формат '0::/path'

bash
cat /sys/fs/cgroup/system.slice/nginx.service/memory.current

Текущее потребление памяти конкретным сервисом

bash
cat /sys/fs/cgroup/system.slice/nginx.service/io.pressure

PSI для I/O - 'some' и 'full' за 10/60/300 секунд

bash
systemctl set-property nginx.service MemoryHigh=512M MemoryMax=1G

Runtime изменение лимитов сервиса (persists в /etc/systemd/system.control/)

bash
echo '+memory +io' > /sys/fs/cgroup/<slice>/cgroup.subtree_control

Включить controllers для children этой cgroup

§ см. также

  • cgroupscgroups (v2)cgroups v2 - иерархическая виртуальная FS под `/sys/fs/cgroup` через которую ядро лимитирует CPU/память/I/O процессов. Docker/k8s/systemd пишут сюда.
  • kubelet-internalskubelet - архитектура агента ноды Kuberneteskubelet - демон на каждой ноде. Получает PodSpec через API, запускает контейнеры через CRI, монтирует volumes через CSI, следит за health. При pressure делает eviction. Image GC и cgroup-tree - тоже его.
  • oom-killerOOM killerOOM killer - механизм ядра, который выбирает и завершает процесс когда система упирается в лимит памяти. В контейнерах работает per-cgroup.
  • systemdsystemd - init и менеджер сервисовsystemd - init-система Linux: PID 1, который запускает всё остальное, следит за зависимостями, перезапускает упавшее, агрегирует логи.
  • docker-storage-driversDocker storage drivers - overlay2, btrfs, zfsStorage driver - как Docker хранит image-layers и контейнер-changes на диске. overlay2 default (overlayfs над ext4/xfs), btrfs/zfs через subvolumes/snapshots, fuse-overlayfs для rootless.
  • namespacesLinux namespacesNamespaces - механизм ядра, который даёт процессу собственный изолированный view на ресурс (сеть, mount-points, PID, UID, IPC, hostname, time). На них построены все контейнеры.
  • prometheus-basicsPrometheus: scrape, TSDB, PromQL и production-pitfallsPrometheus - сервер мониторинга: сам опрашивает приложения по HTTP, собирает числовые метрики, хранит во встроенной БД ~15 дней. По ним строят графики в Grafana и алерты через Alertmanager. Стандарт de-facto в Kubernetes.
  • pyroscope-continuous-profilingContinuous profiling: Pyroscope, eBPF, flame graphs в продеContinuous profiling - always-on CPU/memory profiler в проде через eBPF. 1-2% overhead. Flame graphs показывают hot path. Pyroscope (Grafana), Parca, Polar Signals. Замена ad-hoc perf для production debug.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки