cgroups (Português)
Grupos de Controle, ou em inglês Control groups (ou também conhecido por cgroups) é um recurso para gerenciamento, restrição e auditoria de grupos de processos fornecido pelo kernel Linux. Comparado com outros gerenciamentos como pelo comando nice(1) ou por configurações em /etc/security/limits.conf
, cgroups é mais flexível por conseguir operar em (sub)conjuntos de processos (usável com diferentes usuários de sistema).
O controle de grupos pode ser acessado por diversas ferramentas:
- Usando diretivas nos arquivos unit do systemd para especificar limites de serviços e slices;
- Acessando o sistema de arquivos
cgroup
diretamente; - Por via de
cgcreate
,cgexec
ecgclassify
(parte dos pacotes libcgroup-gitAUR e libcgroup-gitAUR) ou similares; - Usando o serviço de controle de grupos (rules engine daemon) para automaticamente mover certos usuários/grupos/comandos para grupos (
/etc/cgrules.conf
) ecgconfig.service
(parte dos pacotes libcgroup-gitAUR e libcgroup-gitAUR); - E através de outros softwares de virtualização como Linux Containers (LXC).
Em Arch Linux systemd é o método preferível e mais fácil para chamar e configurar cgroups, pois faz parte da instalação padrão.
Instalação
Tenha certeza que você possui um desses pacotes instalados para automatizar o controle de grupos:
- systemd - para controle de recursos de um serviço systemd.
- libcgroupAUR, libcgroup-gitAUR - conjunto de ferramentas standalone (
cgcreate
,cgclassify
, persistência viacgconfig.conf
).
Com systemd
Hierarquia
A atual hierarquia de cgroup pode ser vista com systemctl status
ou pelo comando systemd-cgls
.
$ systemctl status
● myarchlinux State: running Jobs: 0 queued Failed: 0 units Since: Wed 2019-12-04 22:16:28 UTC; 1 day 4h ago CGroup: / ├─user.slice │ └─user-1000.slice │ ├─user@1000.service │ │ ├─gnome-shell-wayland.service │ │ │ ├─ 1129 /usr/bin/gnome-shell │ │ ├─gnome-terminal-server.service │ │ │ ├─33519 /usr/lib/gnome-terminal-server │ │ │ ├─37298 fish │ │ │ └─39239 systemctl status │ │ ├─init.scope │ │ │ ├─1066 /usr/lib/systemd/systemd --user │ │ │ └─1067 (sd-pam) │ └─session-2.scope │ ├─1053 gdm-session-worker [pam/gdm-password] │ ├─1078 /usr/bin/gnome-keyring-daemon --daemonize --login │ ├─1082 /usr/lib/gdm-wayland-session /usr/bin/gnome-session │ ├─1086 /usr/lib/gnome-session-binary │ └─3514 /usr/bin/ssh-agent -D -a /run/user/1000/keyring/.ssh ├─init.scope │ └─1 /sbin/init └─system.slice ├─systemd-udevd.service │ └─285 /usr/lib/systemd/systemd-udevd ├─systemd-journald.service │ └─272 /usr/lib/systemd/systemd-journald ├─NetworkManager.service │ └─656 /usr/bin/NetworkManager --no-daemon ├─gdm.service │ └─668 /usr/bin/gdm └─systemd-logind.service └─654 /usr/lib/systemd/systemd-logind
Encontre o cgroup de um processo
O nome de um processo do cgroup pode ser encontrado em /proc/PID/cgroup
.
Por exemplo, o cgroup do shell:
$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-3.scope
Uso de recursos do cgroup
O comando systemd-cgtop
pode ser usado para visualizar o uso de recursos:
$ systemd-cgtop
Control Group Tasks %CPU Memory Input/s Output/s user.slice 540 152,8 3.3G - - user.slice/user-1000.slice 540 152,8 3.3G - - user.slice/u…000.slice/session-1.scope 425 149,5 3.1G - - system.slice 37 - 215.6M - -
Customização de cgroups
Um arquivo unit systemd.slice(5) pode ser usado para definir uma configuração de cgroup personalizada, do qual deve ser colocado em um diretório do systemd como /etc/systemd/system/
. As opções de controle de recursos que podem ser atribuídas estão documentadas em systemd.resource-control(5).
Este é um exemplo de um slice unit que pode somente permitir 30% da CPU em uso:
/etc/systemd/system/meu.slice
[Slice] CPUQuota=30%
Lembre-se de fazer systemd daemon-reload para selecionar qualquer arquivo novo ou modificado em .slice
.
Como serviços
Arquivo unit de serviço
Recursos podem ser especificamente direcionados em uma definição de serviço ou como arquivo drop-in:
[Service] MemoryMax=1G
Neste exemplo os limites do serviço foram definidos para 1 gigabyte.
Agrupando unit em um slice
Um serviço pode ser direcionado para uma execução de slice específica em:
[Service] Slice=meu.slice
Como root
systemd-run
pode ser usado para executar um comando em um slice específico.
# systemd-run --slice=meu.slice comando
--uid=nomedousuario
é uma opção usada para gerar o comando como um usuário específico.
# systemd-run --uid=nomedousuario --slice=meu.slice comando
A opção --shell
é usada para gerar um comando em shell de dentro do slice.
Como usuário sem privilégios
Usuários sem privilégios podem dividir os recursos disponibilizados em novos cgroups, se claro algumas condições forem atendidas.
É importante que cgroups v2 seja utilizado para que um usuário sem privilégios consiga as permissões de gerenciamento de recursos cgroup.
Tipos de controladores
Nem todos os recursos podem ser controlados pelo usuário.
Controlador | Controlado pelo usuário | Opções |
---|---|---|
cpu | Precisa de delegação | CPUAccounting, CPUWeight, CPUQuota, AllowedCPUs, AllowedMemoryNodes |
io | Precisa de delegação | IOWeight, IOReadBandwidthMax, IOWriteBandwidthMax, IODeviceLatencyTargetSec |
memory | Sim | MemoryLow, MemoryHigh, MemoryMax, MemorySwapMax |
pids | Sim | TasksMax |
rdma | Não | Não definido ou desconhecido. |
eBPF | Não | IPAddressDeny, DeviceAllow, DevicePolicy |
Delegações de usuário
Para o usuário controlar CPU e recursos de I/O os mesmos precisam ser delegados ao usuário em questão. Isso pode ser feito com um arquivo drop-in.
Por exemplo, se o seu id de usuário for 1000:
/etc/systemd/system/user@1000.service.d/delegate.conf
[Service] Delegate=cpu cpuset io
Reinicie o sistema e verifique a utilização dos controladores cpu
e io
na sessão de usuário:
$ cat /sys/fs/cgroup/user.slice/user-1000.slice/cgroup.controllers
cpuset cpu io memory pids
Slices definidos pelo usuário
Os arquivos slice de usuário podem ser colocados em ~/.config/systemd/user/
.
Para executar comandos em um certo slice:
$ systemd-run --user --slice=meu.slice comando
Você pode também executar seu login shell de dentro do slice:
$ systemd-run --user --slice=meu.slice --shell
Ajuste durante runtime - Tempo de execução
Recursos definidos pelo cgroups podem ser ajustados durante o tempo de execução usando o comando systemctl set-property
. A sintaxe é idêntica às opções de systemd.resource-control(5).
Por exemplo, para cortar o acesso à internet de todas as sessões de usuário:
$ systemctl set-property user.slice IPAddressDeny=any
--runtime
seja passada. As alterações são salvas em /etc/systend/system.control/
e, portanto, gerenciam as opções gerais do sistema, enquanto as opções de usuário são salvas em .config/systemd/user.control/
.Com libcgroup
É possível habilitar o serviço chconfig
com systemd. Isto permite facilmente o rastreio de quaisquer erros gerados pelo cgconfig.conf
.
Grupos ad-hoc
Um dos poderes de cgroups é a criação de grupos "ad-hoc" em tempo real. Uma das vantagens é adicionar privilégios ao usuário regular ativo para criação de grupos customizados. No exemplo abaixo nomedogrupo
é o nome do cgroup:
# cgcreate -a usuario -t usuario -g memory,cpu:nomedogrupo
Agora todos os comportamentos ajustados no grupo nomedogrupo
podem ser escritos pelo usuário ativo.
$ ls -l /sys/fs/cgroup/memory/nomedogrupo
total 0 -rwxrwxr-x 1 user root 0 Sep 25 00:39 cgroup.event_control -rwxrwxr-x 1 user root 0 Sep 25 00:39 cgroup.procs -rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.rt_period_us -rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.rt_runtime_us -rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.shares -rwxrwxr-x 1 user root 0 Sep 25 00:39 notify_on_release -rwxrwxr-x 1 user root 0 Sep 25 00:39 tasks
Cgroups são hierárquicos, ou seja, um usuário pode criar quantos subgrupos forem desejados. Se por exemplo um usuário regular quiser executar o shell bash
de dentro de um novo subgrupo chamado foo
:
$ cgcreate -g memory,cpu:nomedogrupo/foo $ cgexec -g memory,cpu:nomedogrupo/foo bash
Por garantia verifique (somente necessário ao usar cgroups legacy (v1)):
$ cat /proc/self/cgroup
11:memory:/nomedogrupo/foo 6:cpu:/nomedogrupo/foo
Desta forma um novo subdiretório foi criado para este grupo. Para limitar o uso da memória de todos os processos envolvidos no grupo em 10MB, execute o comando abaixo:
$ echo 10000000 > /sys/fs/cgroup/memory/groupname/foo/memory.limit_in_bytes
Note que o limite de memória se aplica apenas ao uso de RAM. A partir do momento que as tarefas alcançarem o máximo estabelecido, a memória é convertida em swap. Entretanto a troca não afetará a performance de forma significativa.
De maneira similar, você pode mudar a prioridade ("shares", ou compartilhamentos) de CPU em grupo. Por padrão todos os grupos tem 1024 shares. Um grupo com 100 shares deterá aproximadamente 10% do tempo de CPU.
$ echo 100 > /sys/fs/cgroup/cpu/groupname/foo/cpu.shares
Mais ajustes de comportamento ou estatísticas podem ser encontrados ao listar o diretório do cgroup.
Se for preferível um processo já em execução pode ter seu cgroup alterado. Como mostrado no exemplo abaixo, para mover todos os comandos dos processos em shell 'bash' com destino ao nomedogrupo/foo
:
$ pidof bash 13244 13266 $ cgclassify -g memory,cpu:nomedogrupo/foo `pidof bash` $ cat /proc/13244/cgroup 11:memory:/nomedogrupo/foo 6:cpu:/nomedogrupo/foo
Configuração persistente de grupo
Se você quiser que cgroups sejam criados durante o boot, defina no arquivo de configuração em /etc/cgconfig.conf
. Por exemplo, o "nomedogrupo" garante permissão para o $USER
e usuários do grupo $GROUP
para gerenciar limites e adicionar tarefas. Portanto as definições de grupo do subgrupo "nomedogrupo/foo" seriam semelhantes a configuração abaixo:
/etc/cgconfig.conf
group nomedogrupo { perm { # who can manage limits admin { uid = $USER; gid = $GROUP; } # who can add tasks to this group task { uid = $USER; gid = $GROUP; } } # create this group in cpu and memory controllers cpu { } memory { } } group nomedogrupo/foo { cpu { cpu.shares = 100; } memory { memory.limit_in_bytes = 10000000; } }
- Comentários devem começar na primeira linha! Ou seja, o carácter # usado em comentários deve aparecer como o primeiro carácter da linha. Caso não seja, o cgconfigparser irá ter problemas de parsing e será reportado o erro:
cgroup change of grupo failed
, ou de forma traduzida:mudança de grupo falhou para cgroup
. O erro também é evitável se cgconfig for executado por uma unit do systemd. - A seção de permissões é opcional.
- A hierarquia do diretório
/sys/fs/cgroup/
que contém todos os controladores já possui os subdiretórios criados e montados durante o boot como um sistema de arquivos virtual. Isto fornece a habilidade de criar uma nova entrada de grupo com o comando$CONTROLLER-NAME { }
. Se por alguma razão você quiser criar e montar hierarquias em outro lugar, será necessário escrever uma segunda entrada em/etc/cgconfig.conf
da seguinte forma:
mount { cpuset = /local/do/nomedogrupo; }
Isto é equivalente a esses comandos em shell:
# mkdir /local/do/nomedogrupo # mount -t /local/do -o cpuset nomedogrupo /local/do/nomedogrupo
Com o sistema de arquivos virtual cgroup
A partir do systemd versão 232, ao invés do método cgm descrito na próxima seção abaixo, esta seção aborda uma alternativa manual para configurar a limitação de memória em uso.
Crie um novo nome de cgroup chamado nomedogrupo:
# mkdir /sys/fs/cgroup/memory/nomedogrupo
Exemplo: defina o valor limite de memória para 100MB:
# echo 100000000 > /sys/fs/cgroup/memory/nomedogrupo/memory.limit_in_bytes
Mova um único processo para o cgroup por vez:
# echo pid > /sys/fs/cgroup/memory/groupname/cgroup.procs
Exemplos
Restrição de uso da memória ou CPU de um comando
O exemplo a seguir demonstra um cgroup que restringe um determinado comando para 2GB de memória.
$ systemd-run --scope -p MemoryMax=2G --user comando
E o exemplo a seguir demonstra um comando restringido para 20% do uso de um core de CPU.
$ systemd-run --scope -p CPUQuota="20%" --user comando
Matlab
Fazer cálculos extensos com MATLAB pode resultar em crash do seu sistema. Devido ao fato que Matlab não possui nenhuma proteção contra o consumo de memória ou CPU da máquina. O exemplo a seguir mostra um cgroup que restringe o uso do Matlab para o limite dos primeiros 6 cores de CPU e 5GB de memória.
Com systemd
~/.config/systemd/user/matlab.slice
[Slice] AllowedCPUs=0-5 MemoryHigh=6G
Inicie Matlab desta maneira (tenha certeza que o caminho está correto):
$ systemd-run --user --slice=matlab.slice /opt/MATLAB/2012b/bin/matlab -desktop
Com libcgroup
/etc/cgconfig.conf
group matlab { perm { admin { uid = nomedousuario; } task { uid = nomedousuario; } } cpuset { cpuset.mems="0"; cpuset.cpus="0-5"; } memory { memory.limit_in_bytes = 5000000000; } }
Mude nomedousuario
para o nome de usuário que estiver executando Matlab.
Você também pode restringir o compartilhamento de CPU (share) ao delimitar com cpu
.
Inicie Matlab desta maneira (tenha certeza que o caminho está correto):
$ cgexec -g memory,cpuset:matlab /opt/MATLAB/2012b/bin/matlab -desktop
Documentação
- Para informações de controladores e o que certos switches ou ajustes de comportamento (tunables) significam, consulte a documentação do kernel: v1 ou v2 (ou instale linux-docs e veja
/usr/src/linux/Documentation/cgroup
) - Página do manual do Linux: cgroups(7).
- Um detalhado e completo guia sobre gerenciamento de recursos pode ser encontrado na documentação do Red Hat Enterprise Linux.
Para comandos e configurações de arquivos, veja as páginas man relevantes, por exemplo: cgcreate(1) ou cgrules.conf(5)
Dicas e truques
Habilitar cgroup v1
Cgroup v2 é agora habilitado por padrão. Se você quiser trocar para cgroup v1, defina o seguinte parâmetro de kernel:
systemd.unified_cgroup_hierarchy=0