Containers: Entendendo processos

Seguindo nossa série de artigos sobre containers, já vimos um pouco da história dos containers, aprendemos o que é o Docker e suas alternativas e nos aprofundamos mais falando sobre namespaces e cgroups. Nesse artigo vamos entender um pouco como funcionam os processos nos containers.

O processo init

Em um mundo ideal na utilização de containers é ideal que você execute apenas um processo por container, mas às vezes isso acaba sendo impossível. Por exemplo, além do processo principal do container, também podem ser executados processos de monitoramento ou geração de logs.

Assim que são iniciados os processos, haverá um processo pid 1 em execução no container, que é o que constumamos chamar de processo número 1 ou initprocess e então outros processos filho serão criados à partir desse processo.

Quando você liga um servidor Linux, após a etapa da BIOS/boot-loader, o kernel do Linux é carregado. Os arquivos do kernel normalmente ficam localizados na pasta /boot, chamada vmlinuz. Após a inicialização do kernel, ele iniciará o processo init. Em seguida, muda-se para o nome do usuário. Após cria-se outros processos e ele gerencia esses mesmos processos.

Levando em consideração o conceito de containers linux, uma vez que o container tenha estabelecido seu próprio isolamento de processo (PID namespace) o número do processo nesse namespace também é iniciado à partir de 1. Portanto, o processo init do container também é chamado de processo número 1. Em seguida, outros processos são criados à partir do processo número 1 que foi criado no container. Vamos dar uma olhada no nosso container httpd:

Se você tentar executar o comando kill -9 1 em seu container, descobrirá que isso não funciona:

Mas por que? Quando você executa o comando kill -9 1 na verdade você está enviando um sinal para o processo PID 1. Você pode obter uma lista de sinais de comando kill executando o comando kill -l:

Portanto, -9 significa SIGKILL (se você não usar -9, por padrão SIGTERM (15) será usado). Este comando kill -9 1 envia um sinal SIGKILL para o processo 1.

Como o processo trata o sinal?

Quando um processo recebe um sinal, ele tem as três opções a seguir:

  • Ignora: Ou seja, nenhum processamento é feito neste sinal, mas existem duas excessões. Para os dois sinais SIGKILL e SIGSTOP, o processo não pode ignorá-lo. Isso ocorre porque sua função principal é fornecer ao kernel do Linux e aos superusuários o privilégio de excluir qualquer processo.
  • Captura: Isso significa que o processo do usuário pode registrar seu próprio manipulador para este sinal. Novamente SIGKILL e SIGSTOP são exceções aqui. Esses dois sinais não podem ter o próprio código de processamento do usuário e só podem executar o comportamento padrão do sistema.
  • Padrão: O Linux define um comportamento padrão para cada sinal. Você pode pode exemplo executar man 7 signal no Linux para ver o comportamento padrão de cada sinal. Para a maioria dos sinais, o aplicativo não precisa registrar seu próprio manipulador, apenas usa o comportamento padrão definido pelo sistema.

Como verificar se o sinal é ignorado?

Você pode verificar o status do processo no diretório /proc, usando nosso container httpd como exemplo:

Entenda SigCgt por sinais sendo capturados. Seu valor é exibido em formato hexadecimal de 8 bytes. Os 4 bytes mais à direita representam sinais padrão, a esquerda é a extensão de sinal em tempo real do Linux. Cada bit nos 8 bytes representa um sinal correspondente. Então, no nosso exemplo temos o seguinte:

46eb => 0100 0110 1110 1011

Você pode observar que o 9º bit é 0, o que significa que o sinal 9 não foi capturado, é por isso que o kill -9 1 não funcionou.

Conclusão

Nesse artigo vimos um pouco sobre processos no container e o funcionamento do kernel do linux. Lembre-se do seguinte:

  • O processo número 1 é o primeiro processo do espaço do usuário, ele cria outros processos no namespace do container.
  • Os sinais SIGKILL e SIGSTOP não podem ser ignorados ou capturados pelo processo número 1.
  • O kernel do Linux ignora os sinais que possuem apenas o manipulador padrão para cada processo init no namespace.