🌌 Маскування енергоспоживання капсули — «Диспетчер не лише ховає — він ще й пайкує, і дивись, як гарно це влаштовано», — тішиться С.І.Д. Капсула не повинна світитися на моніторингу ресурсів «ТехНови», тож розберися разом із ним, як через cgroups v2 виставити жорсткі ліміти CPU й пам'яті, щоб капсула не видала себе аномальним стрибком навантаження.
Простори імен відповідають на питання «що процес бачить», а групи керування (control groups, cgroups) — на питання «скільки ресурсів процес може спожити». Це два незалежні механізми ядра: namespaces ізолюють, cgroups обмежують і обліковують. Docker використовує обидва одночасно — саме тому один контейнер не бачить процесів іншого (namespaces) і водночас не може забрати всю пам'ять хоста (cgroups).
Сучасні дистрибутиви (і Docker Engine з версії 20.10+ за замовчуванням, включно з
Ubuntu 24.04) працюють на cgroups v2 — уніфікованій ієрархії, змонтованій в одну
точку /sys/fs/cgroup, на відміну від застарілої v1, де для кожного контролера
(memory, cpu, pids, io) існувала окрема ієрархія. У v2 кожна cgroup — це
каталог у файловій системі cgroupfs, а обмеження виражені простими текстовими файлами
всередині нього: memory.max, memory.high, cpu.max, pids.max, io.max тощо.
Прапорець docker run --memory=100m створює для контейнера власну cgroup і записує в
memory.max значення 104857600 (байти). Коли сума використаної процесами контейнера
resident-пам'яті наближається до цієї межі, ядро спершу намагається звільнити кеш
сторінок (reclaim), а якщо це не допомагає — активує cgroup-локальний OOM-кілер,
який вбиває процес(и) тільки всередині цієї cgroup, не чіпаючи інші контейнери й хост.
Це принципова відмінність від системного OOM: одна цятка на моніторингу, а не крах усієї
машини.
Прапорець --cpus=0.5 транслюється в пару файлів cpu.max виду 50000 100000 — це
означає «не більш ніж 50 000 мікросекунд процесорного часу з кожних 100 000
мікросекунд» (тобто половина одного ядра в середньому за період). Це м'яке обмеження
через CFS bandwidth controller: контейнер може короткочасно використати ціле ядро в
межах кванта, але за період буде «притиснутий» throttling'ом, що видно в
cpu.stat (поле nr_throttled). Окремо варто розрізняти --cpus (частка ядра) і
--cpuset-cpus (жорстка прив'язка до конкретних номерів ядер) — перше обмежує обсяг
часу, друге — фізичну локацію виконання.
Для діагностики варто дивитися не лише на ліміти, а й на фактичне споживання:
docker stats дає зручний живий знімок, а пряме читання /sys/fs/cgroup/.../ memory.current, memory.max, cpu.stat, pids.current — те саме джерело даних без
посередників, корисне, коли потрібно зрозуміти, наскільки близько контейнер підійшов
до межі ще до того, як спрацює OOM-кілер.
Прочитати cgroups(7) і розділ Docker docs «Resource constraints»; запустити контейнер з docker run --memory=100m --cpus=0.5 і переглянути відповідні файли в ієрархії /sys/fs/cgroup/ (memory.max, cpu.max).
Що станеться з процесом усередині контейнера, якщо він спробує виділити пам'яті більше, ніж дозволяє memory.max його cgroup — і чим ця поведінка відрізняється від класичного OOM на хості без лімітів?
0xC1D. Тому, виставляючи memory.max, не опускайся нижче — «інакше я мовчатиму, як 101.9 у порожньому класі», — а якщо memory.current колись покаже саме це число, знай: він просто позує.Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.
docker rundocker run -it ubuntu:24.04 bash — інтерактивно з терміналом; -d — у фоні; --rm — видалити після виходу; -p 8080:80 — прокинути порт).dockerdocker <підкоманда> --help — довідка).docker execdocker exec -it storage sh).docker inspect-f '{{.State.Pid}}' — витягнути одне поле.docker stats--no-stream — один знімок).💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.