### A note on resource reporting tools
The tools you are used to using to report usage like `top`, `/proc/meminfo` and so on **are not cgroups aware**. This means that they’ll report the information about the host even if we run them inside of a container. I found a [nice blog post](http://fabiokung.com/2014/03/13/memory-inside-linux-containers/) from Fabio Kung on this topic. Give it a read.
So, what can we do?
If you want to quickly find which container (or any systemd service, really) uses the most resources on the host I recommend the `systemd-cgtop` command:
systemd-cgtop [OPTIONS...] [CGROUP]
Show top control groups by their resource usage.
`-h --help Show this help`
`--version Show package version`
`-p --order=path Order by path`
`-t --order=tasks Order by number of tasks/processes`
`-c --order=cpu Order by CPU load (default)`
`-m --order=memory Order by memory load`
`-i --order=io Order by IO load`
`-r --raw Provide raw (not human-readable) numbers`
`--cpu=percentage Show CPU usage as percentage (default)`
`--cpu=time Show CPU usage as time`
`-P Count userspace processes instead of tasks (excl. kernel)`
`-k Count all processes instead of tasks (incl. kernel)`
`--recursive=BOOL Sum up process count recursively`
`-d --delay=DELAY Delay between updates`
`-n --iterations=N Run for N iterations before exiting`
`-1 Shortcut for --iterations=1`
`-b --batch Run in batch mode, accepting no input`
`--depth=DEPTH Maximum traversal depth (default: 3)`
`-M --machine= Show container`
![[Pasted image 20251111194234.png]]
Отличный вопрос 👇
`systemd-cgtop` — это **утилита мониторинга**, которая показывает **использование ресурсов (CPU, память, I/O, задачи)** в реальном времени по _control groups (cgroups)_, то есть по **юнитам systemd, контейнерам, сервисам и процессам**.
---
## 🧩 Что такое `systemd-cgtop`
- Это аналог `top`, но не для отдельных процессов, а для **групп процессов**, которыми управляет **systemd**.
- Каждая строка в выводе — это **cgroup**: например, контейнер Docker, сервис systemd, юнит `.scope`, пользовательская сессия и т.д.
---
## 📊 Пример вывода
```bash
$ systemd-cgtop
Path Tasks %CPU Memory Input/s Output/s
/ 327 5.0 1.2G
/user.slice 89 2.0 500M
/system.slice 220 3.0 700M
/docker-45ea2142f956c5127ef4f87e8d7bf78db830abb355d1d220644bf40efd95fdf6.scope 6 15.0 250M
```
### Поля:
| Поле | Значение |
| --------------------- | ------------------------------------------------------------------------ |
| **Path** | Имя _cgroup_, обычно совпадает с сервисом, контейнером или пользователем |
| **Tasks** | Количество процессов (или потоков) в группе |
| **%CPU** | Доля CPU, потребляемая всеми процессами этой группы |
| **Memory** | Используемая память |
| **Input/s, Output/s** | Скорость чтения/записи данных (в байтах/с) |
---
## ⚙️ Примеры использования
### 🔹 Обычный просмотр:
```bash
systemd-cgtop
```
### 🔹 Обновление каждые 2 секунды:
```bash
systemd-cgtop --delay=2s
```
### 🔹 Показать только активные группы:
```bash
systemd-cgtop --hide-unmoved
```
### 🔹 Показать больше процессов (не только 10 по умолчанию):
```bash
systemd-cgtop -n 0
```
---
## 🐳 В контексте Docker
Когда ты видишь такие строки:
```
docker-45ea2142f956c5127ef4f87e8d7bf78db830abb355d1d220644bf40efd95fdf6.scope
```
— это конкретный **контейнер Docker**, который systemd отслеживает как отдельную _scope-группу_.
В `systemd-cgtop` ты можешь увидеть:
- сколько CPU он потребляет,
- сколько оперативки занимает,
- сколько процессов в контейнере,
- и его I/O-активность.
---
## 💡 Применение в DevOps
`systemd-cgtop` полезен для:
- наблюдения за нагрузкой контейнеров и сервисов;
- отладки runaway-процессов;
- проверки ограничений ресурсов (`CPUQuota`, `MemoryMax`);
- диагностики проблем в Docker, Podman, LXC, systemd-nspawn и т.д.
---
Хочешь, я покажу, как с помощью `systemd-cgtop` можно _в реальном времени отследить_, какой контейнер потребляет больше всего CPU и памяти, с автоматическим логированием в файл?
Отлично. Ниже — готовые варианты, как **в реальном времени** смотреть, какой контейнер жрёт больше CPU/памяти, и **логировать это в файл**.
---
# Вариант A — быстрый просмотр Top-N контейнеров (без логов)
**CPU (топ-5):**
```bash
watch -n 1 'systemd-cgtop -b -n1 | { read h; read h2; printf "%s\n%s\n" "$h" "$h2"; grep docker- | sort -k3,3 -hr | head -5; }'
```
**Память (топ-5):**
```bash
watch -n 1 'systemd-cgtop -b -n1 | { read h; read h2; printf "%s\n%s\n" "$h" "$h2"; grep docker- | awk "NR>0{print}" | sort -k4,4 -hr | head -5; }'
```
> Пояснение: `systemd-cgtop -b -n1` — один «снимок» в batch-режиме, `grep docker-` — только контейнеры (`*.scope`), `sort -h` — «человеческая» сортировка по столбцу `%CPU` (3-й) или `Memory` (4-й).
---
# Вариант B — простой **логер** в CSV (каждую секунду)
Создаст файл `cgtop-docker.csv` с колонками:
`timestamp,path,tasks,cpu_percent,memory`
```bash
#!/usr/bin/env bash
set -euo pipefail
LOG="cgtop-docker.csv"
INTERVAL="1s"
# Заголовок (если файла нет)
[ -f "$LOG" ] || echo "timestamp,path,tasks,cpu_percent,memory" > "$LOG"
while :; do
TS="$(date -Is)"
# Один срез cgtop
systemd-cgtop --batch --iterations=1 --delay="$INTERVAL" \
| awk -v ts="$TS" '
NR<=2 { next } # пропускаем 2 строки заголовка
$1 ~ /^docker-.*\.scope$/ { # только docker *.scope
# Ожидаемый формат столбцов:
# Path Tasks %CPU Memory Input/s Output/s
path=$1; tasks=$2; cpu=$3; mem=$4;
gsub(/%/,"",cpu); # убираем символ %
print ts "," path "," tasks "," cpu "," mem
}' >> "$LOG"
done
```
Запуск:
```bash
chmod +x ./cgtop-logger.sh
sudo ./cgtop-logger.sh
```
Остановить: `Ctrl+C`.
Посмотреть «топ по CPU» за последние 200 строк:
```bash
tail -n 200 cgtop-docker.csv | sort -t, -k4,4nr | head
```
---
# Вариант C — логер + сводка «кто лидер сейчас»
Этот скрипт на каждом шаге печатает две строки-сводки: **TOPCPU** и **TOPMEM**.
```bash
#!/usr/bin/env bash
set -euo pipefail
LOG_RAW="cgtop-docker.csv"
LOG_SUMMARY="cgtop-summary.log"
INTERVAL="1s"
[ -f "$LOG_RAW" ] || echo "timestamp,path,tasks,cpu_percent,memory" > "$LOG_RAW"
while :; do
TS="$(date -Is)"
systemd-cgtop --batch --iterations=1 --delay="$INTERVAL" \
| awk -v ts="$TS" -v raw="$LOG_RAW" -v sum="$LOG_SUMMARY" '
BEGIN {
maxcpu = -1; maxcpupath = "";
maxmem = -1; maxmempath = "";
}
NR<=2 { next }
$1 ~ /^docker-.*\.scope$/ {
path=$1; tasks=$2; cpu=$3; mem=$4;
# лог сырого CSV
cpu_pct=cpu; gsub(/%/,"",cpu_pct);
print ts "," path "," tasks "," cpu_pct "," mem >> raw;
# CPU лидер
cpu_num=cpu_pct+0;
if (cpu_num > maxcpu) { maxcpu=cpu_num; maxcpupath=path }
# нормализуем память в MiB
mem_s=mem;
unit_tol=tolower(mem_s);
gsub(/[^0-9.]/,"",mem_s);
mem_val=mem_s+0;
if (unit_tol ~ /g/) mem_mib=mem_val*1024;
else if (unit_tol ~ /m/) mem_mib=mem_val;
else if (unit_tol ~ /k/) mem_mib=mem_val/1024;
else mem_mib=mem_val; # fallback
if (mem_mib > maxmem) { maxmem=mem_mib; maxmempath=path }
}
END {
if (maxcpupath != "") printf("%s TOPCPU %s %.2f%%\n", ts, maxcpupath, maxcpu) >> sum;
if (maxmempath != "") printf("%s TOPMEM %s %.2f MiB\n", ts, maxmempath, maxmem) >> sum;
}'
done
```
Просмотр сводки:
```bash
tail -f cgtop-summary.log
```
---
# Вариант D — как сервис systemd (фон, автозапуск)
`/etc/systemd/system/cgtop-logger.service`
```ini
[Unit]
Description=Log Docker cgroups via systemd-cgtop
After=network.target docker.service
[Service]
Type=simple
ExecStart=/usr/local/bin/cgtop-logger.sh
Restart=always
RestartSec=2s
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
[Install]
WantedBy=multi-user.target
```
Положите скрипт в `/usr/local/bin/cgtop-logger.sh`, сделайте исполняемым.
Далее:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now cgtop-logger.service
sudo journalctl -u cgtop-logger.service -f
```
(Опционально — сделать `*.timer`, если нужен периодический запуск.)
---
## Полезные замечания
- Колонки `systemd-cgtop` могут слегка отличаться между версиями systemd (но порядок `Path Tasks %CPU Memory ...` стабилен).
- Если у вас **cgroup v2**, вывод всё равно совместим, скрипты выше работают.
- Если контейнеры управляются не Docker, а Podman/k8s-cri, меняется только префикс (`libpod-...scope`, `kubepods-...`), достаточно заменить `grep docker-` на соответствующий паттерн (или убрать фильтр вообще).
Если хочешь — подгоню скрипт именно под твой вывод (cgroup v1/v2, суффиксы памяти, нужные интервалы) и добавлю выгрузку в Prometheus-совместимый формат.