Namespaces¶
Na seção anterior, vimos uma visão geral das tecnologias que sustentam os contêineres. Agora vamos aprofundar nos namespaces, o mecanismo do kernel Linux responsável pelo isolamento — ou seja, controlar o que cada processo enxerga do sistema.
O que são namespaces?¶
Os namespaces são uma característica do núcleo do Linux que permite isolar processos e recursos em diferentes contêineres. Cada contêiner tem seu próprio namespace, o que significa que os processos em um contêiner não podem ver ou interagir com os processos em outro contêiner. Isso é fundamental para garantir o isolamento entre contêineres e evitar conflitos de dependência.
Os namespaces são usados para isolar diferentes recursos do sistema, como:
- PID namespace: Isola os IDs de processo (PIDs) entre contêineres, permitindo que cada contêiner tenha seu próprio conjunto de processos.
- Network namespace: Isola as interfaces de rede entre contêineres, permitindo que cada contêiner tenha sua própria pilha de rede e endereços IP.
- Mount namespace: Isola os pontos de montagem entre contêineres, permitindo que cada contêiner tenha seu próprio sistema de arquivos.
- User namespace: Isola os IDs de usuário e grupo entre contêineres, permitindo que cada contêiner tenha seu próprio conjunto de usuários e permissões.
- IPC namespace: Isola os recursos de comunicação entre processos (IPC) entre contêineres, permitindo que cada contêiner tenha seu próprio conjunto de recursos IPC.
- UTS namespace: Isola os nomes de host e domínio entre contêineres, permitindo que cada contêiner tenha seu próprio nome de host e domínio. O nome UTS vem de UNIX Time-Sharing System, uma referência histórica à estrutura
utsnamedo kernel. - Cgroup namespace: Isola os grupos de controle (cgroups) entre contêineres, permitindo que cada contêiner tenha seu próprio conjunto de cgroups.
- Time namespace: Isola o tempo entre contêineres, permitindo que cada contêiner tenha seu próprio relógio e fuso horário. Além dos namespaces listados acima, é comum mencionar o Seccomp (Secure Computing Mode) ao discutir o isolamento de contêineres. Contudo, o Seccomp não é um namespace — trata-se de um mecanismo do kernel Linux para filtrar chamadas de sistema (syscalls), restringindo quais operações um processo pode executar. Ele atua como uma camada adicional de segurança, complementando o isolamento proporcionado pelos namespaces.
A imagem abaixo ilustra como esses recursos estão isolados dentro de um namespace:
flowchart TB
subgraph Host["Sistema Operacional Host"]
direction LR
Kernel["Kernel Linux"]
subgraph C1["Contêiner A"]
PID1["PID"]
NET1["NET"]
MNT1["MNT"]
USER1["USER"]
end
subgraph C2["Contêiner B"]
PID2["PID"]
NET2["NET"]
MNT2["MNT"]
USER2["USER"]
end
C1 --- Kernel
C2 --- Kernel
end
Criando um ambiente isolado com namespace¶
No Linux, você pode criar um ambiente isolado usando namespaces com o comando unshare. O comando unshare permite que você execute um comando em um novo namespace, isolando-o do restante do sistema. Isso é útil para testar e desenvolver aplicativos em um ambiente isolado.
Neste exemplo, criaremos um novo namespace de PID, rede e sistema de montagem, e iniciaremos um bash dentro desse ambiente isolado.
Vamos analisar os parâmetros usados:
-p: Cria um novo namespace de PID, onde os processos terão IDs independentes do sistema principal.-m: Cria um novo namespace de montagem, isolando o sistema de arquivos.-n: Cria um novo namespace de rede, isolando as interfaces de rede.-f: Força a criação do novo processo no namespace.--mount-proc: Monta o sistema de arquivos/procdentro do namespace, permitindo visualizar apenas os processos do ambiente isolado.bash: O comando a ser executado dentro do novo namespace.
Após executar o comando, você terá um shell bash dentro de um novo namespace de PID, rede e sistema de montagem. Isso significa que qualquer processo iniciado dentro desse shell não terá acesso aos processos ou recursos do sistema principal.
Para verificar o isolamento, execute o comando ps aux dentro do namespace:
Você verá que apenas o processo bash (PID 1) e o próprio ps aparecem listados. Compare essa saída abrindo outro terminal (fora do namespace) e executando o mesmo ps aux — a lista de processos será significativamente maior.
Outra forma de observar o isolamento é verificar a rede. Dentro do namespace, execute:
Você verá apenas a interface lo (loopback), pois o namespace de rede está isolado e não possui acesso às interfaces de rede do host.
Para sair do namespace, basta executar:
Da parte externa do namespace, você pode verificar os processos em execução usando o comando ps. Nesse caso, queremos identificar o PID do bash que foi iniciado dentro do namespace. Para isso, você pode usar o comando ps com a opção -ef para listar todos os processos em execução:
Com esse comando, você verá uma lista de processos em execução e poderá identificar o PID do processo unshare. O PID do bash iniciado dentro do namespace será um filho desse processo. Uma forma de visualizar isso é usando o comando pstree, que exibe a árvore de processos:
- Substitua
<PID_IDENTIFICADO_AO_COMANDO_UNSHARE>pelo PID do processounshareque você encontrou na etapa anterior. O PID do bash será listado como um filho do processounshare.
Exercício opcional: isolando o hostname com UTS namespace¶
O UTS namespace permite que cada contêiner tenha seu próprio nome de host, independente do host real. Vamos experimentar isso na prática.
Passo 1: Crie um novo namespace de UTS junto com os demais:
O parâmetro -u cria um novo UTS namespace. Dentro do namespace, verifique o hostname atual:
Você verá o mesmo hostname do host, pois o namespace herda o valor original no momento da criação.
Passo 2: Altere o hostname dentro do namespace:
A saída agora mostrará meu-container.
Passo 3: Em outro terminal (fora do namespace), verifique que o hostname do host não foi alterado:
Ele continuará com o nome original do host. Isso demonstra o isolamento do UTS namespace — a alteração do hostname dentro do namespace não afeta o sistema host.
Esse é exatamente o comportamento que ferramentas como o Docker usam internamente: cada contêiner recebe seu próprio UTS namespace, e o Docker define o hostname do contêiner como o ID curto do contêiner (ou o valor passado pela opção --hostname).