В прошлом месяце я выступал на ApacheCon с докладом о Cgroups. Похоже, очень немногие пользователи Linux (включая вашего покорного слугу до недавнего времени) знакомы с Cgroups и их возможностями. И это прискорбно, поскольку Cgroups — это очень мощные и позволяют распределять ресурсы на серверах гораздо более гибко и детально, чем любое другое средство из арсенала Linux. Более того, они встроены непосредственно в ядро и поставляются «из коробки» в большинстве дистрибутивов Linux.
В этой статье я расскажу, как использовать Cgroups с Docker, что станет своего рода продолжением другого моего доклада на Docker Meetup в Остине, штат Техас.
Эта статья требует некоторого базового понимания Cgroups. Если вы совсем не знакомы с Cgroups, я рекомендую вам ознакомиться с моей презентацией с ApacheCon, а также с ресурсами в конце статьи. Но не волнуйтесь, мы не будем погружаться слишком глубоко. Если вы просто бегло просмотрите слайды, вы сможете уловить основные концепции.
Docker и Cgroups
Docker поставляется с двумя различными драйверами: LXC и libcontainer. Драйвер LXC является устаревшим, а libcontainer — новым драйвером по умолчанию. В большинстве случаев предпочтительным является libcontainer, так как именно в нем происходят все нововведения (например, docker exec не работает с драйвером LXC).
Однако важно отметить наличие двух разных драйверов, поскольку еще не все возможности Cgroup были перенесены в libcontainer (или, по крайней мере, открыты для Docker). При использовании драйвера LXC вы просто передаете аргументы LXC напрямую, тогда как в случае с libcontainer для Docker доступны явные аргументы политик Cgroup. Вам нужно будет явно указать драйвер при запуске демона Docker, поэтому вы не можете запускать оба драйвера одновременно.
Вот пример того, как вы можете это проверить:
[bash light=”true”] # С драйвером LXC$ docker run -d –name=’lxc_test’ \
–lxc-conf="lxc.cgroup.cpu.shares=50" \
busybox
# С драйвером libcontainer
$ docker run -d –name=’libcontainer_test’ \
–cpu-shares=50 \
busybox
[/bash]
Вот обзор некоторых функций Cgroup и того, как они соотносятся между двумя драйверами:
| Функция | Libcontainer | LXC |
|---|---|---|
| Относительная доля CPU | -c, –cpu-shares | –lxc-conf=”lxc.cgroup.cpu.shares” |
| Привязка к ядру CPU | –cpuset-cpus | –lxc-conf=”lxc.cgroup.cpuset.cpus” |
| Ограничение памяти | -m, –memory | –lxc-conf=”lxc.cgroup.cpuset.mems” |
LXC
Если вы хотите использовать драйвер LXC для Docker, вам сначала нужно его включить. Способ включения зависит от вашего дистрибутива Linux, но вот инструкции о том, как включить драйвер LXC в Ubuntu 14.04.
Как упоминалось выше, при этом вы отказываетесь от ряда возможностей. Поэтому, если вам действительно не нужна функциональность, которая еще не реализована в Docker с использованием libcontainer, вам лучше придерживаться драйвера по умолчанию.
Некоторые полезные политики Cgroup, которые еще не реализованы в libcontainer, включают ограничение ввода-вывода (I/O throttling, описанное в презентации), что может быть очень полезно для определенных приложений.
Если вы решили использовать драйвер LXC, добавление аргументов не составит труда. Все, что вам нужно сделать, это добавить аргумент --lxc-conf и передать политику Cgroup, которую вы хотите установить.
Libcontainer
Как видно из таблицы выше, базовые политики Cgroup уже доступны в текущей версии Docker (1.6 на момент написания статьи).
Использовать эти политики очень просто. Если вы, например, хотите привязать контейнер Docker к первому ядру процессора, вы должны добавить --cpuset-cpus=0 к вашей команде docker run.
Вы также можете использовать аргумент --cgroup-parent с libcontainer и вручную задавать более точные ограничения ресурсов. Затем вы свяжете контейнер с этой группой с помощью этого аргумента.
Демо: Docker с Cgroups
В скринкасте ниже мы будем использовать два Docker-контейнера (‘low_prio’ и ‘high_prio’). Мы используем базовый контейнер ‘busybox’ и запускаем md5sum /dev/urandom, чтобы симулировать процесс, ресурсоемкий для CPU. По умолчанию это потребляло бы все доступные ресурсы CPU. Однако мы применим две политики Cgroup для управления ресурсами. Сначала мы используем ‘cpuset.cpus’, чтобы привязать контейнеры к одному и тому же ядру CPU (ядро 0).
Затем мы используем ‘cpu.shares’ для назначения относительной доли CPU. Мы даем контейнеру ‘low_prio’ значение 20, а ‘high_prio’ — значение 80. Это означает, что 20% CPU будет выделено контейнеру ‘low_prio’, а 80% CPU — контейнеру ‘high_prio’. Обратите внимание, однако, что относительная доля — это произвольная шкала (мы могли бы с тем же успехом использовать значения 2 и 8).
После того как мы продемонстрируем, что управление ресурсами действительно работает, мы запустим тот же набор контейнеров без каких-либо политик Cgroup, чтобы посмотреть, как они себя ведут.
Для справки, вот команды, используемые для запуска контейнеров.
[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 на одном хосте, использование Cgroups для управления ресурсами между контейнерами имеет большой смысл. Например, возможно, у вас есть задачи фоновой обработки, выполняющиеся в одном контейнере, и другой контейнер, обслуживающий пользовательский контент. В этом случае вы можете использовать свои новые знания, чтобы гарантировать, что вы отдадите приоритет контейнерам, ориентированным на пользователя, перед фоновыми задачами.
Комментарии
Комментариев пока нет. Будьте первым.