Dia 21: Docker — Containers, Images, Volumes e Networks
O Docker mudou a forma como se empacota e distribui software. Em vez de instalar dependências directamente no sistema, um container empacota a aplicação e tudo o que ela precisa — bibliotecas, runtime, configuração — numa unidade isolada e portável. Este guia cobre os quatro pilares do Docker: images (o template), containers (a instância a correr), volumes (persistência de dados) e networks (comunicação entre containers). Comandos testados em Ubuntu 24.04 e Rocky Linux 9.
Este artigo faz parte da série Linux em 30 Dias. Para seguir a ordem, ver o Dia 1: Terminal, Shell e Comandos Essenciais.
Introdução: Containers vs Máquinas Virtuais
Uma máquina virtual (VM) emula hardware completo: tem o seu próprio kernel, drivers e sistema operativo convidado. Cada VM consome gigabytes de disco e RAM, mesmo quando a aplicação lá dentro precisa de poucos megabytes. O overhead de virtualização é pago a cada arranque — uma VM típica demora 30 a 60 segundos a arrancar.
Um container partilha o kernel do hospedeiro. Em vez de virtualizar hardware, usa funcionalidades do kernel Linux — namespaces (isola processos, rede, mount points) e cgroups (limita CPU, memória, I/O) — para criar isolamento ao nível do processo. O resultado: um container arranca em milissegundos, pesa megabytes e vários containers partilham o mesmo kernel sem duplicar o SO (Docker — What is Docker?, namespaces(7), cgroups(7)).
| Comparação | Máquina Virtual | Container |
|---|---|---|
| Kernel | Próprio (convidado) | Partilhado com o hospedeiro |
| Tamanho | 1–20 GB | 5–500 MB |
| Arranque | 30–60 segundos | Milissegundos a segundos |
| Isolamento | Hardware completo | Processo (namespaces + cgroups) |
| Overhead | Alto (SO completo) | Baixo (apenas a aplicação) |
O Docker foi lançado em 2013 por Solomon Hykes e popularizou o conceito de container que já existia no Linux através de LXC, cgroups e namespaces. Hoje é o padrão de facto para empacotamento de aplicações, com um ecossistema que inclui o Docker Hub (registo público de images), Docker Compose (orquestração multi-container) e Docker Swarm / Kubernetes (orquestração em cluster). A documentação oficial está em docs.docker.com.
Instalar Docker
O Docker instala-se via repositório oficial. NÃO usar os pacotes docker.io ou docker-compose das distribuições — estão desactualizados. Seguir a documentação oficial de instalação.
Ubuntu/Debian — adicionar a chave GPG e o repositório oficial:
# Instalar dependências
apt update
apt install -y ca-certificates curl gnupg
# Adicionar chave GPG oficial
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
# Adicionar repositório
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" \
> /etc/apt/sources.list.d/docker.list
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Rocky/Alma/RHEL — o Docker não está nos repositórios padrão; usar o repositório oficial:
# Adicionar repositório oficial
dnf install -y dnf-plugins-core
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Activar e arrancar o serviço
systemctl enable --now docker
Verificar a instalação:
docker --version
# Docker version 27.x, build xxx
docker run hello-world
# Hello from Docker!
# This message shows that your installation appears to be working correctly.
Para evitar escrever sudo antes de cada comando, adicionar o utilizador ao grupo docker:
usermod -aG docker $USER
# Reiniciar sessão ou: newgrp docker
docker dá-lhe acesso root sem password — um container pode montar o sistema de ficheiros do hospedeiro. Em servidores de produção, deixar o sudo docker e restringir o grupo a administradores de confiança.
Imagens e Containers
Uma image é um template só de leitura — um sistema de ficheiros em camadas com a aplicação e dependências. Um container é uma instância a correr dessa image, com uma camada de escrita por cima. A analogia: a image é o programa no disco; o container é o processo em execução (Docker — What is an image?, Docker — What is a container?).
Obter e gerir images
# Descarregar image do Docker Hub
docker pull nginx:latest
docker pull ubuntu:24.04
docker pull alpine:3.20
# Listar images locais
docker images
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx latest 605c77e624dd 2 weeks ago 142MB
# alpine 3.20 3243e171c5db 3 weeks ago 7.8MB
# Apagar image (só se não houver containers a usá-la)
docker rmi nginx:latest
# Remover images não usadas (liberta disco)
docker image prune -a
As imagens usam um sistema de camadas (layers). Cada instrução num Dockerfile cria uma camada só de leitura. Várias images que partilham a mesma imagem base reutilizam as camadas — daí o docker pull de uma segunda image ser muito mais rápido que a primeira.
Gerir containers
# Lançar container interativo
docker run -it --name teste ubuntu:24.04 bash
# -i stdin aberto
# -t aloca pseudo-terminal
# --name dá nome ao container
# Lançar em background (detached)
docker run -d --name web -p 8080:80 nginx:latest
# -d detached (corre em background)
# -p mapeia porta hospedeiro:container
# Listar containers a correr
docker ps
# CONTAINER ID IMAGE STATUS PORTS NAMES
# a1b2c3d4e5f6 nginx Up 2 minutes 0.0.0.0:8080->80/tcp web
# Listar todos (incluindo parados)
docker ps -a
# Ver logs de um container
docker logs web
docker logs -f web # seguir em tempo real
docker logs --tail 50 web # últimas 50 linhas
# Entrar num container a correr
docker exec -it web bash
# Parar e arrancar
docker stop web
docker start web
# Reiniciar
docker restart web
# Apagar container (tem de estar parado)
docker rm web
docker rm -f web # força mesmo a correr
# Apagar todos os containers parados
docker container prune
Dockerfile — construir images próprias
O Dockerfile é um ficheiro de texto com instruções para construir uma image. Exemplo — uma aplicação Node.js simples:
FROM node:20-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Construir a image:
docker build -t minha-app:1.0 .
# -t tag (nome:versão)
# . contexto (directório actual)
docker run -d --name app -p 3000:3000 minha-app:1.0
Instruções principais do Dockerfile (referência oficial):
| Instrução | Função |
|---|---|
FROM |
Define a image base (obrigatório, primeira instrução) |
WORKDIR |
Define o directório de trabalho dentro do container |
COPY |
Copia ficheiros do hospedeiro para o container |
RUN |
Executa um comando durante o build (cria camada) |
EXPOSE |
Documenta a porta que o container escuta (não publica) |
CMD |
Comando a executar ao arrancar o container (um só) |
ENTRYPOINT |
Comando fixo; CMD pode passar argumentos |
Volumes e Bind Mounts — Persistência de Dados
Quando um container é apagado com docker rm, a sua camada de escrita desaparece — os dados perdem-se. Para persistir dados entre reinícios e entre containers, o Docker oferece três mecanismos: volumes, bind mounts e tmpfs mounts (Docker — Volumes).
Volumes
Volumes são a forma recomendada para persistência. O Docker gere o armazenamento — fica em /var/lib/docker/volumes/ — e sobrevive à remoção do container. São independentes do sistema de ficheiros do hospedeiro e funcionam em Linux, Mac e Windows.
# Criar volume
docker volume create dados_pg
# Usar volume num container
docker run -d --name postgres \
-e POSTGRES_PASSWORD=segredo \
-v dados_pg:/var/lib/postgresql/data \
postgres:16
# Listar volumes
docker volume ls
# Ver detalhes
docker volume inspect dados_pg
# Apagar volume (se não estiver em uso)
docker volume rm dados_pg
# Remover volumes não usados
docker volume prune
Bind Mounts
Bind mounts montam um directório específico do hospedeiro dentro do container. Úteis para desenvolvimento — o código no hospedeiro aparece dentro do container em tempo real — mas menos portáveis porque dependem do layout do sistema de ficheiros do hospedeiro.
# Bind mount: directório actual → /app no container
docker run -d --name dev \
-v $(pwd):/app \
-w /app \
node:20-slim npm start
# Sintaxe longa (preferida para legibilidade)
docker run -d --name dev \
--mount type=bind,source=$(pwd),target=/app \
node:20-slim npm start
| Característica | Volume | Bind Mount |
|---|---|---|
| Localização | Gerida pelo Docker | Directório do hospedeiro |
| Portabilidade | Alta (independente do SO) | Baixa (depende do caminho) |
| Backup | docker volume commands |
Ferramentas normais do SO |
| Uso típico | Bases de dados, dados de produção | Desenvolvimento, config files |
/ ou /etc.
Networks — Comunicação entre Containers
Cada container tem a sua própria stack de rede. Por defeito, o Docker cria uma rede bridge chamada bridge e liga todos os containers a ela. Mas containers na bridge predefinida não se conseguem resolver por nome — para isso, criar uma rede personalizada (Docker — Networking).
Tipos de rede
| Driver | Descrição | Uso |
|---|---|---|
bridge |
Rede virtual isolada no hospedeiro. NAT para acesso externo. | Predefinido, standalone |
host |
Container partilha a stack de rede do hospedeiro (sem isolamento). | Performance máxima |
overlay |
Rede distribuída entre múltiplos hosts (Swarm). | Clusters Docker Swarm |
none |
Sem rede. Container totalmente isolado. | Batch processing isolado |
Gerir redes
# Listar redes
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# a1b2c3d4e5f6 bridge bridge local
# b2c3d4e5f6a1 host host local
# c3d4e5f6a1b2 none null local
# Criar rede personalizada
docker network create app-net
# Ligar containers à rede (por nome, DNS automático)
docker run -d --name db --network app-net \
-e POSTGRES_PASSWORD=segredo postgres:16
docker run -d --name web --network app-net \
-p 8080:3000 minha-app:1.0
# O container "web" consegue aceder a "db" por nome:
# psql -h db -U postgres
# Ver detalhes da rede
docker network inspect app-net
# Desligar container de uma rede
docker network disconnect app-net web
# Apagar rede (se não houver containers ligados)
docker network rm app-net
# Remover redes não usadas
docker network prune
A vantagem de uma rede personalizada é a resolução de nomes por DNS. O container web liga-se ao db usando o nome — não precisa de saber o IP. Se o db for reiniciado e mudar de IP, o DNS actualiza automaticamente.
Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
permission denied ao executar docker |
Utilizador não está no grupo docker | sudo usermod -aG docker $USER e reiniciar sessão |
Cannot connect to the Docker daemon |
Serviço docker parado | sudo systemctl start docker |
port is already allocated |
Outro processo usa a mesma porta | Mudar o mapeamento -p ou parar o processo conflituante |
No space left on device |
Disco cheio com images/volumes antigos | docker system prune -a para limpar tudo não usado |
| Dados desaparecem ao reiniciar container | Sem volume; camada de escrita é efémera | Montar volume com -v para dados persistentes |
| Containers não se encontram por nome | Estão na bridge predefinida (sem DNS) | Criar rede personalizada e ligar os containers com --network |
image not found no build |
Typo no nome da image base ou sem tag | Verificar FROM no Dockerfile; confirmar tag em hub.docker.com |
Container sai imediatamente (Exited (0)) |
CMD/ENTRYPOINT termina sem processo daemon | Verificar docker logs; usar -it para debug interativo |
bind source path does not exist |
Directório do hospedeiro não existe | Criar o directório antes de docker run ou usar caminho absoluto |
docker system df mostra o espaço usado por images, containers e volumes. Executar antes de prune para avaliar o que vai ser limpo.
Artigos Relacionados
- Dia 20: VPN — WireGuard e OpenVPN, Túneis Site-to-Site — Túneis seguros para acesso remoto; containers podem correr serviços VPN
- Dia 14: Backups e Recuperação — rsync, tar e BorgBackup — Backup de volumes Docker e dados de containers
- Dia 7: Systemd — Services, Timers e journalctl — O Docker corre como serviço systemd; entender a integração
- Dia 9: Firewalls — iptables, nftables, UFW e firewalld — O Docker manipula iptables/nftables; conhecer as regras geradas
- Dia 16: DNS Server com Bind9 — Containers usam DNS interno; entender resolução de nomes