Aula 23: Containers Linux — Docker e LXC na prática
Seja bem-vindo à vigésima terceira aula do curso Linux — Do Zero ao Avançado. Hoje vamos mergulhar fundo no universo dos Containers Linux, uma tecnologia que revolucionou a forma como desenvolvemos, distribuímos e executamos aplicações em ambientes de produção. Containers Linux não são máquinas virtuais tradicionais — são unidades de software leves, portáteis e auto-suficientes que compartilham o kernel do sistema hospedeiro, mas isolam processos, rede e sistema de arquivos usando funcionalidades nativas do kernel como namespaces e cgroups. Nesta aula, vamos explorar as duas principais ferramentas de containerização no ecossistema Linux: o Docker, amplamente adotado para empacotamento e entrega de aplicações, e o LXC (Linux Containers), que oferece um ambiente mais próximo de um sistema operacional completo, ideal para virtualização leve de servidores.
Por que isso importa para um profissional de infraestrutura ou segurança da informação? Simples: containers estão em toda parte. Orquestradores como Kubernetes, pipelines de CI/CD, ambientes de desenvolvimento isolados, microsserviços — tudo roda sobre containers. Dominar a criação, configuração e solução de problemas em Containers Linux é hoje um pré-requisito para qualquer engenheiro de plataforma, analista de infraestrutura ou especialista em DevSecOps. Em nossos projetos na JRT Technology Solutions, utilizamos diariamente essas tecnologias para provisionar ambientes homogêneos entre desenvolvimento e produção, reduzir custos com virtualização e implementar políticas rígidas de isolamento e segurança.
Nesta aula, você vai instalar e configurar o Docker e o LXC do zero, tanto em distribuições baseadas em Debian (Ubuntu) quanto em Red Hat (Rocky Linux). Vamos criar containers, inspecionar seu funcionamento interno, gerenciar imagens e snapshots, mapear portas de rede e montar volumes persistentes. Cada comando será explicado em detalhes, com suas flags e opções relevantes, e você terá acesso às saídas esperadas para validar cada etapa. Ao final, você será capaz de decidir quando usar Docker ou LXC, diagnosticar os erros mais comuns e implementar boas práticas de segurança e desempenho em ambientes containerizados.
Prepare-se para uma das aulas mais densas e práticas deste curso. O conteúdo foi estruturado para que você possa replicar cada procedimento em seu próprio laboratório — seja ele uma VM local, um servidor bare-metal ou instâncias na nuvem. Lembre-se: o domínio de Containers Linux exige prática constante. Vamos começar.
O que você vai aprender nesta aula
- Os fundamentos do kernel Linux que tornam os containers possíveis: namespaces, cgroups, capabilities e union filesystems
- A diferença conceitual e prática entre containers de aplicação (Docker) e containers de sistema (LXC)
- A instalação completa do Docker Engine no Ubuntu 24.04 LTS e no Rocky Linux 9.x, incluindo pós-instalação e verificação de integridade
- A instalação e configuração do LXC e do LXD nos mesmos sistemas operacionais
- Como executar containers Docker com mapeamento de portas, variáveis de ambiente e volumes persistentes
- Como criar containers LXC privilegiados e não privilegiados, gerenciar snapshots e configurar perfis
- A inspeção de containers em tempo real com comandos de diagnóstico
- Verificação completa da instalação com saídas esperadas para cada sistema
- Os 5 erros mais comuns ao trabalhar com containers Linux e suas soluções definitivas
- Boas práticas de segurança, otimização de imagens e limpeza de artefatos
Pré-requisitos e Ambiente
Para acompanhar esta aula prática, você precisa de:
- Uma máquina com Linux — recomenda-se Ubuntu Server 24.04 LTS ou Rocky Linux 9.3+, com pelo menos 4 GB de RAM, 2 vCPUs e 20 GB de disco livre. Se possível, prepare duas VMs, uma para cada distribuição, para testar ambos os métodos de instalação.
- Acesso root ou a um usuário com privilégios sudo — todos os comandos de instalação de pacotes exigem privilégios elevados.
- Conhecimento básico de redes TCP/IP — vamos mapear portas e inspecionar interfaces de rede virtuais. Se precisar revisar, volte à Aula 18 (Fundamentos de Rede no Linux).
- Conceitos de systemd — tanto o Docker quanto o LXC utilizam units do systemd. Revisite a Aula 12 se tiver dúvidas sobre gerenciamento de serviços.
- Um editor de texto — usaremos principalmente o vim e o nano para editar arquivos de configuração.
- Conexão com a internet — as instalações exigem repositórios oficiais e download de imagens.
Se você estiver usando uma distribuição diferente (Fedora, Debian puro, AlmaLinux), os passos serão muito similares, com pequenas variações nos gerenciadores de pacotes. Consulte a documentação oficial para adaptações. Em nossos treinamentos na JRT Technology Solutions, padronizamos os procedimentos para as famílias Debian e Red Hat exatamente como faremos aqui.
Fundamentos dos Containers Linux: Namespaces, Cgroups e Union Filesystems
Antes de instalarmos qualquer ferramenta, é crucial entender o que acontece “sob o capô” quando executamos um container. Containers Linux não são uma tecnologia monolítica, mas uma combinação inteligente de diversas funcionalidades do kernel Linux, cada uma resolvendo um problema específico de isolamento ou gerenciamento de recursos. Os três pilares fundamentais são:
Namespaces: introduzidos a partir do kernel 2.6.24, os namespaces criam visões particionadas dos recursos do sistema. Existem atualmente 8 tipos de namespaces no kernel moderno: PID (isolamento de processos), NET (interfaces de rede e tabelas de roteamento), MNT (pontos de montagem), UTS (hostname e domínio NIS), IPC (comunicação interprocesso), USER (mapeamento de UIDs/GIDs), CGROUP (visão dos cgroups) e TIME (relógios monotônicos e boot time). Quando você inicia um container, o runtime (Docker, LXC, containerd) cria um novo conjunto de namespaces para aquele processo raiz do container, isolando-o do restante do sistema.
Cgroups (Control Groups): se namespaces tratam do isolamento (o que o container vê), os cgroups tratam do controle (quanto o container pode consumir). Com cgroups v1 e v2, você pode limitar CPU, memória RAM, I/O de disco e largura de banda de rede por grupo de processos. Sem cgroups, um container com um fork bomb ou um vazamento de memória poderia derrubar o host inteiro. É por meio dos cgroups que definimos –memory=512m ou –cpus=2 nos comandos de inicialização de containers.
Union Filesystems (OverlayFS, AUFS, btrfs, ZFS): containers são construídos a partir de imagens empilhadas em camadas. Cada instrução em um Dockerfile (RUN, COPY, ADD) gera uma nova camada somente-leitura. Quando o container é iniciado, uma camada fina de leitura-escrita é adicionada no topo. Isso permite compartilhamento de camadas base entre containers, economizando espaço em disco e acelerando deploys. O OverlayFS é o driver de armazenamento padrão na maioria das distribuições modernas. O LXC também pode utilizar backends de armazenamento como ZFS, LVM ou dir puro.
Compreender esses três mecanismos permite diagnosticar problemas como “por que meu container não acessa a rede externa?” (namespace NET mal configurado ou bridge ausente) ou “por que meu container foi morto com OOM?” (cgroups de memória estourados). Em ambientes de produção na JRT Technology Solutions, ajustamos finamente os limites de cgroups para garantir isolamento estrito entre cargas de trabalho de clientes diferentes rodando no mesmo host bare-metal.
Instalação do Docker Engine no Ubuntu/Debian e Rocky Linux
O Docker está disponível em duas edições: Docker Desktop (com interface gráfica, voltada para desenvolvedores) e Docker Engine (apenas daemon e CLI, ideal para servidores). Nesta aula, instalaremos o Docker Engine diretamente dos repositórios oficiais, método recomendado para ambientes de produção. Vamos seguir o procedimento completo para ambas as famílias de distribuições. Cada comando será executado como root ou com sudo prefixado.
Ubuntu 24.04 LTS / Debian 12
- Remover versões antigas — Pacotes como docker.io ou docker-compose dos repositórios da distribuição podem conflitar:
# Remove pacotes antigos que possam existir (Docker antigo, podman, etc.)
sudo apt-get remove -y docker docker-engine docker.io containerd runc 2>/dev/null
sudo apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2>/dev/null
sudo rm -rf /var/lib/docker /etc/docker
echo "Limpeza concluída — sistema pronto para instalação limpa do Docker."
Limpeza concluída — sistema pronto para instalação limpa do Docker.
- Instalar dependências e configurar o repositório oficial — O Docker mantém seu próprio repositório APT com pacotes assinados via GPG:
# Atualiza a lista de pacotes e instala dependências
sudo apt-get update -y
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# Cria o diretório para chaves GPG e adiciona a chave oficial do Docker
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Adiciona o repositório à lista de sources do APT
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Atualiza novamente a lista de pacotes — agora com o repositório do Docker
sudo apt-get update -y
echo "Repositório oficial do Docker adicionado com sucesso."
Repositório oficial do Docker adicionado com sucesso.
- Instalar os pacotes do Docker Engine — Instalamos o daemon (docker-ce), o cliente CLI (docker-ce-cli), o runtime de baixo nível (containerd.io) e os plugins auxiliares (Buildx e Compose v2):
# Instala Docker Engine, containerd e plugins
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
echo "Docker Engine instalado. Verificando a versão..."
docker --version
Docker version 27.1.1, build 6312585
- Pós-instalação — configurar o grupo docker — Por padrão, o socket do Docker (/var/run/docker.sock) pertence ao grupo docker. Adicionar seu usuário a este grupo evita digitar sudo em cada comando:
# Cria o grupo 'docker' se não existir e adiciona o usuário atual
sudo groupadd docker 2>/dev/null
sudo usermod -aG docker $USER
# Aplica as mudanças de grupo sem sair da sessão
newgrp docker <<< "docker run hello-world"
Se você receber uma mensagem de "Hello from Docker!", a instalação está funcional. Caso contrário, faça logout e login novamente para que a membership do grupo seja recarregada.
Rocky Linux 9.x / RHEL 9 / AlmaLinux 9
- Remover versões antigas e configurar repositório — A família Red Hat usa dnf e o repositório oficial do Docker deve ser adicionado manualmente:
# Remove versões antigas e configura o repositório oficial Docker CE
sudo dnf remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc 2>/dev/null
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
# Atualiza o cache de metadados e instala os pacotes
sudo dnf makecache
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
echo "Docker instalado no Rocky Linux. Habilitando e iniciando o daemon..."
Docker instalado no Rocky Linux. Habilitando e iniciando o daemon...
- Habilitar e iniciar o serviço — No RHEL/Rocky, o serviço não é iniciado automaticamente após a instalação:
# Inicia o daemon do Docker e configura para iniciar no boot
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager -l | head -5
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
Active: active (running) since Sun 2026-08-02 10:15:22 UTC; 5s ago
Docs: https://docs.docker.com
Main PID: 2341 (dockerd)
- Configurar o grupo docker — O procedimento é idêntico ao Ubuntu:
sudo groupadd docker 2>/dev/null
sudo usermod -aG docker $USER
newgrp docker <<< "docker run --rm hello-world"
Com o Docker funcional em ambas as distribuições, você está pronto para criar e gerenciar containers de aplicação. A instalação via repositório oficial garante atualizações de segurança e compatibilidade com o ecossistema Docker.
Instalação do LXC e LXD no Ubuntu/Debian e Rocky Linux
O LXC (Linux Containers) é um projeto mais antigo que o Docker, focado em oferecer ambientes completos de sistema operacional dentro de containers, similares a VMs leves. O LXD é um daemon que adiciona uma camada de gerenciamento sobre o LXC, fornecendo uma CLI amigável (lxc), API REST, snapshots e migração ao vivo. Nosso foco será no LXD, que está disponível como pacote snap (Ubuntu) ou via repositórios EPEL/COPR (Rocky Linux).
Ubuntu 24.04 LTS (via Snap)
- Instalar e inicializar o LXD — O snap é o método recomendado para Ubuntu, pois inclui atualizações contínuas e suporte a ZFS nativo:
# Instala o LXD via snap (já vem pré-instalado em algumas edições do Ubuntu)
sudo snap install lxd
# Alternativa: sudo snap refresh lxd --channel=latest/stable
# Adiciona o usuário atual ao grupo lxd
sudo usermod -aG lxd $USER
newgrp lxd
# Inicializa o LXD com configuração guiada interativa
lxd init --auto
echo "LXD inicializado com configuração padrão."
LXD inicializado com configuração padrão.
O comando lxd init --auto cria um bridge de rede (lxdbr0), configura um pool de armazenamento baseado em diretório (/var/snap/lxd/common/lxd/storage-pools/default) e habilita o acesso via socket Unix. Se preferir uma configuração personalizada (ZFS, LVM, rede específica), execute lxd init sem a flag --auto e responda às perguntas interativas.
Rocky Linux 9.x (via EPEL + COPR)
No ecossistema Red Hat, o LXC/LXD não está nos repositórios oficiais, mas está disponível via EPEL para o LXC e via COPR ou snap para o LXD. Aqui usaremos o snap, que garante a mesma experiência do Ubuntu:
- Instalar o snapd e configurar o LXD:
# Instala EPEL (necessário para o snapd no Rocky)
sudo dnf install -y epel-release
sudo dnf update -y
# Instala e habilita o snapd
sudo dnf install -y snapd
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap # Ativa o suporte a snaps clássicos
# Instala o LXD via snap
sudo snap install lxd
sudo snap refresh lxd --channel=latest/stable
# Adiciona o usuário ao grupo lxd e inicializa
sudo usermod -aG lxd $USER
newgrp lxd
lxd init --auto
Após a inicialização, o daemon lxd estará escutando no socket Unix e pronto para receber comandos. Em nossos projetos na JRT Technology Solutions, utilizamos LXD para criar ambientes de laboratório de segurança, onde cada analista recebe um container completo com Kali Linux ou Parrot OS, isolado e com snapshots para rollback rápido.
Primeiros Passos com Docker: Executando e Gerenciando Containers
Com o Docker Engine instalado e o daemon em execução, vamos criar, inspecionar e gerenciar containers. Vamos começar com exemplos práticos que demonstram os conceitos aprendidos na seção teórica: namespaces, cgroups e camadas de imagem. Utilize o terminal como usuário com acesso ao grupo docker.
1. Executando um container simples e entendendo sua anatomia:
# Baixa a imagem 'alpine' (apenas ~7 MB) e executa um shell interativo
docker run -it --name meu-alpine alpine:latest /bin/sh
# Dentro do container, inspecione o hostname e os processos visíveis:
hostname
ps aux
ip addr show
# Saia do container com 'exit' ou Ctrl+D
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
Digest: sha256:b89d9c93e9ed3597454152d1e2a03f...
Status: Downloaded newer image for alpine:latest
/ # hostname
d2c1a8f9b4e3
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 /bin/sh
7 root 0:00 ps aux
/ # ip addr show
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
inet 127.0.0.1/8 scope host lo
8: eth0@if9: mtu 1500 qdisc noqueue state UP
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
Observe: dentro do container, o PID 1 é o shell que iniciamos, não o systemd ou init do host. A interface eth0@if9 é um par de veth (Virtual Ethernet) — uma ponta está dentro do container (eth0) e a outra está no bridge docker0 do host. O hostname é um identificador único gerado pelo Docker (ou definido pela flag --name). Este isolamento é fruto dos namespaces PID, NET e UTS.
2. Containers em segundo plano com mapeamento de portas:
# Executa um servidor Nginx em segundo plano (-d), mapeando a porta 8080 do host para a 80 do container
docker run -d --name meu-nginx -p 8080:80 nginx:alpine
# Verifica se está rodando e testa com curl
docker ps --filter "name=meu-nginx"
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx:alpine "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 0.0.0.0:8080->80/tcp meu-nginx
200
O código HTTP 200 confirma que o Nginx está servindo páginas. O mapeamento de portas utiliza iptables (ou nftables) no host para redirecionar o tráfego da porta 8080 para o container. Você pode inspecionar as regras com sudo iptables -t nat -L DOCKER.
3. Persistência de dados com volumes:
# Cria um diretório no host para armazenar dados persistentes
mkdir -p /home/$USER/nginx-data
# Cria um container Nginx que monta esse diretório no caminho onde o Nginx serve arquivos
docker run -d --name nginx-volume -p 8081:80 -v /home/$USER/nginx-data:/usr/share/nginx/html:ro nginx:alpine
# Cria um arquivo de teste no host e verifica se é acessível via container
echo "Containers Linux — Persistência de Dados
" > /home/$USER/nginx-data/index.html
curl http://localhost:8081
Containers Linux — Persistência de Dados
A flag -v (ou --volume) monta um bind mount do host para o container. A opção :ro torna a montagem read-only dentro do container, impedindo que processos dentro dele modifiquem os arquivos — uma prática de segurança recomendada.
4. Inspecionando e depurando containers:
# Exibe logs de um container específico
docker logs meu-nginx --tail 20
# Inspeciona detalhes em JSON (redireciona para o jq para melhor visualização)
docker inspect meu-nginx | jq '.[0].NetworkSettings.IPAddress'
# Acessa um shell dentro de um container já em execução (sem iniciar um novo)
docker exec -it meu-nginx /bin/sh
# Dentro do container, verifique os limites de cgroups
cat /sys/fs/cgroup/memory.max
"172.17.0.3"
/ # cat /sys/fs/cgroup/memory.max
max
O comando docker inspect é uma ferramenta poderosa que expõe todos os detalhes de configuração do container: variáveis de ambiente, volumes montados, configurações de rede, limites de recursos e muito mais. Em troubleshooting, é frequentemente o primeiro comando que executamos na JRT Technology Solutions.
Primeiros Passos com LXC/LXD: Criando e Gerenciando Containers de Sistema
Diferentemente do Docker, que executa um único processo principal por container, o LXC inicia um sistema operacional completo — com init (systemd ou sysvinit), múltiplos processos e serviços. Isso o torna ideal para simular servidores completos ou ambientes de laboratório. Vamos criar containers com LXD, gerenciar snapshots e configurar limites de recursos.
1. Listando imagens disponíveis e lançando um container Ubuntu:
# Lista imagens de sistemas disponíveis nos repositórios remotos do LXD
lxc image list images: ubuntu | head -10
# Lança um container chamado 'lab-ubuntu' usando a imagem Ubuntu 24.04
lxc launch images:ubuntu/noble lab-ubuntu
# Aguarda o container inicializar e verifica seu estado
lxc list lab-ubuntu
lxc exec lab-ubuntu -- cat /etc/os-release | head -5
+--------------+--------------+--------+------------------------------------+--------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+--------------+--------------+--------+------------------------------------+--------------+-----------+
| lab-ubuntu | RUNNING | 10.0.0.10 | fd42:abc:1234:5678::1 | CONTAINER | 0 |
+--------------+--------------+--------+------------------------------------+--------------+-----------+
NAME="Ubuntu"
VERSION="24.04 LTS (Noble Numbat)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 24.04 LTS"
O container lab-ubuntu já possui um endereço IP alocado pelo bridge lxdbr0 (rede 10.0.0.0/24 por padrão). Você pode acessar o console diretamente com lxc exec lab-ubuntu -- bash ou usar lxc console lab-ubuntu para um console interativo.
2. Snapshots e rollback:
# Cria um snapshot chamado 'estado-limpo' antes de instalar pacotes
lxc snapshot lab-ubuntu estado-limpo
# Instala alguns pacotes e verifica
lxc exec lab-ubuntu -- apt-get update -y
lxc exec lab-ubuntu -- apt-get install -y apache2
lxc exec lab-ubuntu -- systemctl status apache2 --no-pager
# Restaura o snapshot para voltar ao estado anterior
lxc stop lab-ubuntu
lxc restore lab-ubuntu estado-limpo
lxc start lab-ubuntu
# Verifica que o Apache não está mais instalado
lxc exec lab-ubuntu -- dpkg -l apache2 2>&1 | grep "no packages found"
dpkg-query: no packages found matching apache2
Snapshots são instantâneos do estado completo do container (disco + memória se houver). No LXD, eles são extremamente rápidos de criar e restaurar, especialmente quando se utiliza ZFS como backend de armazenamento. Em laboratórios de treinamento na JRT Technology Solutions, usamos snapshots extensivamente para resetar ambientes entre exercícios.
3. Configurando limites de recursos (cgroups) no LXC:
# Define limites de CPU e memória no container (2 vCPUs, 512 MB RAM)
lxc config set lab-ubuntu limits.cpu 2
lxc config set lab-ubuntu limits.memory 512MB
# Aplica e verifica as configurações
lxc config show lab-ubuntu | grep limits
limits.cpu: "2"
limits.memory: 512MB
Para validar que os limites estão sendo aplicados, execute lxc exec lab-ubuntu -- nproc (deve retornar 2) e lxc exec lab-ubuntu -- free -m (deve mostrar ~512 MB disponíveis). Essas configurações utilizam cgroups v2 no kernel e são aplicadas instantaneamente, sem necessidade de reiniciar o container.
4. Perfis de configuração no LXD:
Perfis permitem aplicar conjuntos de configurações a múltiplos containers. O perfil default é aplicado a todos os novos containers. Vamos criar um perfil personalizado:
# Cria um novo perfil chamado 'restrito'
lxc profile create restrito
# Define limites e restrições de segurança
lxc profile set restrito limits.cpu 1
lxc profile set restrito limits.memory 256MB
lxc profile set restrito security.nesting false
lxc profile set restrito security.privileged false
# Exibe o conteúdo do perfil
lxc profile show restrito
config:
limits.cpu: "1"
limits.memory: 256MB
security.nesting: "false"
security.privileged: "false"
description: ""
devices: {}
name: restrito
Para aplicar este perfil a um container, use lxc profile add NOME-DO-CONTAINER restrito. Perfis são herança e composição poderosas que simplificam a administração de dezenas de containers com políticas consistentes.
Verificando a Instalação / Testando a Configuração
Esta seção é crítica: após instalar e configurar Docker e LXC, execute as verificações abaixo em cada sistema. As saídas esperadas confirmam que todas as funcionalidades estão operacionais. Se qualquer comando falhar, revise a seção correspondente da aula antes de prosseguir.
Verificação do Docker
# 1. Verifica que o daemon está rodando e a versão instalada
docker version --format '{{.Server.Version}}'
# 2. Executa o container de teste oficial e remove automaticamente (--rm)
docker run --rm hello-world | head -4
# 3. Verifica se o usuário pertence ao grupo docker (sem sudo)
groups | grep -o docker
# 4. Lista containers ativos (deve estar vazio se você parou todos)
docker ps -a
27.1.1
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
docker
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Verificação do LXC/LXD
# 1. Verifica a versão do LXD e se o daemon está respondendo
lxd --version
# 2. Lista containers existentes e seus estados
lxc list
# 3. Verifica as redes gerenciadas pelo LXD
lxc network list
# 4. Verifica os pools de armazenamento
lxc storage list
5.21
+--------------+---------+---------------------+------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+--------------+---------+---------------------+------+------------+-----------+
| lab-ubuntu | RUNNING | 10.0.0.10 (eth0) | | CONTAINER | 0 |
+--------------+---------+---------------------+------+------------+-----------+
+----------+----------+----------+---------------+---------------------------+-------------+
| NAME | TYPE | MANAGED | IPV4 | IPV6 | DESCRIPTION |
+----------+----------+----------+---------------+---------------------------+-------------+
| lxdbr0 | bridge | YES | 10.0.0.1/24 | fd42:abc:1234:5678::1/64 | |
+----------+----------+----------+---------------+---------------------------+-------------+
+----------+--------+--------+-----------------------------+
| NAME | DRIVER | SIZE | DESCRIPTION |
+----------+--------+--------+-----------------------------+
| default | dir | N/A | Default storage pool |
+----------+--------+--------+-----------------------------+
Se todas as saídas corresponderem (com pequenas variações de versão), seu ambiente de Containers Linux está 100% funcional. Parabéns!
Erros Comuns e Como Resolver
Durante a instalação e operação de containers, alguns erros são recorrentes. Abaixo, os 5 problemas mais frequentes que encontramos em campo — inclusive em suporte a clientes da JRT Technology Solutions — com suas causas, sintomas e soluções completas.
-
Erro: "Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"
Causa: O daemon do Docker não está em execução, ou o usuário atual não tem permissão para acessar o socket.
Solução 1: Verificar se o serviço está ativo: sudo systemctl status docker. Se não estiver, inicie com sudo systemctl start docker e habilite com sudo systemctl enable docker.
Solução 2: Se o serviço está rodando mas o erro persiste, execute ls -la /var/run/docker.sock. O grupo deve ser docker com permissão srw-rw----. Adicione seu usuário ao grupo: sudo usermod -aG docker $USER, faça logout e login novamente.
Solução 3: Em sistemas com SELinux (Rocky/RHEL), o booleano container_manage_cgroup pode estar desabilitado. Execute sudo setsebool -P container_manage_cgroup on e reinicie o Docker. -
Erro: "Error response from daemon: driver failed programming external connectivity on endpoint ... (iptables failed)"
Causa: Conflito de regras no iptables/nftables, geralmente após instalação de outro firewall ou após o Docker ter sido reiniciado com regras residuais.
Solução 1: Reinicie o Docker, que recriará suas chains: sudo systemctl restart docker.
Solução 2: Se o erro persistir, limpe as chains do Docker manualmente (cuidado: isso afetará containers em execução): sudo iptables -t nat -F DOCKER, sudo iptables -t filter -F DOCKER, sudo iptables -t filter -F DOCKER-ISOLATION-STAGE-1, sudo iptables -t filter -F DOCKER-ISOLATION-STAGE-2, em seguida reinicie o Docker.
Solução 3: Em ambientes com firewalld ativo, certifique-se de que o Docker não está bloqueado: sudo firewall-cmd --add-masquerade --permanent e sudo firewall-cmd --reload. -
Erro: "Error: Failed to create network 'lxdbr0': Failed to load kernel module 'bridge'"
Causa: O módulo do kernel bridge não está carregado. O LXD necessita deste módulo para criar bridges de rede virtuais.
Solução: Carregue o módulo manualmente e configure para carregamento automático: sudo modprobe bridge, depois execute echo "bridge" | sudo tee /etc/modules-load.d/lxd-bridge.conf. Reinicie o LXD com sudo systemctl restart snap.lxd.daemon. Verifique com lsmod | grep bridge. -
Erro: "LXD doesn't appear to be installed. Please install LXD before running this tool."
Causa: O snap do LXD foi instalado mas o link simbólico para o binário lxc não está no PATH, comum no Rocky Linux.
Solução: Verifique se o snap está no PATH: echo $PATH | grep /snap/bin. Caso não esteja, adicione ao seu .bashrc: export PATH="/snap/bin:$PATH". Recarregue com source ~/.bashrc. Verifique com which lxc, que deve retornar /snap/bin/lxc. -
Erro: "Error: Failed to run: /usr/bin/lxc ... setrlimit: RLIMIT_NOFILE: Operation not permitted"
Causa: Limites de arquivos abertos (file descriptors) muito baixos no container LXC não privilegiado, geralmente ao rodar serviços como bancos de dados.
Solução 1: Aumentar o limite no perfil do container: lxc config set NOME limits.kernel.nofile 65535, reinicie o container.
Solução 2: Se o container for não privilegiado e o erro persistir, ajuste o arquivo /etc/security/limits.conf no host: adicione * soft nofile 65535 e * hard nofile 65535, e reinicie o host. Em ambientes de produção na JRT Technology Solutions, padronizamos este ajuste no kickstart/preseed de todos os servidores.
Boas Práticas e Dicas Avançadas para Containers Linux
Dominar a criação de containers é apenas o começo. Para operar ambientes containerizados em produção — seja com Docker, LXC ou ambos — é necessário adotar um conjunto de práticas que elevam a segurança, eficiência e governança. As recomendações a seguir são fruto de anos de experiência da JRT Technology Solutions em projetos de grande escala.
1. Minimize o footprint de suas imagens: Ao construir imagens Docker, sempre parta de imagens alpine ou distroless quando possível. Imagens baseadas em Debian/Ubuntu completas podem ter centenas de megabytes com binários desnecessários, aumentando a superfície de ataque e o tempo de pull. Utilize builds multi-stage no Dockerfile: compile em uma imagem pesada (golang, node, maven) e copie apenas o binário final para uma imagem enxuta. Exemplo de multi-stage para uma aplicação Go:
# Estágio 1: compilação
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /meu-app
# Estágio 2: imagem final (scratch é uma imagem vazia)
FROM scratch
COPY --from=builder /meu-app /meu-app
ENTRYPOINT ["/meu-app"]
2. Nunca rode containers como root, a menos que estritamente necessário: Tanto no Docker quanto no LXC, o processo principal do container roda como root por padrão dentro do namespace. Embora namespaces de usuário mitiguem o risco, a melhor prática é definir a diretiva USER no Dockerfile para um UID não privilegiado (ex: 1000). No LXC, utilize a opção security.privileged: "false" (padrão no LXD) e mapeie usuários com lxc config set NOME raw.idmap "both 1000 1000".
3. Implemente health checks: O Docker suporta HEALTHCHECK via Dockerfile ou flag --health-cmd. Isso permite que orquestradores (Docker Swarm, Kubernetes) saibam quando um container está realmente saudável, não apenas "rodando". Um health check simples para um servidor web:
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost/health || exit 1
4. Limpe artefatos regularmente (prune): Containers parados, imagens órfãs, volumes não utilizados e networks abandonadas consomem espaço em disco e poluem a listagem. Agende um cron job semanal com docker system prune -af --volumes. No LXD, use lxc storage info default para monitorar o uso e remova containers/snapshots antigos com lxc delete NOME ou lxc snapshot delete NOME SNAPSHOT. Em um incidente recente atendido pela JRT Technology Solutions, um servidor de CI/CD ficou inoperante porque /var/lib/docker atingiu 100% de uso devido a imagens não limpas.
5. Monitore e logue containers com drivers apropriados: O Docker suporta múltiplos logging drivers: json-file (padrão), syslog, journald, fluentd, splunk, etc. Configure o driver adequado no arquivo /etc/docker/daemon.json:
{
"log-driver": "journald",
"log-opts": {
"tag": "docker/{{.Name}}"
}
}
Reinicie o Docker após a alteração. Para o LXC, o LXD integra-se naturalmente ao journald do host; acompanhe com journalctl -u snap.lxd.daemon -f.
A tabela a seguir resume as principais diferenças entre Docker e LXC/LXD, para auxiliar na escolha da ferramenta correta:
| Característica | Docker | LXC/LXD |
|---|---|---|
| Foco principal | Aplicações (um processo principal) | Sistemas operacionais completos |
| Inicialização | Processo definido no ENTRYPOINT/CMD | systemd (ou outro init) inicia múltiplos serviços |
| Camadas de imagem | Sim, via UnionFS (Overlay2) | Backends de storage (ZFS, LVM, dir) sem UnionFS |
| Snapshots | Não nativo (commit gera nova imagem) | Nativo via LXD (lxc snapshot) |
| Rede padrão | Bridge docker0 (NAT), redes overlay | Bridge lxdbr0 (NAT), suporte a macvlan, SR-IOV |
| Orquestração nativa | Docker Swarm, integração com Kubernetes | LXD Cluster (nativo, com API REST consistente) |
| Isolamento de usuário | Namespaces de usuário opcionais | Não privilegiado por padrão (mapeamento UID/GID) |
| Persistência | Volumes, bind mounts | Disks adicionais, pools de storage, bind mounts |
Outra tabela de referência rápida para os comandos principais que vimos: