Одним из главных «кирпичиков» создания контейнеров: контрольными группами (control groups, cgroups).
Контрольные группы служат для ограничения доступных группе процессов ресурсов, например оперативной памяти, времени CPU и ресурсов сетевого ввода/вывода. С точки зрения безопасности хорошо настроенная контрольная группа позволяет гарантировать, что процесс не сможет влиять на поведение других процессов, захватывая все ресурсы — например, все ресурсы CPU или память, — не оставляя ничего остальным приложениям. Существует также контрольная группа pid, предназначенная для ограничения общего количества процессов в контрольной группе, тем самым сводя на нет результативность так называемой fork-бомбы.
Fork-бомба — это программа, создающая процессы, которые, в свою очередь, тоже создают процессы, что приводит к экспоненциальному росту объема используемых ресурсов и в итоге выводит машину из строя.
### Иерархии контрольных групп
Для каждого из типов ресурсов есть своя иерархия контрольных групп, управляемая контроллером контрольных групп. Всякий процесс Linux является членом одной контрольной группы для каждого типа ресурсов.
Любой создаваемый процесс наследует контрольные группы своего родительского процесса.
Ядро Linux обменивается информацией о контрольных группах через набор псевдофайловых систем, располагающихся обычно по пути /sys/fs/cgroup.
Чтобы просмотреть все типы контрольных групп в системе, можно вывести содержимое этого каталога:
```
[root@whoisdeveloper cgroup]# ls
blkio cpu cpuacct cpu,cpuacct cpuset devices freezer hugetlb memory net_cls net_cls,net_prio net_prio perf_event pids rdma systemd
```
Работа с контрольными группами требует чтения и записи в файлы и каталоги в этой иерархии. Посмотрим на контрольную группу memory в качестве примера:
```
[root@whoisdeveloper memory]# ls
cgroup.clone_children init.scope memory.kmem.limit_in_bytes memory.kmem.tcp.limit_in_bytes memory.limit_in_bytes memory.memsw.max_usage_in_bytes memory.oom_control memory.swappiness release_agent
cgroup.event_control memory.failcnt memory.kmem.max_usage_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.max_usage_in_bytes memory.memsw.usage_in_bytes memory.pressure_level memory.usage_in_bytes system.slice
cgroup.procs memory.force_empty memory.kmem.slabinfo memory.kmem.tcp.usage_in_bytes memory.memsw.failcnt memory.move_charge_at_immigrate memory.soft_limit_in_bytes memory.use_hierarchy tasks
cgroup.sane_behavior memory.kmem.failcnt memory.kmem.tcp.failcnt memory.kmem.usage_in_bytes memory.memsw.limit_in_bytes memory.numa_stat memory.stat notify_on_release user.slice
```
Управлять контрольной группой можно с помощью записи в одни из этих файлов, в то время как другие содержат информацию о состоянии группы, записанную ядром Linux.
```
[root@whoisdeveloper memory]# cat system.slice/memory.limit_in_bytes
9223372036854771712
```
По умолчанию объем памяти не ограничен, так что это гигантское число соответствует всей памяти, доступной виртуальной машине.
Если не ограничить объем памяти, доступный процессу, то он может оставить без памяти другие процессы на том же хост-компьютере. Это может происходить либо непреднамеренно, в результате утечки памяти в приложении, либо в результате атаки на истощение ресурсов, при которой злоумышленник специально применяет утечку памяти, чтобы использовать как можно больше памяти. Установка ограничений памяти и прочих ресурсов, доступных процессу, позволяет уменьшить последствия подобных атак и гарантировать, что прочие процессы смогут продолжать нормально работать.
Чтобы ограничить память, выделяемую контрольной группе при создании контейнера, можно изменить файл config.json в комплекте runc. Ограничения контрольной группы задаются в разделе linux:resources этого файла:
```
"linux": {
"resources": {
"memory": {
"limit": 1000000
},
...
}
}
```
### Приписываем процесс к контрольной группе
Как и при установке ограничений на ресурсы, чтобы приписать процесс к определенной контрольной группе, необходимо просто записать его идентификатор в файл cgroup.procs этой группы.
### Контрольные группы версии 2
В ядре Linux в 2016 году появилась версия 2 контрольных групп, и первым дистрибутивом Linux.
Основное различие между версиями состоит в том, что в версии 2 процесс не может сочетать различные группы для различных контроллеров. В версии 1 процесс может входить в /sys/fs/cgroup/memory/mygroup и /sys/fs/cgroup/pids/yourgroup. В версии 2 все проще: процесс входит в /sys/fs/cgroup/ourgroup и управляется всеми контроллерами ourgroup.
Версия 2 контрольных групп также лучше поддерживает контейнеры, не требующие полномочий суперпользователя, что позволяет ограничивать ресурсы и для них.