# cgroups v2 - unified hierarchy, PSI, eBPF control _Процессы и ресурсы · LinuxLab Knowledge Base_ **TL;DR:** cgroups v2 - один tree вместо отдельных hierarchies на controller. Чистая семантика, новые fields (memory.high, io.cost). PSI показывает resource pressure. eBPF может управлять resources. Default в RHEL 9, Ubuntu 22+. ## Зачем cgroups v2 [[cgroups|cgroups v1]] (с 2007) - **per-controller hierarchy**: для каждого resource (cpu, memory, blkio, net_cls...) - отдельное дерево в `/sys/fs/cgroup//...`. Процесс мог быть в разных 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 | Controller | v1 эквивалент | Что делает | |------------|---------------|------------| | **cpu** | cpu+cpuacct | weight, max, period - CPU shares и hard limit | | **cpuset** | cpuset | привязка к CPU/NUMA-node | | **memory** | memory | usage, low, high, max, soft limit | | **io** | blkio | weight, max BW, BW limits | | **pids** | pids | ограничение по числу процессов | | **rdma** | rdma | InfiniBand resources | | **misc** | - | gpu hpu - кастомизируемые лимиты | | **hugetlb** | hugetlb | huge 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.min` | reserved, никогда не reclaim'ится (если есть chance) | | `memory.low` | best-effort protected - reclaim только под pressure | | `memory.high` | soft limit - kernel начинает throttling/reclaim | | `memory.max` | hard limit - превышение = OOM в этой cgroup | | `memory.swap.max` | swap-лимит отдельно | | `memory.events` | counters: 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](/kb/oom-killer.md)) Это даёт graceful backpressure вместо OOMK-цикла. ## CPU controller - weight и max ``` cpu.weight # 1-10000 (default 100), относительный вес cpu.max # "max ", напр. "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 - сравнение | Свойство | v1 | v2 | |----------|-----|-----| | Дерево | per-controller | unified | | Process в нескольких? | да (один на controller) | нет | | Threaded mode | нет | да (`cgroup.type` = threaded) | | Soft memory limit | да (memory.soft_limit_in_bytes) | нет (используй memory.high) | | OOM behavior | OOM в cgroup | + memory.high throttling | | PSI | нет | да | | eBPF integration | минимальная | первоклассная | | Default in distros (2025) | RHEL 7-8, Ubuntu < 21 | RHEL 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//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//cgroup.subtree_control ``` Включить controllers для children этой cgroup ## См. также - [cgroups (v2)](/kb/cgroups.md) - [kubelet - архитектура агента ноды Kubernetes](/kb/kubelet-internals.md) - [OOM killer](/kb/oom-killer.md) - [systemd - init и менеджер сервисов](/kb/systemd.md) - [Docker storage drivers - overlay2, btrfs, zfs](/kb/docker-storage-drivers.md) - [Linux namespaces](/kb/namespaces.md) - [Prometheus: scrape, TSDB, PromQL и production-pitfalls](/kb/prometheus-basics.md) - [Continuous profiling: Pyroscope, eBPF, flame graphs в проде](/kb/pyroscope-continuous-profiling.md)