Limitar o uso de recursos do processo no AL2023 usando systemd - Amazon Linux 2023

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Limitar o uso de recursos do processo no AL2023 usando systemd

No Amazon Linux 2023 (AL2023), recomendamos o uso de systemd para controlar quais recursos podem ser usados por processos ou grupos de processos. O uso de systemd é um substituto poderoso e fácil de usar para manipular cgroups manualmente ou usar utilitários como cpulimit, que antes só estavam disponíveis para o Amazon Linux no repositório EPEL de terceiros.

Para obter informações abrangentes, consulte a documentação do systemd upstream para systemd.resource-control ou a página man para systemd.resource-control em uma instância do AL2023.

Os exemplos abaixo usarão o teste de estresse da CPU stress-ng (do pacote stress-ng) para simular uma aplicação com uso intenso da CPU e memcached para simular uma aplicação com uso intenso da memória.

Os exemplos abaixo abrangem a aplicação de um limite de CPU em um comando único e um limite de memória em um serviço. A maioria das restrições de recursos que systemd oferece podem ser usadas em qualquer lugar que systemd execute um processo, e é possível usar várias ao mesmo tempo. Os exemplos abaixo são limitados a uma única restrição para fins ilustrativos.

Controle de recursos com systemd-run para execução de comandos únicos

Embora comumente associado aos serviços do sistema, systemd também pode ser usado por usuários que não são raiz para executar serviços, agendar temporizadores ou executar processos pontuais. No exemplo a seguir, vamos usar stress-ng como a aplicação. No primeiro exemplo, vamos executá-la usando systemd-run na conta padrão ec2-user e, no segundo exemplo, vamos aplicar limites quanto ao uso da CPU.

exemplo : usar systemd-run na linha de comandos para executar um processo, sem limitar o uso de recursos
  1. Garanta que o pacote stress-ng esteja instalado, pois vamos usá-lo no exemplo.

    [ec2-user ~]$ sudo dnf install -y stress-ng
  2. Use systemd-run para executar um teste de estresse da CPU de 10 segundos sem limitar a quantidade de CPU que ele pode usar.

    [ec2-user ~]$ systemd-run --user --tty --wait --property=CPUAccounting=1 stress-ng --cpu 1 --timeout 10 Running as unit: run-u6.service Press ^] three times within 1s to disconnect TTY. stress-ng: info: [339368] setting to a 10 second run per stressor stress-ng: info: [339368] dispatching hogs: 1 cpu stress-ng: info: [339368] successful run completed in 10.00s Finished with result: success Main processes terminated with: code=exited/status=0 Service runtime: 10.068s CPU time consumed: 9.060s

    A opção --user instrui systemd-run a executar o comando como o usuário conectado, a opção --tty significa que há um TTY anexado, --wait indica para esperar até que o serviço seja concluído e a opção --property=CPUAccounting=1 instrui systemd-run a registrar quanto tempo de CPU é usado na execução do processo. A opção da linha de comandos --property pode ser usada para enviar as configurações de systemd-run que podem estar definidas em um arquivo de configuração systemd.unit.

Quando instruído a colocar carga na CPU, o programa stress-ng usará todo o tempo de CPU disponível para realizar o teste durante o período solicitado. Para uma aplicação do mundo real, pode ser desejável limitar o tempo total de execução de um processo. No exemplo abaixo, solicitaremos que stress-ng seja executado por um tempo maior do que a restrição de duração máxima que foi imposta usando systemd-run.

exemplo : usar systemd-run na linha de comandos para executar um processo, limitando o uso da CPU a 1 segundo
  1. Verifique se stress-ng está instalado para executar este exemplo.

  2. A propriedade LimitCPU é equivalente a ulimit -t, que limitará o tempo máximo que esse processo poderá usar a CPU. Nesse caso, como estamos solicitando uma execução de estresse de 10 segundos e limitando o uso da CPU a 1 segundo, o comando receberá um sinal SIGXCPU e falhará.

    [ec2-user ~]$ systemd-run --user --tty --wait --property=CPUAccounting=1 --property=LimitCPU=1 stress-ng --cpu 1 --timeout 10 Running as unit: run-u12.service Press ^] three times within 1s to disconnect TTY. stress-ng: info: [340349] setting to a 10 second run per stressor stress-ng: info: [340349] dispatching hogs: 1 cpu stress-ng: fail: [340349] cpu instance 0 corrupted bogo-ops counter, 1370 vs 0 stress-ng: fail: [340349] cpu instance 0 hash error in bogo-ops counter and run flag, 3250129726 vs 0 stress-ng: fail: [340349] metrics-check: stressor metrics corrupted, data is compromised stress-ng: info: [340349] unsuccessful run completed in 1.14s Finished with result: exit-code Main processes terminated with: code=exited/status=2 Service runtime: 1.201s CPU time consumed: 1.008s

Mais comumente, talvez você queira restringir a porcentagem do tempo da CPU que pode ser consumido por um processo específico. No exemplo abaixo, restringiremos a porcentagem do tempo da CPU que pode ser consumido por stress-ng. Para um serviço do mundo real, pode ser desejável limitar a porcentagem máxima do tempo da CPU que um processo em segundo plano pode consumir para deixar os recursos livres para o processo que atende às solicitações do usuário.

exemplo : usar systemd-run para limitar um processo a 10% do tempo de uma CPU
  1. Verifique se stress-ng está instalado para executar este exemplo.

  2. Vamos usar a propriedade CPUQuota para instruir systemd-run a restringir o uso da CPU pelo comando que vamos executar. Não vamos limitar a quantidade de tempo que o processo pode permanecer em execução, apenas a quantidade de CPU que ele pode usar.

    [ec2-user ~]$ systemd-run --user --tty --wait --property=CPUAccounting=1 --property=CPUQuota=10% stress-ng --cpu 1 --timeout 10 Running as unit: run-u13.service Press ^] three times within 1s to disconnect TTY. stress-ng: info: [340664] setting to a 10 second run per stressor stress-ng: info: [340664] dispatching hogs: 1 cpu stress-ng: info: [340664] successful run completed in 10.08s Finished with result: success Main processes terminated with: code=exited/status=0 Service runtime: 10.140s CPU time consumed: 1.014s

    Observe como a contabilidade da CPU nos informa que, embora o serviço tenha sido executado por 10 segundos, ele consumiu apenas 1 segundo de tempo real da CPU.

Há várias maneiras de configurar systemd para limitar o uso de recursos de CPU, memória, rede e E/S. Consulte a documentação do systemd upstream para systemd.resource-control ou a página man para systemd.resource-control em uma instância do AL2023 para obter informações abrangentes.

Nos bastidores, systemd usa recursos de kernel do Linux, como cgroups, para implementar esses limites, evitando a necessidade de configurá-los manualmente. A documentação de kernel do Linux para cgroup-v2 contém muitos detalhes sobre como cgroups funcionam.

Controle de recursos em um serviço de systemd

Há vários parâmetros que podem ser adicionados à seção [Service] dos serviços de systemd para controlar o uso dos recursos do sistema. Isso inclui limites rígidos e flexíveis. Para conhecer o comportamento exato de cada opção, consulte a documentação de systemd upstream para systemd.resource-control ou a página man para systemd.resource-control em uma instância do AL2023.

Os limites comumente usados são MemoryHigh para especificar um limite de controle de utilização para o uso de memória, MemoryMax para definir um limite superior rígido (uma vez atingido, o OOM Killer é invocado) e CPUQuota (conforme ilustrado na seção anterior). Também é possível configurar pesos e prioridades em vez de números fixos.

exemplo : usar systemd para definir limites de uso de memória para os serviços

