Desvendando Contêineres: Construindo seu Próprio Ambiente Isolado no Linux sem Docker

Desvendando Contêineres: Construindo seu Próprio Ambiente Isolado no Linux sem Docker

Explore os mecanismos fundamentais por trás da tecnologia de contêineres no Linux, construindo um ambiente isolado do zero e entendendo como namespaces, chroot e cgroups funcionam para criar isolamento e gerenciar recursos.

MundiX News·12 de junho de 2026·10 min de leitura·👁 7 views

Embora plataformas como Docker tenham popularizado a contêinerização, a ideia subjacente é uma funcionalidade nativa do Linux que existe há muito tempo. Este artigo detalha como construir um contêiner mínimo no Linux utilizando apenas as ferramentas do sistema, sem depender de plataformas externas. Ao longo deste guia, desvendaremos os segredos por trás da criação de sistemas Linux isolados, a limitação do chroot como uma solução completa de contêiner, o funcionamento dos namespaces para isolamento de processos e o gerenciamento de recursos através de cgroups.

Antes de mergulharmos na construção prática, é essencial compreender os componentes que formam um contêiner. Felizmente, o Linux já oferece os mecanismos necessários; o desafio reside em aprender a utilizá-los de forma eficaz. Nosso "kit de construção de contêineres" se baseará em três pilares principais:

  • Namespaces: Responsáveis por isolar processos e seus ambientes de execução, garantindo que cada contêiner tenha sua própria visão do sistema.
  • Chroot: Utilizado para isolar o sistema de arquivos, definindo um novo diretório raiz para um processo, simulando um ambiente separado.
  • Cgroups (Control Groups): Permitem a limitação e o gerenciamento de recursos do sistema, como CPU e memória, alocados para cada contêiner.

Podemos começar a experimentar cada um desses mecanismos individualmente, mas para uma compreensão completa, seguiremos uma abordagem passo a passo, construindo um contêiner mínimo do zero.

Construindo um Sistema de Arquivos Isolado do Zero

Nosso primeiro objetivo é replicar a funcionalidade de um sistema de arquivos de contêiner. Começaremos com um diretório vazio e o transformaremos gradualmente em um ambiente Linux mínimo. Para o isolamento do sistema de arquivos, empregaremos o mecanismo chroot. Ele altera o diretório raiz de um processo, instruindo o sistema a considerar uma pasta específica como o novo /.

Passo 1. Criando um Sistema Vazio

Primeiro, criamos o diretório que servirá como raiz para nosso futuro contêiner:

bash
mkdir xakep

Ao inspecionar o conteúdo, notamos que o diretório está vazio:

bash
ls xakep

Neste momento, é apenas uma pasta comum, mas em breve abrigará o sistema de arquivos do nosso contêiner.

Passo 2. Tentando Acessar o Sistema

Agora, tentamos iniciar o chroot:

bash
sudo chroot xakep /bin/bash

Recebemos o erro esperado:

chroot: failed to run command '/bin/bash': No such file or directory

Isso é lógico, pois o diretório xakep ainda não contém um shell. Para o processo dentro do chroot, esta pasta é a raiz do sistema, e ele procura por /bin/bash especificamente lá.

Passo 3. Adicionando o Primeiro Programa

Criamos o diretório bin dentro de xakep e copiamos o bash para lá:

bash
mkdir -p xakep/bin
cp /bin/bash xakep/bin/

Tentamos novamente:

bash
sudo chroot xakep /bin/bash

O erro persiste. A razão é que a maioria dos programas no Linux depende de bibliotecas dinâmicas, que ainda não estão presentes em nosso ambiente isolado.

Passo 4. Verificando Dependências

Para descobrir quais bibliotecas o bash necessita, usamos o comando ldd:

bash
ldd /bin/bash

O comando ldd lista as bibliotecas dinâmicas necessárias para a execução de um programa. Neste contexto, ele nos ajuda a identificar os arquivos essenciais para o nosso rootfs mínimo.

