Авторские статьи об OpenSource

LXD 2.0: Контроль ресурсов [4/12].


Контейнеры нужно уметь ограничивать в ресурсах, которые им предоставляет хостовая операционная система. Научимся создавать лимиты и указывать приоритеты.

Доступные лимиты ресурсов

LXD предлагает различные возможности для ограничения ресурсов. Некоторые ограничения применимы к самому контейнеру: квоты ОЗУ, лимиты CPU и приоритеты I/O. Некоторые ограничения связаны с конкретным устройством типа пропускной способности I/O или лимитами дискового использования.

Как и конфигурации LXD, лимиты на ресурсы можно динамически изменять "на лету". Иногда операция может завершиться с ошибкой. К примеру, вы выставляете лимит ОЗУ ниже, чем сейчас контейнер потребляет сейчас, но в любом случае LXD будет пытаться выполнить ваше задание и доложит о неудаче.

Все лимиты могут быть унаследованы из профилей, применяемые к данному контейнеру. То есть установка limits.memory=256MB в профиле default заставит контейнеры, использующие его (как правило, все), иметь максимальный объём ОЗУ = 256 Мб.

Не поддерживается ограничение ресурсов на группу контейнеров, так как нет простого и хорошего способа реализовать подобное на существующем kernel API.

Диск

Наиболее востребованная функциональность - установить ограничение на размер файловой системы контейнера. LXD позволяет это сделать, хотя это сложнее выполнить, чем это кажется на первый взгляд. В Linux нет возможности установить квоты на каталог (path-based quotas), а большинство файловых систем оперируют квотами на пользователя или группу, что мало полезно для контейнеров.

Это означает, что в данный момент LXD поддерживает только лимиты для диска, только если используется ZFS или btrfs. Возможно реализовать данный функционал и для LVM, но тут всё в большей степени зависит от файловой системы, так как не все ФС позволяют онлайн увеличение (online growth) или уменьшение (online shrink) своего размера.

CPU

Когда речь заходит о ЦПУ, то поддерживаются 4 вещи:

  • Просто дайте мне X. В этом режиме вы позволяете LXD отобрать часть ваших ядер процессора и автоматически балансировать при нагрузке.
  • Дайте конкретный набор ядер (скажем 1,3 и 5). Подобен первому режиму, но исключая автобалансировку. Вы будете работать на конкретных ядрах, независимо на сколько они заняты.
  • Дайте мне 20%. В этом режиме вы увидите все ядра, но планировщик ограничит вас 20% в CPU при загрузке хостовой системы. Если система не занята, то контейнер может получить процессорного времени сколько ему хочется, но если другие контейнеры начнут использование CPU, то ваш контейнер будет ограничен.
  • Из каждых измеренных 200 мс дайте мне 50 мс и не более того. Этот режим похож на предыдущий в том, что вы видите все ядра, но использовать процессорное время будете как указано в лимите, независимо простаивает хостовая система или нет. В системах без over-commit это позволяет нарезать CPU более аккуратно и гарантирует постоянную производительность для этих контейнеров.

Можно комбинировать первый режим с одним из двух последних. Запросить ограничить количество ЦПУ и дополнительно ограничить процессорное время.

Кроме того, имеется регулятор приоритета, который используется для подсказок планировщику (scheduler) кто побеждает когда система загружена и 2 контейнера борются за один и тот же ресурс.

Память

Лимиты про память звучат просто - дайте мне X Мб ОЗУ! Поддерживаются запросы с помощью процентов - дайте мне 10% ОЗУ хоста! Так же можете реализовать следующее: выбрать включён или не включён swap на уровне каждого контейнера и если он включён, то указать контейнеру чтобы выгружал память в swap в первую очередь.

Ограничения память по умолчанию жёсткие (hard). То есть когда кончится память, то OOM Killer (out of memory killer) начнёт резвиться с вашими процессами. В качестве альтернативы вы можете установить мягкие (soft) ограничения, тогда позволено будет потреблять сколько хотите пока кто-то так же не потребует памяти. Вам не будет выделено памяти до тех пор пока не отдадите обратно ниже своего лимита или пока хост не получит память от других контейнеров.

Сетевой I/O

Поддерживаются две вещи:

  • Простое ограничение в бит/сек сетевого интерфейса. Можете указать ограничение для входящего, исходящего или для обоих. Применимо только для сетевых интерфейсов типа bridged и p2p.
  • Сетевой приоритет I/O применяется когда интерфейс, через который идёт обмен, становится перегруженным.

Блочный I/O

Самое странное напоследок. Вам покажется, что всё просто, но есть масса случаев, в которых всё будет работать не так как вы задумали.
Поддержка ограничений I/O для блочных устройств подобна вышеописанному ограничению сетевых интерфейсов. Вы можете установить лимиты IOPS или байт/сек на чтение и запись. Указать приоритет блочного I/O, который подсказывает планировщику I/O о предпочтениях.

Странность появляется от того, как и где эти лимиты применяются. К сожалению, основная особенность реализации ограничений в том, что они применяются ко всему блочному устройству. Это означает, что вы не можете установить ограничение ввода-вывода на раздел и тем более на путь в ФС. Это так же означает, что при использовании ZFS или btrfs, которые могут использовать несколько блочных устройств, мы гарантированно не можем знать какое блочное устройство обслуживает данный путь. Это означает, что, вполне возможно, контейнер может иметь множество записей про блочные устройства (bind-mount или прямые mount), которые ссылаются на тот же нижележащий диск.

И вот тут всё становится действительно странным. Для того, чтобы всё работало, LXD имеет логику, выясняющая какие блочные устройства связаны с данным путём. Опрашиваются ZFS и btrfs инструменты и рекурсивно распутываются петлевые (loop) устройства. Логика хоть и не идеальна, но даёт множество блочных устройств, к которым нужно применить лимиты. LXD, получив все пути, попадает в реально странную область. Среднее значение лимитов, установленное вами для каждого блочного устройства, применяется к ним. Это означает, что "в среднем" вы будете получать правильные значения в контейнере, но это так же означает, что вы не сможете иметь /fast и /slow каталоги на том же физическом диске и с различающимися лимитами скорости. LXD позволит установить значения, но в реальности скорость будет как среднее из этих двух значений.

Как это всё работает?

Большинство ограничений, описанных выше, применяются с помощью Linux cgroups API. Сетевые лимиты применяются через старый, добрый tc.
LXD при старте определяет какие cgroup есть в ядре хоста и использует только доступные. Если какие-либо cgroup отсутствуют, то вам будет выведено сообщение и помещено в журнал. В Ubuntu 16.04 всё доступно по умолчанию, за исключением учёта swap, требующая от вас указания swapaccount=1 параметру ядра системы.

Применение лимитов

Все лимиты применяются непосредственно к контейнеру или к одному из его профилей.
Ко всему в пределах контейнера - lxc config set CONTAINER KEY VALUE
Ко всему в пределах данного профиля - lxc profile set PROFILE KEY VALUE
Можно более тонко указать ограничения:
для конкретного устройства контейнера - lxc config device set CONTAINER DEVICE KEY VALUE
для конкретного устройства в профиле - lxc profile device set CONTAINER DEVICE KEY VALUE

Полный список опций конфигурации, типы устройств и ключи для них доступны в документации configuration

CPU

Лимит для контейнера только 2 ЦПУ - lxc config set my-container limits.cpu 2
Указываем использовать конкретные ядра - lxc config set my-container limits.cpu 1,3
Более сложный пример, с использованием диапазонов - lxc config set my-container limits.cpu 0-3,7-11

Ограничения применяются на лету и это подтверждает пример:
lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
processor : 3

lxc config set zerotier limits.cpu 2
lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1

Обратите внимание, чтобы избежать запутывания userspace, lxcfs упорядочивает /proc/cpuinfo таким образом, чтобы не было промежутков-окон (gaps).

Как и всё в LXD, ограничения могут применяться из профиля
lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
processor : 3

lxc profile set default limits.cpu 3
lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2

Указываем лимиты процессорного времени в 10% от общего - lxc config set my-container limits.cpu.allowance 10%
Жёстко нарезаем процессорное время - lxc config set my-container limits.cpu.allowance 25ms/200ms
Уменьшаем приоритет контейнера до минимума - lxc config set my-container limits.cpu.priority 0

Memory

Просто ограничиваем предел ОЗУ - lxc config set my-container limits.memory 256MB Поддерживаются суффиксы kB, MB, GB, TB, PB и EB.

Отключить swap для контейнера (включён по умолчанию) - lxc config set my-container limits.memory.swap false
Объяснить ядру, что приоритетнее вытеснение памяти в swap - lxc config set my-container limits.memory.swap.priority 0
Переключаем лимиты из hard в soft - lxc config set my-container limits.memory.enforce soft

Диск и блочный I/O

В отличие от процессора и памяти, ограничения диска и ввода-вывода применяются к конкретному физическому устройству. Устанавливаем квоту (требуется btrfs и ZFS) - lxc config device set my-container root size 20GB
Пример
lxc exec zerotier -- df -h /

Filesystem                        Size Used Avail Use% Mounted on
encrypted/lxd/containers/zerotier 179G 542M  178G   1% /

lxc config device set zerotier root size 20GB
lxc exec zerotier -- df -h /

Filesystem                       Size  Used Avail Use% Mounted on
encrypted/lxd/containers/zerotier 20G  542M   20G   3% /

Скорость указываем так
lxc config device set my-container limits.read 30MB
lxc config device set my-container limits.write 10MB

Можно в IOPS
lxc config device set my-container limits.read 20Iops
lxc config device set my-container limits.write 10Iops

На загруженной системе для увеличения приоритета I/O на максимум - lxc config set my-container limits.disk.priority 10

Network I/O

Пример
lxc exec zerotier -- wget speedtest.newark.linode.com/100MB-newark.bin -O /dev/null

--2016-03-26 22:17:34-- speedtest.newark.linode.com/100MB-newark.bin
Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 104857600 (100M) [application/octet-stream]
Saving to: '/dev/null'

/dev/null 100%[===================>] 100.00M 58.7MB/s in 1.7s 

2016-03-26 22:17:36 (58.7 MB/s) - '/dev/null' saved [104857600/104857600]

lxc profile device set default eth0 limits.ingress 100Mbit
lxc profile device set default eth0 limits.egress 100Mbit
lxc exec zerotier -- wget speedtest.newark.linode.com/100MB-newark.bin -O /dev/null

--2016-03-26 22:17:47-- speedtest.newark.linode.com/100MB-newark.bin
Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 104857600 (100M) [application/octet-stream]
Saving to: '/dev/null'

/dev/null 100%[===================>] 100.00M 11.4MB/s in 8.8s 

2016-03-26 22:17:56 (11.4 MB/s) - '/dev/null' saved [104857600/104857600]

Выставляем приоритет - lxc config set my-container limits.network.priority 5

Получение информации о потреблении ресурсов

LXD API позволяет получить текущее потребление ресурсов контейнером:

  • ОЗУ: текущее значение, пиковое, текущее потребление swap и пиковое
  • Диск: текущее использование диска
  • Сеть: байты и пакеты, переданные и полученные каждым интерфейсом

lxc info zerotier

Name: zerotier
Architecture: x86_64
Created: 2016/02/20 20:01 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 29258
Ips:
 eth0: inet 172.17.0.101
 eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
 eth0: inet6 fe80::216:3eff:feec:65a8
 lo: inet 127.0.0.1
 lo: inet6 ::1
 lxcbr0: inet 10.0.3.1
 lxcbr0: inet6 fe80::f0bd:55ff:feee:97a2
 zt0: inet 29.17.181.59
 zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
 zt0: inet6 fe80::79:e7ff:fe0d:5123
Resources:
 Processes: 33
 Disk usage:
  root: 808.07MB
 Memory usage:
  Memory (current): 106.79MB
  Memory (peak): 195.51MB
  Swap (current): 124.00kB
  Swap (peak): 124.00kB
 Network usage:
  lxcbr0:
   Bytes received: 0 bytes
   Bytes sent: 570 bytes
   Packets received: 0
   Packets sent: 0
  zt0:
   Bytes received: 1.10MB
   Bytes sent: 806 bytes
   Packets received: 10957
   Packets sent: 10957
  eth0:
   Bytes received: 99.35MB
   Bytes sent: 5.88MB
   Packets received: 64481
   Packets sent: 64481
  lo:
   Bytes received: 9.57kB
   Bytes sent: 9.57kB
   Packets received: 81
   Packets sent: 81
Snapshots:
 zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)

Итог

Команда LXD проделала многомесячную работу над созданием языка, позволяющего легко описать ваши ограничения. Задача была создать лёгкий и в тоже время мощный язык, позволяющий указать вашу специфику. Указание лимитов на лету и наследование через профиля дают вам мощный инструмент для управления нагрузкой без пагубного влияния на работающие службы.

Оглавление цикла статей про LXD 2.0.
Предыдущая статья LXD 2.0: Ваш первый LXD контейнер [3/12].
Следующая статья LXD 2.0: Управление образами [5/12].

    Twitter   


Разделы

Главная
Новости
Ворох бумаг
Видео Linux
Игры в Linux
Безопасность
Статьи о FreeBSD
Статьи об Ubuntu
Статьи о Snappy
Статьи об Ubuntu Phone
Статьи о Kubuntu
Статьи о Xubuntu
Статьи о Lubuntu
Статьи об Open Source
Карта сайта

Лучшее на сайте:

1С под Linux.   Ускорение Ubuntu.   21 пример iptables.   Цикл статей о Ceph.   Убунту в дикой среде.   Ubuntu Linux на SSD.   Ubuntu для блондинок.   Поддержка железа в Linux.   BTSync на службе у админа.   Андроид программы в Ubuntu.   Прокидывание портов для p2p.   Анти СПАМ в Postfix.  



Круги Гугл Ада.


Группа поддержки