Containers: O que são namespaces e cgroups

Containers e ferramentas associadas, como Docker e Kubernetes, já existem há algum tempo. Eles ajudaram a mudar a forma como o software é desenvolvido e entregue em ambientes de aplicativos modernos. Os containers tornam possível implantar e executar rapidamente cada parte do software em seu próprio ambiente segregado, sem a necessidade de construir máquinas virtuais (VMs) individuais.

A maioria das pessoas provavelmente não está nem um pouco interessada em saber como os containers funcionam por debaixo dos panos, mas acho que é importante entender as tecnologias subjacentes. Eu, particularmente, sou uma pessoa curiosa e gosto de entender como as coisas funcionam.

O que é um namespace?

Os namespaces fazem parte do kernel do Linux desde 2002 (introduzido na versão 2.4.19 do kernel), é uma feature que permite criar e lidar com diversos contextos em um mesmo sistema, vendo propriedades globais diferentes e isoladas em cada contexto. Basicamente, os namespaces são responsáveis por gerar o isolamento de grupos de processos em seu nível lógico, como o gerenciamento de usuários, rede, etc., garantido que o container não enxergue os processos do host e vice-versa. Logo, ao criar um container, são criados namespaces como PID (Process ID) para isolar processos, NET (Network) para controlar e isolar as redes de cada container, IPC (Inter Process Communication) que permite a comunicação entre processos, etc.

O uso de containers oferece um ambiente isolado que parece uma VM completa. No entanto, não é uma VM - é um processo em execução em um servidor em algum lugar. Se por exemplo forem iniciados dois containers, haverá dois processos em execução em um único servidor em algum lugar - mas eles estão isolados um do outro.

Namespaces Linux mais usados

No kernel do Linux, existem diferentes tipos de namespaces. Cada namespace tem suas próprias propriedades exclusivas e existem alguns tipos comuns de namespaces amplamente usados hoje em dia. Abaixo uma breve visão geral de cada tipo de namespace:

  • Isolamento de processo (PID namespace): Um PID ou ID de processo ajuda um sistema a rastrear uma tarefa específica em um computador. Ao iniciar o Google Chrome em seu computador, ele terá um PID associado a ele. Para ter certeza de que o computador interage com uma tarefa adequadamente, a tarefa é referenciada pelo PID. Vamos supor que você tenha o Google Chrome e o Firefox abertos ao mesmo tempo. Você abre seu site de pesquisas preferido (Google) em cada navegador e pesquisa fotos de gatinhos em um e de cachorros no outro. Ambos os navegadores estão fazendo uma solicitação semelhante ao mesmo site. Como o computador garante que os resultados da pesquisa corretos sejam retornados ao navegador correto? Uma das formas principais é rastrear as solicitações que cada PID faz e, em seguida, retornar os resultados ao processo de solicitação. Mas o que isso tem haver com namespaces? Digamos que você deseja executar cópias idênticas de um software. Alguns softwares não foram escritos para ter mais de uma cópia aberta ao mesmo tempo. Para fazer isso, você pode ter que isolar o PID para que ele não fique ciente do que está acontecendo fora de seus próprios processos. Isso é o que o isolamento do processo pode ajudar a resolver.

  • Interfaces de rede (NET namespace): Cada computador conectado a uma rede (como a Internet) requer um endereço IP. Este é um número exclusivo que permite que os computadores se comuniquem com eficácia. Quando um tipo específico de recurso é acessado, digamos uma página da web, há uma porta específica usada para essa comunicação. Isso ocorre porque um computador pode hospedar um servidor web, um servidor de jogos ou até um servidor de e-mail no mesmo host. As portas podem ser 80 ou 443 para tráfego web, 8000 para o tráfego de jogos e 25 para e-mail. Quando eu digito https:// em meu navegador, o computador o traduz para enviar o tráfego para o IP do navegador xxx.xxx.xxx.xxx na porta 443 (conexão segura). O servidor na outra ponta responde com o conteúdo apropriado por meio do endereço IP de origem. Algumas stacks de tecnologia não oferecem suporte a várias instâncias do software em execução simultaneamente. Ao contrário do isolamento PID, no entanto, quando um software como um servidor de e-mail está recebendo uma conexão, ele espera estar em uma porta específica. Portanto, mesmo se você isolar o PID, o servidor de e-mail terá apenas uma única instância em execução porque a porta 25 já está em uso. Os namespaces de rede permitem que os processos dentro de cada instância de namespace tenham acesso a um novo endereço IP junto com toda a gama de portas. Assim, você pode executar várias versões de um servidor de e-mail escutando na porta 25 sem nenhum conflito de software.

  • Sistema Unix de compartilhamento de tempo (UTS namespace): Basicamente permite que um único sistema pareça ter diferentes nomes de host e domínio para diferentes processos.

  • Namespace de usuário (User namespace): Cada sistema operaciona possui sua forma de rastrear os arquivos que o usuário possui. Isso permite que o sistema restrinja o acesso a arquivos confidenciais do sistema e também evita que pessoas que usam o mesmo computador acessem os arquivos umas das outras. Para o usuário final, os arquivos são mostrados como propriedade de um nome de login, já para o computador, essa é apenas uma combinação arbitrária de caracteres alfanuméricos. Para rastrear essas permissões corretamente, há um processo de mapeamento do usuário para um número de identificação de usuário (UID) específico, como 1001. Esse UID é então aplicado aos metadados do arquivo. Isso permite que você altere seu nome de usuário sem que o sistema precise fazer uma quantidade significativa de atualização nos metadados.

  • Montagem (MNT namespace): O namespace de montagem é usado para isolar os pontos de montagem de forma que os processos em diferentes namespaces não possam visualizar os arquivos uns dos outros. Se você estiver familiarizado com o comando chroot, ele funciona de forma semelhante. Basicamente, quando montamos ou desmontamos um sistema de arquivos, todos os outros processos podem observar esse processo, com mount podemos basicamente limitar isso. Esse isolamento nos fornece a capacidade de montar diferentes sistemas de arquivos sem afetar o host.

  • Comunicação entre processos(IPC namespace): Os IPCs tratam da comunicação entre os processos usando áreas de memória compartilhada, filas de mensagens e semáforos. Em programação, sempre que o aplicativo precisa manter um registro temporário de alguma informação, ele solicita que o sistema operacional dedique uma determinada quantidade de memória (RAM) ao seu processo. Você pode pensar nisso como iniciar uma conversa com alguém em um programa de bate-papo. Por padrão, existem apenas dois destinatários, você e a pessoa do outro lado, ou pensado de outra forma, o sistema operacional e o aplicativo. Ambos os participantes podem ver todo o histórico da conversa, mas ninguém mais pode. No entanto, se alguém for convidado para a conversa, ele também poderá ler e participar da discussão. A memória compartilhada pode agir de forma semelhante, permitindo que dois ou mais programas tenham acesso às mesmas informações. A aplicação mais comum para este tipo de gerenciamento é possivelmente o uso de bancos de dados.