Passo 5. Adicionando Bibliotecas

Para que o bash funcione, precisamos copiar as bibliotecas dinâmicas listadas pelo ldd para o diretório /lib (ou /lib64, dependendo da arquitetura) dentro do nosso chroot. Por exemplo, se ldd /bin/bash mostrar que /lib64/ld-linux-x86-64.so.2 é necessário, você copiaria:

bash
mkdir -p xakep/lib64
cp /lib64/ld-linux-x86-64.so.2 xakep/lib64/

Você precisará repetir este processo para todas as bibliotecas listadas pelo ldd que não sejam vdso ou linux-vdso.so.1.

Passo 6. Verificando Nosso Mini-Sistema

Após copiar as bibliotecas necessárias, podemos tentar executar o bash novamente:

bash
sudo chroot xakep /bin/bash

Se tudo foi copiado corretamente, você agora estará dentro do shell do seu ambiente chroot. No entanto, a experiência ainda é muito limitada. Você não tem acesso a comandos básicos como ls, cp, mv, etc., pois eles também são programas que dependem de bibliotecas.

Passo 7. Adicionando Utilitários Básicos

Para tornar o ambiente minimamente utilizável, precisamos adicionar utilitários básicos. Uma maneira eficiente de fazer isso é utilizando o BusyBox, que combina muitas ferramentas comuns em um único executável pequeno. Para isso, você precisaria baixar o binário do BusyBox, copiá-lo para xakep/bin/ e, em seguida, criar links simbólicos para os comandos que deseja disponibilizar (como ls, cp, mv, sh, etc.) apontando para o executável do BusyBox.

Criando um Sistema Linux Mínimo com BusyBox

Passo 1. Criando o Sistema de Arquivos

Crie um diretório para o seu novo sistema de arquivos:

bash
mkdir mycontainer

Passo 2. Copiando o BusyBox

Baixe o binário estático do BusyBox (ou compile-o) e copie-o para o diretório bin do seu contêiner:

bash
mkdir -p mycontainer/bin
cp /path/to/busybox mycontainer/bin/

Passo 3. Criando os Comandos

Entre no diretório bin do seu contêiner e crie links simbólicos para os comandos que o BusyBox oferece. Por exemplo:

bash
cd mycontainer/bin/
./busybox --install .
cd ../..

O comando --install . dentro do diretório bin criará os links simbólicos necessários para os comandos suportados pelo BusyBox.

Passo 4. Executando o Sistema

Agora você pode tentar executar o shell:

bash
sudo chroot mycontainer /bin/sh

Você terá um ambiente com utilitários básicos disponíveis.

Verificando a Limitação do Chroot

O chroot isola o sistema de arquivos, mas não isola processos, rede ou outros recursos do sistema host. Um processo rodando dentro de um chroot ainda pode ver e interagir com outros processos no sistema host, e pode até mesmo escapar do ambiente chroot se tiver privilégios suficientes ou explorar vulnerabilidades. Portanto, chroot por si só não constitui um contêiner seguro.

O Que Obtivemos Após o BusyBox

Com o chroot e o BusyBox, criamos um ambiente de sistema de arquivos isolado e com utilitários básicos. No entanto, ainda falta o isolamento de processos e o gerenciamento de recursos, que são cruciais para a funcionalidade completa de um contêiner.

Isolando Processos: Namespaces

Os namespaces são um recurso do kernel Linux que particiona recursos do kernel de forma que um processo em um namespace não veja os recursos de outro. Existem vários tipos de namespaces:

  • PID namespace: Isola a árvore de processos. Um processo no PID namespace 1 é o primeiro processo dentro do contêiner.
  • Network namespace: Isola a pilha de rede (interfaces de rede, tabelas de roteamento, regras de firewall).
  • Mount namespace: Isola os pontos de montagem do sistema de arquivos.
  • UTS namespace: Isola o hostname e o nome de domínio.
  • IPC namespace: Isola os recursos de comunicação entre processos (semáforos, filas de mensagens).
  • User namespace: Isola os IDs de usuário e grupo.