Neste exemplo, definiremos um limite rígido de uso de memória para memcached, um cache simples de valores-chave, e mostraremos como o OOM Killer é invocado para esse serviço e não para todo o sistema.

  1. Primeiro, precisamos instalar os pacotes necessários para este exemplo.

    [ec2-user ~]$ sudo dnf install -y memcached libmemcached-awesome-tools
  2. Habilite memcached.service, depois inicie o serviço para que memcached esteja em execução.

    [ec2-user ~]$ sudo systemctl enable memcached.service Created symlink /etc/systemd/system/multi-user.target.wants/memcached.service → /usr/lib/systemd/system/memcached.service. [ec2-user ~]$ sudo systemctl start memcached.service
  3. Verifique se memcached.service está em execução.

    [ec2-user ~]$ sudo systemctl status memcached.service ● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Active: active (running) since Fri 2025-01-31 22:36:42 UTC; 1s ago Main PID: 356294 (memcached) Tasks: 10 (limit: 18907) Memory: 1.8M CPU: 20ms CGroup: /system.slice/memcached.service └─356294 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 22:35:36 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon.
  4. Agora que memcached está instalado e em execução, podemos observar que ele funciona inserindo alguns dados aleatórios no cache.

    Em /etc/sysconfig/memcached, a variável CACHESIZE é definida como 64 por padrão, o que significa 64 megabytes. Ao inserir mais dados do que o tamanho máximo do cache, podemos ver que preenchemos o cache e alguns itens são removidos usando memcached-tool, e que memcached.service está usando cerca de 64 MB de memória.

    [ec2-user ~]$ for i in $(seq 1 150); do dd if=/dev/random of=$i bs=512k count=1; memcp -s localhost $i; done [ec2-user ~]$ memcached-tool localhost display # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM 2 120B 0s 1 0 no 0 0 0 39 512.0K 4s 63 126 yes 24 2 0 [ec2-user ~]$ sudo systemctl status memcached.service ● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Active: active (running) since Fri 2025-01-31 22:36:42 UTC; 7min ago Main PID: 356294 (memcached) Tasks: 10 (limit: 18907) Memory: 66.7M CPU: 203ms CGroup: /system.slice/memcached.service └─356294 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 22:36:42 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon.
  5. Use a propriedade MemoryMax para definir um limite rígido para memcached.service. Se esse limite atingido, o OOM Killer será invocado. Opções adicionais podem ser definidas para o serviço adicionando-as a um arquivo de substituição. Isso pode ser feito editando diretamente o arquivo /etc/systemd/system/memcached.service.d/override.conf ou de forma interativa, usando o comando edit de systemctl.

    [ec2-user ~]$ sudo systemctl edit memcached.service

    Adicione o seguinte à substituição para definir um limite rígido de 32 MB de memória para o serviço.

    [Service] MemoryMax=32M
  6. Instrua systemd a recarregar sua configuração.

    [ec2-user ~]$ sudo systemctl daemon-reload
  7. Observe que memcached.service agora está sendo executado com um limite de memória de 32 MB.

    [ec2-user ~]$ sudo systemctl status memcached.service ● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Drop-In: /etc/systemd/system/memcached.service.d └─override.conf Active: active (running) since Fri 2025-01-31 23:09:13 UTC; 49s ago Main PID: 358423 (memcached) Tasks: 10 (limit: 18907) Memory: 1.8M (max: 32.0M available: 30.1M) CPU: 25ms CGroup: /system.slice/memcached.service └─358423 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 23:09:13 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon.
  8. O serviço funcionará normalmente usando menos de 32 MB de memória, o que podemos confirmar ao carregar menos de 32 MB de dados aleatórios no cache e verificar o status do serviço.

    [ec2-user ~]$ for i in $(seq 1 30); do dd if=/dev/random of=$i bs=512k count=1; memcp -s localhost $i; done
    [ec2-user ~]$ sudo systemctl status memcached.service ● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Drop-In: /etc/systemd/system/memcached.service.d └─override.conf Active: active (running) since Fri 2025-01-31 23:14:48 UTC; 3s ago Main PID: 359492 (memcached) Tasks: 10 (limit: 18907) Memory: 18.2M (max: 32.0M available: 13.7M) CPU: 42ms CGroup: /system.slice/memcached.service └─359492 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 23:14:48 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon.
  9. Agora podemos fazer com que memcached use mais de 32 MB de memória, tentando usar os 64 MB completos de cache que são a configuração padrão de memcached.

    [ec2-user ~]$ for i in $(seq 1 150); do dd if=/dev/random of=$i bs=512k count=1; memcp -s localhost $i; done

    Você observará que em algum momento durante o comando acima, há erros de conexão com o servidor de memcached. Isso ocorre porque o OOM Killer encerrou o processo devido à restrição que impusemos a ele. O restante do sistema funcionará normalmente e nenhum outro processo será considerado pelo OOM Killer, já que restringimos apenas memcached.service.

    [ec2-user ~]$ sudo systemctl status memcached.service ● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Drop-In: /etc/systemd/system/memcached.service.d └─override.conf Active: failed (Result: oom-kill) since Fri 2025-01-31 23:20:28 UTC; 2s ago Duration: 2.901s Process: 360130 ExecStart=/usr/bin/memcached -p ${PORT} -u ${USER} -m ${CACHESIZE} -c ${MAXCONN} $OPTIONS (code=killed, signal=KILL) Main PID: 360130 (code=killed, signal=KILL) CPU: 94ms Jan 31 23:20:25 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon. Jan 31 23:20:28 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: memcached.service: A process of this unit has been killed by the OOM killer. Jan 31 23:20:28 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: memcached.service: Main process exited, code=killed, status=9/KILL Jan 31 23:20:28 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: memcached.service: Failed with result 'oom-kill'.