cgroups (Português)

From ArchWiki

Status de tradução: Esse artigo é uma tradução de Cgroups. Data da última tradução: 2023-03-11. Você pode ajudar a sincronizar a tradução, se houver alterações na versão em inglê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 e cgclassify (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) e cgconfig.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 via cgconfig.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.

Nota: Como mostrado acima, um slice permite agrupar e gerenciar recursos e processos de uma unidade (unit) do systemd.

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
Nota: eBPF tecnicamente não é considerado um controlador, mas sim as opções implementadas pelo systemd que as usa e somente o usuário root possui permissão de mudá-las.

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
Atenção: Os ajustes serão permanentes a não ser que a opção --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/.
Nota: Nem todos os recursos tem efeito imediato após alteração. Por exemplo, gerar alterações com TaskMax terá efeito somente depois de criado novos processos.

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

Nota: Quando usada uma versão de Systemd ≥ 205 (maior ou igual a 205) ao gerenciar cgroups, você pode ignorar totalmente este arquivo.

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;
  }
}
Nota:
  • 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
Nota: Neste último comando, somente um PID pode ser escrito por vez. Portanto, repita esse passo para cada processo que precise ser movido.

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

Veja também