Para criar um contêiner com namespaces, você usaria ferramentas como unshare ou diretamente as chamadas de sistema clone() com as flags apropriadas. Por exemplo, para criar um novo PID e UTS namespace:

bash
sudo unshare --pid --uts --mount-proc /bin/bash

Isso iniciará um novo shell em um novo PID e UTS namespace. O hostname dentro deste shell será o mesmo do host, a menos que você o altere explicitamente.

Alterando o Hostname do Contêiner

Dentro de um UTS namespace isolado, você pode definir um hostname diferente:

bash
sudo unshare --uts --mount-proc hostname meu-container-host

Agora, ao iniciar um shell dentro deste namespace, o hostname será "meu-container-host".

Limitando Recursos com Cgroups

Cgroups (Control Groups) são um mecanismo do kernel Linux que permite alocar, priorizar, limitar e gerenciar o uso de recursos do sistema (CPU, memória, I/O de disco, rede) por um conjunto de processos. Eles são essenciais para garantir que um contêiner não consuma todos os recursos do host e afete outros serviços.

O Que São Cgroups

Cgroups organizam processos em hierarquias e aplicam limites a cada grupo. Existem duas versões principais: cgroups v1 e cgroups v2. A v2 é mais unificada e recomendada.

Criando Sua Própria Cgroup

Para gerenciar cgroups, você geralmente interage com o sistema de arquivos virtual montado em /sys/fs/cgroup. Vamos criar uma cgroup simples para limitar recursos.

Limitando Memória

Para limitar a memória, você criaria um diretório na hierarquia de memória e definiria os valores apropriados nos arquivos de controle.

Limitando CPU

Similarmente, para limitar o uso da CPU, você criaria um diretório na hierarquia de CPU e configuraria os parâmetros de limitação.

Adicionando um Processo a uma Cgroup

Após criar a cgroup, você adiciona o PID do processo que deseja controlar ao arquivo cgroup.procs (ou tasks em cgroups v1) dentro do diretório da cgroup.

Verificando o Limite de CPU

Você pode verificar o uso da CPU de um processo dentro de uma cgroup monitorando os arquivos de controle de CPU ou usando ferramentas como top ou htop e observando a coluna de uso da CPU.

Verificando a Limitação de Memória

Da mesma forma, o uso de memória pode ser monitorado através dos arquivos de controle de memória da cgroup.

Esquema Geral de um Contêiner

Um contêiner, como implementado por Docker ou outras plataformas, combina esses mecanismos:

  1. Isolamento do Sistema de Arquivos: Usando chroot ou, mais comumente, namespaces de montagem para criar um sistema de arquivos raiz isolado (geralmente a partir de uma imagem).
  2. Isolamento de Processos: Usando PID namespaces para que os processos dentro do contêiner tenham sua própria visão da lista de processos.
  3. Isolamento de Rede: Usando network namespaces para criar interfaces de rede virtuais e configurar o roteamento e firewall independentes.
  4. Isolamento de Recursos: Usando cgroups para limitar e gerenciar o consumo de CPU, memória e I/O.
  5. Isolamento de Hostname e IPC: Usando UTS namespaces e IPC namespaces.

Limpeza

Após experimentar, é importante remover os diretórios e sistemas de arquivos criados para liberar espaço e evitar conflitos.

Conclusão

Construir um contêiner do zero, mesmo que mínimo, revela a complexidade e a engenhosidade por trás da contêinerização. Compreender chroot, namespaces e cgroups não apenas aprofunda o conhecimento sobre como Docker e outras ferramentas funcionam, mas também capacita os administradores e desenvolvedores a gerenciar e otimizar ambientes de contêineres de forma mais eficaz. Essa jornada prática demonstra que a contêinerização é uma poderosa orquestração de recursos nativos do Linux, oferecendo isolamento e eficiência sem a necessidade de virtualização completa.

📤 Compartilhar & Baixar