Manage Docker with Cgroups

Управлявайте Docker ресурси с контролни групи

Миналия месец говорих на конференция на Apache за контролните групи (Cgroups, сигрупите). Изглежда доста малко ползватели на Linux (доскоро дори и аз) са запознати с тях и силата им. Това е жалко, защото сигрупите са доста мощни и ви позволяват да разпределите ресурсите на сървърите си по далеч по-детайлен начин от всеки друг инструмент на разположение в Linux. Нещо повече, той е внедрен директно в ядрото и следователно е неделима част от повечето Linux дистрибуции.

В този пост ще дам насоки как да ползвате Cgroups с Docker, нещо като надграждане над друга лекция, която изнесох на Docker конференцията в Остин, Тексас.

Статията изисква базови познания за Cgroups. Ако сте пълен новак в темата, то по-добре вижте презентацията ми от конференцията ApacheCon. Все пак не се притеснявайте – няма да задълбаваме. Като хвърлите едно око на презентацията, ще схванете основите на концепцията.

Docker и Cgroups

Docker идва с два различни драйвъра – LXC и libcontainer. LXC драйверът е наследственият драйвър, а libcontainer е новият и ползван по подразбиране. В повечето случаи той е предпочитаният, тъй като се ползва за по-иновативни неща (например docker exec не работи с LXC драйвъра).

Въпреки това е важно да се споменат и двата драйвъра, понеже не всички сигрупи са преминали на libcontainer. Когато ползвате LXC драйвър, просто директно предавате LXC аргументи, а с libcontainer има директно изложение към Docker. Когато включвате Docker, трябва да настроите конкретен драйвър, не можете да пуснете два драйвъра едновременно.

Ето пример как да проверите:

[bash light=”true”] # With the LXC driver
$ docker run -d –name=’lxc_test’ \
–lxc-conf="lxc.cgroup.cpu.shares=50" \
busybox

# With the libcontainer driver
$ docker run -d –name=’libcontainer_test’ \
–cpu-shares=50 \
busybox
[/bash]

Ето и преглед на някои от функциите на Cgroups и как превключват между двата драйвъра:

Функция Libcontainer LXC
Относителен CPU дял -c, –cpu-shares –lxc-conf=”lxc.cgroup.cpu.shares”
Заключване за CPU ядро –cpuset-cpus –lxc-conf=”lxc.cgroup.cpuset.cpus”
Limit memory -m, –memory –lxc-conf=”lxc.cgroup.cpuset.mems”

LXC

Ако искате да ползвате LXC драйвъра за Docker, трябва първо да го активирате. Методът е различен според версиите за Linux, ето например инструкции за Ubuntu 14.04.

Както споменах по-горе, по този начин се отказвате от редица опции. Затова ви препоръчваме да ползвате libcontainer, освен ако не ви е нужна функция на Docker, която е несъвемстима с Docker libcontainer.

Някои полезни полици за сигрупи все още не са въведени в libcontainer, а може да са доста полезни за различни приложения, например I/O ограничаване.

Ако сте избрали LXC драйвър, добавянето на аргументи е доста лесно. Просто ги слагате и добавяте –lxc-conf и парола към полицата за сигрупата, която искате да настроите.

Libcontainer

Както се вижда в таблицата горе, базовите полици за сигрупи вече са въведени в настоящата версия на Docker.

Използването им е доста просто. Ако например искате да заключете Docker контейнер за първия центален процесор, добавяте --cpuset-cpus=0 към командата за стартиране на Docker.

Може да ползвате и the --cgroup-parent аргумента с libcontainer и ръчно да настроите по-подробни ресурсните ограничения. След това ще го свържете.

Демо: Docker със Cgroups

В скрийнкаста долу сме ползвали два Docker контейнтера (‘low_prio’ и ‘high_prio’). Ползваме базовия контейнер ‘busybox’ и пускаме md5sum /dev/urandom, за да симулираме „глад” на цетралния процесор. Това ще изразходва всичките му налични ресурси. Затова ще приложим две полици за сигрупи, за да управляваме ресурсите.
Първо използваме ‘cpuset.cpus’, за да заключим контейнерите към едно и също ядро (ядро 0).

След това използваме ‘cpu.shares’, за да назначим относително споделяне на процесора. Даваме на ‘low_prio’ контейнера стойност от 20, а на ‘high_prio’ – 80. Това означава, че 20% от процесора ще отидат в ‘low_prio’ контейнера, а другите 80% – в ‘high_prio’. Имайте предвид, че относителният дял е произволна скала (можехме да използваме също така стойности като 2 и 8).

След като демонстрирахме, че управлението на ресурсите работи, пускаме същото количество контейнери без полици за сигрупи, за да видим какво се случва.

За сравнение, ето командите за стартиране на контейнерите.

[bash light=”true”] $ docker run -d \
–name=’low_prio’ \
–cpuset-cpus=0 \
–cpu-shares=20 \
busybox md5sum /dev/urandom
$ docker run -d \
–name=’high_prio’ \
–cpuset-cpus=0 \
–cpu-shares=80 \
busybox md5sum /dev/urandom
[/bash]

Обобщение

Ако управлявате множество Docker контейнери на един хост, използването на сигрупи за уравлението на ресурсите между контейнерите има голям смисъл. Това важи например за ситуация, в която имате процеси на заден план в един контейнер, а друг контейнер служи за потребителското съдържание. В този случай можете да използвате новопридобитото знание за сигрупите, за да сте сигурни, че приоритизирате потребителските контейнери над второстепенните задачи.