Dia 21: Docker no Linux — Contentores, Imagens, Volumes e Redes

O Docker transformou a forma como se empacota, distribui e executa software no Linux. Em vez de instalar dependências directamente no sistema, um contentor empacota a aplicação com tudo o que precisa — bibliotecas, runtime, configuração — numa unidade isolada e portável. Este guia cobre os quatro pilares do Docker: imagens, contentores, volumes e redes, além do Dockerfile para construir imagens personalizadas. Segue o guia oficial de instalação da documentação Docker.

1. Introdução ao Docker

O Docker é uma plataforma de contentorização que permite correr aplicações em ambientes isolados chamados contentores. Cada contentor partilha o kernel do sistema anfitrião mas tem o seu próprio sistema de ficheiros, processos e rede — é mais leve que uma máquina virtual porque não precisa de um kernel separado.

ℹ Contentor vs Máquina Virtual

Uma máquina virtual inclui um sistema operativo completo com o seu próprio kernel. Um contentor partilha o kernel do anfitrião e só empacota a aplicação e as suas dependências. Por isso, os contentores arrancam em segundos e ocupam megabytes em vez de gigabytes.

Os quatro conceitos fundamentais do Docker são:

Conceito O que é Analogia
Imagem Template só de leitura com a aplicação e dependências Receita ou molde
Contentor Instância em execução de uma imagem Bolo feito da receita
Volume Armazenamento persistente para dados do contentor Disco externo
Rede Comunicação entre contentores e com o exterior Switch virtual

O Docker usa namespaces do kernel Linux para isolar processos, rede e sistema de ficheiros, e cgroups para limitar recursos (CPU, memória). Isto significa que qualquer distribuição Linux moderna pode correr Docker nativamente, sem virtualização adicional.

2. Instalar Docker

A forma recomendada é usar o repositório oficial do Docker. O exemplo abaixo aplica-se a Debian/Ubuntu. Para outras distribuições, consulta a documentação oficial.

Primeiro, instalar dependências e adicionar a chave GPG oficial:

# Instalar dependências
sudo apt update
sudo apt install -y ca-certificates curl gnupg

# Adicionar chave GPG oficial do Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Adicionar repositório
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Depois, instalar o Docker Engine e verificar:

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Verificar instalação
docker --version
sudo docker run hello-world

Para evitar escrever sudo em cada comando, adicionar o utilizador ao grupo docker:

sudo usermod -aG docker $USER
newgrp docker

⚠ Atenção de segurança

Adicionar o utilizador ao grupo docker dá privilégios equivalentes a root. Num servidor de produção, considera usar sudo docker ou configurar rootless mode.

O serviço Docker deve estar activo. Verificar com systemctl:

sudo systemctl enable docker
sudo systemctl start docker
sudo systemctl status docker

3. Imagens e Contentores

Uma imagem é um template só de leitura. Um contentor é uma instância em execução dessa imagem. O Docker Hub é o repositório público oficial onde se encontram milhares de imagens — desde Ubuntu e Debian até PostgreSQL, Redis e Nginx.

Gerir imagens

# Procurar imagem no Docker Hub
docker search nginx

# Descarregar imagem
docker pull ubuntu:24.04

# Listar imagens locais
docker images

# Eliminar imagem
docker rmi ubuntu:24.04

As imagens usam um sistema de camadas (layers). Cada instrução num Dockerfile cria uma nova camada. Camadas idênticas entre imagens são partilhadas, poupando espaço.

Gerir contentores

# Criar e correr um contentor em primeiro plano
docker run --name meu-nginx -p 8080:80 nginx:latest

# Correr em segundo plano (-d) com reinício automático
docker run -d --name web-server --restart unless-stopped -p 8080:80 nginx

# Listar contentores ativos
docker ps

# Listar todos (incluindo parados)
docker ps -a

# Iniciar, parar, reiniciar
docker start meu-nginx
docker stop meu-nginx
docker restart meu-nginx

# Entrar num contentor em execução
docker exec -it meu-nginx bash

# Ver logs
docker logs -f meu-nginx

# Eliminar contentor
docker rm -f meu-nginx

A flag -it combina -i (interactivo) com -t (terminal), permitindo interagir com o contentor. A flag -p 8080:80 mapeia o porto 8080 do anfitrião para o porto 80 do contentor.

Flag Significado
-d Correr em segundo plano (detached)
-it Interactivo com terminal
-p H:C Mapear porto do anfitrião para o contentor
-v H:C Montar volume ou bind mount
--name Atribuir nome ao contentor
--restart Política de reinício (no, always, unless-stopped, on-failure)

4. Volumes e Persistência

Por defeito, quando um contentor é removido, todos os dados que continha desaparecem. Os volumes e bind mounts resolvem este problema, permitindo que os dados persistam independentemente do ciclo de vida do contentor.

Volumes vs Bind Mounts

Tipo Localização Quando usar
Volume Gerido pelo Docker em /var/lib/docker/volumes/ Bases de dados, dados de aplicações — produção
Bind mount Caminho absoluto no anfitrião Desenvolvimento (código fonte partilhado)
tmpfs Memória RAM do anfitrião Dados temporários sensíveis (segredos)

Comandos para gerir volumes:

# Criar volume nomeado
docker volume create dados-postgres

# Listar volumes
docker volume ls

# Correr contentor com volume
docker run -d --name postgres \
  -v dados-postgres:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=minha-senha \
  postgres:16

# Inspecionar volume
docker volume inspect dados-postgres

# Eliminar volume (contentor deve estar parado)
docker volume rm dados-postgres