O que é um cgroup?

Cgroups são basicamente a tecnologia que nos permite definir limites de uso de recursos em processos Linux. Muitos recursos podem ser limitados pelo uso de Cgroups. Os cgroups fornecem os seguintes recursos:

  • Limites de recursos: Você pode configurar um cgroup para limitar quanto de um determinado recurso (memória ou CPU, por exemplo) um processo pode usar.
  • Priorização: Você pode controlar quanto de um recurso (CPU, disco ou rede) um processo pode usar em comparação com processos em outro cgroup quando há contenção de recursos.
  • Contabilidade: os limites de recursos são monitorados e relatados no nível do cgroup.
  • Controle: Você pode alterar o status (congelado, interrompido ou reiniciado) de todos os processos em um cgroup com um único comando.

Basicamente, você usa cgroups para controlar quanto de um determinado recurso-chave (CPU, memória, rede e I/O de disco) pode ser acessado ou usado por um processo ou conjunto de processos. Os cgroups são um componente-chave dos containers porque geralmente há vários processos em execução em um container que precisam ser controlados juntos. Em um ambiente Kubernetes, por exemplo, os cgroups podem ser utilizados para implementar solicitações e limites de recursos e classes de QoS correspondentes no nível do pod.

Conclusão

Namespaces e cgroups são os blocos de construção para containers e aplicativos modernos. Ter uma compreensão de como eles funcionam é importante à medida que refatoramos os aplicativos para arquiteturas mais modernas. Os namespaces fornecem isolamento de recursos do sistema e os cgroups permitem um controle refinado e a aplicação de limites para esses recursos. Os containers não são a única maneira de usar namespaces e cgroups. Os namespaces e as interfaces cgroup são incorporados ao kernel do Linux, o que significa que outros aplicativos podem usá-los para fornecer separação e restrições de recursos.