Bind mounts para desenvolvimento — partilhar código fonte entre o anfitrião e o contentor:

# Montar directoria actual no contentor
docker run -d --name dev-web \
  -v $(pwd)/src:/app/src \
  -p 3000:3000 \
  node:20

ℹ Boa prática

Em produção, usa sempre volumes nomeados em vez de bind mounts. Os volumes são geridos pelo Docker, são portáveis entre nós, e o backup é mais simples. Reserva bind mounts para ambiente de desenvolvimento.

5. Redes Docker

As redes Docker permitem que os contentores comuniquem entre si e com o exterior. O Docker cria três drivers de rede por defeito: bridge, host e none.

Driver Comportamento Uso típico
bridge Rede virtual isolada no anfitrião Contentores que precisam de comunicar entre si
host Partilha a rede do anfitrião (sem isolamento) Máximo desempenho de rede
none Sem rede Contentores totalmente isolados

Criar uma rede personalizada e ligar contentores por nome (DNS automático):

# Criar rede bridge personalizada
docker network create rede-app

# Correr PostgreSQL nessa rede
docker run -d --name postgres \
  --network rede-app \
  -e POSTGRES_PASSWORD=senha123 \
  postgres:16

# Correr aplicação web na mesma rede
docker run -d --name webapp \
  --network rede-app \
  -p 8080:3000 \
  minha-app:latest

# A aplicação web liga-se ao PostgreSQL
# usando o nome "postgres" como hostname

ℹ DNS automático

Nas redes bridge personalizadas, o Docker fornece resolução DNS automática. O contentor webapp consegue resolver postgres por nome — não precisa de saber o IP. Isto não funciona na rede bridge predefinida, por isso cria sempre redes personalizadas.

Comandos essenciais de rede:

# Listar redes
docker network ls

# Inspecionar rede
docker network inspect rede-app

# Ligar contentor a uma rede em execução
docker network connect rede-app outro-contentor

# Desligar contentor
docker network disconnect rede-app outro-contentor

# Eliminar rede
docker network rm rede-app

6. Dockerfile

O Dockerfile é um ficheiro de texto com instruções para construir uma imagem personalizada. Cada instrução cria uma camada na imagem. O exemplo abaixo cria uma imagem com uma aplicação Node.js simples.

# Imagem base — usar versão especificada, não "latest"
FROM node:20-slim

# Definir directoria de trabalho
WORKDIR /app

# Copiar package.json primeiro (aproveita cache de camadas)
COPY package*.json ./

# Instalar dependências
RUN npm ci --production

# Copiar resto do código
COPY . .

# Expor porto da aplicação
EXPOSE 3000

# Comando para iniciar a aplicação
CMD ["node", "server.js"]

Construir a imagem e correr o contentor:

# Construir imagem (-t define tag, . é o contexto)
docker build -t minha-app:1.0 .

# Correr contentor
docker run -d --name app-prod -p 3000:3000 minha-app:1.0

# Construir com ficheiro Dockerfile alternativo
docker build -f Dockerfile.prod -t minha-app:prod .

Instruções principais do Dockerfile:

Instrução Função
FROM Define a imagem base
WORKDIR Define a directoria de trabalho
COPY Copia ficheiros do anfitrião para a imagem
RUN Executa um comando durante a construção
EXPOSE Documenta o porto que o contentor escuta
CMD Comando por defeito ao iniciar o contentor
ENTRYPOINT Comando fixo que não pode ser sobrescrito
ENV Define variável de ambiente

⚠ Erros comuns no Dockerfile

Não uses :latest como tag base — as imagens mudam e quebram a reprodutibilidade. Especifica sempre versões concretas como node:20-slim. Coloca COPY package*.json antes de COPY . . para aproveitar o cache de camadas — se o código muda mas as dependências não, o npm ci não precisa de repetir.

7. Erros Comuns e Checklist

Estes são os erros mais frequentes ao trabalhar com Docker e como os evitar.

Erro Causa Solução
permission denied Utilador fora do grupo docker sudo usermod -aG docker $USER
port is already allocated Outro processo usa o porto Trocar porto ou parar o contentor conflituante
no space left on device Imagens e contentores acumulados docker system prune -a
Contentor para imediatamente Processo principal terminou Verificar docker logs e o CMD
Dados perdidos após reinício Sem volume montado Usar -v para persistir dados
Contentores não se vêem Rede bridge predefinida Criar rede bridge personalizada

Comandos úteis de limpeza e diagnóstico:

# Remover todos os contentores parados
docker container prune

# Remover imagens não usadas
docker image prune -a

# Remover volumes não usados (cuidado com dados!)
docker volume prune

# Limpeza total — remove tudo não em uso
docker system prune -a --volumes

# Ver uso de espaço do Docker
docker system df

Checklist de boas práticas:

  • Usar tags de versão específicas (nginx:1.27) em vez de :latest
  • Usar volumes nomeados para dados persistentes em produção
  • Criar redes bridge personalizadas para DNS automático entre contentores
  • Definir --restart unless-stopped em contentores de produção
  • Limpar regularmente com docker system prune
  • Usar .dockerignore para excluir ficheiros desnecessários do contexto de build
  • Combinar instruções RUN com && para reduzir camadas
  • Nunca guardar segredos no Dockerfile — usar variáveis de ambiente ou Docker Secrets

✓ Resumo

O Docker segue o fluxo: imagem (template) → contentor (execução) → volume (persistência) → rede (comunicação). O Dockerfile descreve como construir a imagem. Com estes cinco conceitos dominas 90% do uso diário do Docker no Linux.

Artigos Relacionados