Docker no Ubuntu: Guia Completo de Instalação e Uso
Docker é uma plataforma de contentorização de código aberto que empacota aplicações e as suas dependências em unidades isoladas e portáteis chamadas contentores. Ao contrário das máquinas virtuais, os contentores partilham o kernel do SO anfitrião, tornando-os significativamente mais leves, mais rápidos a iniciar e mais eficientes em termos de recursos — uma distinção crítica para quem executa cargas de trabalho num ambiente de VPS Hosting onde os recursos de computação afetam diretamente o custo e o desempenho.
Este guia abrange o processo completo de instalação do Docker no Ubuntu 20.04, 22.04 e 24.04 LTS, incluindo proteção pós-instalação, fluxos de trabalho do Docker Compose e padrões de comandos relevantes para produção que a maioria dos tutoriais omite.
Pré-requisitos e Requisitos do Sistema
Antes de executar um único comando, verifique o seguinte:
- Versão do Ubuntu: 20.04 LTS (Focal), 22.04 LTS (Jammy) ou 24.04 LTS (Noble). O comando `lsb_release -cs` utilizado durante a configuração do repositório detetará automaticamente o seu codinome.
- Arquitetura: `amd64`, `arm64` ou `armhf` são todas suportadas pelo repositório oficial do Docker.
- Versão do kernel: O Docker requer o kernel Linux 3.10 ou superior. Execute `uname -r` para confirmar.
- Privilégios de utilizador: `sudo` ou acesso root é obrigatório para instalação e gestão do daemon.
- Espaço em disco: No mínimo 2 GB livres na partição que aloja `/var/lib/docker`, que é onde o Docker armazena imagens, contentores, volumes e cache de compilação. Em sistemas de produção, monte este diretório numa partição ou volume dedicado.
Passo crítico de pré-instalação: Se instalou anteriormente o Docker a partir do repositório `apt` predefinido do Ubuntu (o pacote `docker.io`), remova-o primeiro para evitar conflitos com os pacotes oficiais do Docker CE:
“`bash
sudo apt remove docker docker-engine docker.io containerd runc
“`
Passo 1: Atualizar os Pacotes do Sistema
Atualize o índice de pacotes e os pacotes instalados para as suas versões mais recentes antes de adicionar qualquer novo repositório:
“`bash
sudo apt update
sudo apt upgrade -y
“`
Isto garante que o resolvedor de dependências do `apt` funciona com metadados de pacotes atuais e que as bibliotecas do sistema base não estão desatualizadas — uma fonte comum de erros de execução subtis com aplicações contentorizadas.
Passo 2: Instalar o Docker Engine a partir do Repositório Oficial
Os repositórios `apt` predefinidos do Ubuntu incluem um pacote chamado `docker.io`, que é mantido pela Canonical e normalmente fica várias versões atrás do lançamento oficial do Docker. Para uso em produção, instale sempre a partir do repositório próprio do Docker.
2.1 Instalar Dependências de Transporte e Verificação
“`bash
sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release -y
“`
Porquê `gnupg`? A partir do Ubuntu 22.04, o `gpg` nem sempre está presente por predefinição. Incluí-lo explicitamente evita que a importação da chave GPG falhe silenciosamente.
2.2 Adicionar a Chave GPG Oficial do Docker
“`bash
sudo install -m 0755 -d /usr/share/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg –dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
“`
O passo `chmod a+r` é frequentemente ignorado nos tutoriais, mas é necessário em sistemas onde o `apt` é executado num contexto de utilizador restrito — sem ele, o gestor de pacotes não consegue ler o chaveiro e apresentará um erro `NO_PUBKEY` durante o `apt update`.
2.3 Adicionar o Repositório Estável do Docker
“`bash
echo
"deb [arch=$(dpkg –print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]
https://download.docker.com/linux/ubuntu
$(lsb_release -cs) stable" |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
“`
A substituição `arch=$(dpkg –print-architecture)` é essencial em servidores baseados em ARM. Codificar `amd64` aqui é um erro comum que causa falhas silenciosas na resolução de pacotes em instâncias ARM.
2.4 Instalar o Docker Engine, CLI e Plugins
“`bash
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
“`
Descrição dos pacotes:
| Pacote | Função |
|---|
| — | — |
|---|
| `docker-ce` | Daemon do Docker Engine (`dockerd`) |
|---|
| `docker-ce-cli` | CLI do cliente (comando `docker`) |
|---|
| `containerd.io` | Runtime de contentores de baixo nível (compatível com OCI) |
|---|
| `docker-buildx-plugin` | Capacidades de compilação alargadas (multiplataforma, BuildKit) |
|---|
| `docker-compose-plugin` | Compose V2 integrado como plugin CLI do Docker |
|---|
Nota sobre `containerd.io`: Este não é o mesmo que o pacote `containerd` no repositório predefinido do Ubuntu. O `containerd.io` do Docker é uma versão específica e testada do runtime containerd. Misturar os dois é uma causa conhecida de falhas na inicialização do daemon.
Passo 3: Verificar a Instalação
Confirme que o daemon está ativo e configurado para iniciar no arranque:
“`bash
sudo systemctl status docker
sudo systemctl enable docker
“`
Verifique a versão instalada:
“`bash
sudo docker –version
sudo docker info
“`
`docker info` é mais informativo do que `–version` isoladamente — revela o driver de armazenamento em uso (normalmente `overlay2`), o driver cgroup (`systemd` vs `cgroupfs`) e o número de CPUs e memória a que o Docker pode aceder.
Execute o teste de verificação canónico:
“`bash
sudo docker run hello-world
“`
Uma execução bem-sucedida imprime uma mensagem “Hello from Docker!” e confirma que o daemon do Docker, a extração de imagens do Docker Hub e a execução de contentores estão todos a funcionar corretamente.
Passo 4: Configurar o Docker para Acesso Sem Root
Por predefinição, o socket do Docker (`/var/run/docker.sock`) é propriedade do `root` e do grupo `docker`. Qualquer utilizador que não esteja no grupo `docker` deve usar `sudo` para cada comando Docker.
“`bash
sudo usermod -aG docker $USER
“`
Aplique a associação ao grupo sem terminar sessão:
“`bash
newgrp docker
“`
Verifique:
“`bash
docker run hello-world
“`
Aviso de segurança: A associação ao grupo `docker` é efetivamente equivalente a `sudo` sem palavra-passe. Um utilizador no grupo `docker` pode facilmente montar o sistema de ficheiros do anfitrião num contentor e escapar a todos os controlos de acesso ao nível do sistema de ficheiros. Em sistemas multi-inquilino ou servidores partilhados, considere usar o Docker rootless:
“`bash
dockerd-rootless-setuptool.sh install
“`
O modo rootless executa o daemon do Docker e os contentores sob um espaço de nomes de utilizador sem privilégios, reduzindo drasticamente a superfície de ataque. É a configuração recomendada para qualquer ambiente onde múltiplos utilizadores partilham o mesmo anfitrião.
Passo 5: Referência de Comandos Essenciais do Docker
Gestão de Imagens
“`bash
Pull a specific image version from Docker Hub
docker pull nginx:1.27-alpine
List locally cached images
docker images
Remove a specific image
docker rmi nginx:1.27-alpine
Remove all dangling (untagged) images to reclaim disk space
docker image prune
Remove all unused images (not just dangling)
docker image prune -a
“`
Dica de produção: Utilize sempre tags com versão (ex.: `nginx:1.27-alpine`) em vez de `latest` em qualquer fluxo de trabalho automatizado ou de produção. A tag `latest` é mutável — pode apontar para uma imagem diferente após um push no registo, comprometendo a reprodutibilidade.
Ciclo de Vida dos Contentores
“`bash
Run a container interactively with a pseudo-TTY
docker run -it ubuntu:22.04 /bin/bash
Run a container in detached mode with port mapping and a name
docker run -d -p 8080:80 –name my-nginx nginx:1.27-alpine
List running containers
docker ps
List all containers (including stopped)
docker ps -a
Stop a running container gracefully (SIGTERM, then SIGKILL after timeout)
docker stop my-nginx
Start a stopped container
docker start my-nginx
Remove a stopped container
docker rm my-nginx
Force-remove a running container (sends SIGKILL immediately)
docker rm -f my-nginx
View real-time logs
docker logs -f my-nginx
Execute a command inside a running container
docker exec -it my-nginx /bin/sh
“`
Inspeção de Recursos e do Sistema
“`bash
Display real-time resource usage statistics
docker stats
Inspect detailed container metadata (JSON)
docker inspect my-nginx
Display disk usage by Docker objects
docker system df
Remove all stopped containers, unused networks, dangling images, and build cache
docker system prune
“`
`docker system prune` é um dos comandos de manutenção mais importantes para servidores de longa duração. Sem limpeza periódica, a cache de compilação do Docker e os contentores parados podem consumir dezenas de gigabytes num anfitrião de desenvolvimento ou CI ativo.
Passo 6: Docker Compose — Orquestração de Aplicações Multi-Contentor
O Docker Compose V2 (o `docker-compose-plugin` instalado anteriormente) é invocado como `docker compose` (com espaço), não o legado `docker-compose` (com hífen). Ambas as sintaxes funcionam se tiver o plugin instalado, mas V2 é o padrão atual.
6.1 Compreender a Estrutura do Ficheiro Compose
Crie um diretório de projeto e um ficheiro `compose.yml` (o nome de ficheiro preferido no Compose V2; `docker-compose.yml` continua suportado para compatibilidade retroativa):
“`bash
mkdir ~/my-web-app && cd ~/my-web-app
nano compose.yml
“`
Um exemplo realista para produção com Nginx e um serviço de backend:
“`yaml
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html:ro
depends_on:
- app
restart: unless-stopped
app:
image: node:20-alpine
working_dir: /usr/src/app
volumes:
- ./app:/usr/src/app
command: node server.js
environment:
- NODE_ENV=production
restart: unless-stopped
“`
Explicação das diretivas chave do Compose:
- `restart: unless-stopped` — O contentor reinicia automaticamente após uma falha ou reinício do sistema, a menos que tenha sido explicitamente parado pelo operador. Esta é a política correta para serviços de longa duração; `always` reiniciará mesmo contentores intencionalmente parados.
- `depends_on` — Controla a ordem de arranque, mas não aguarda que o serviço esteja *pronto* (ex.: uma base de dados a aceitar ligações). Para controlo de prontidão, use `healthcheck` combinado com `condition: service_healthy`.
- `volumes` com `:ro` — Montar ficheiros de configuração como só de leitura impede que um processo de contentor comprometido modifique a sua própria configuração.
6.2 Comandos do Fluxo de Trabalho do Compose
“`bash
Start all services in detached mode
docker compose up -d
View logs for all services
docker compose logs -f
View logs for a specific service
docker compose logs -f web
List running Compose services
docker compose ps
Scale a specific service to multiple replicas
docker compose up -d –scale app=3
Stop services without removing containers
docker compose stop
Stop and remove containers, networks, and volumes
docker compose down –volumes
Rebuild images before starting (useful after code changes)
docker compose up -d –build
“`
6.3 Verificar o Serviço em Execução
“`bash
curl -I http://localhost:8080
“`
Uma resposta `200 OK` confirma que o Nginx está a servir corretamente. Para serviços em execução numa instância remota de VPS Hosting, substitua `localhost` pelo endereço IP público do seu servidor e certifique-se de que a porta relevante está aberta na sua firewall (`ufw allow 8080/tcp`).
Passo 7: Fundamentos de Rede do Docker
Compreender o modelo de rede do Docker é essencial para construir aplicações multi-contentor que comunicam de forma segura.
Drivers de rede predefinidos:
| Driver | Caso de Uso |
|---|
| — | — |
|---|
| `bridge` | Predefinição para contentores autónomos; espaço de nomes de rede isolado no anfitrião |
|---|
| `host` | O contentor partilha a pilha de rede do anfitrião; desempenho máximo, zero isolamento |
|---|
| `none` | Sem acesso à rede; útil para processamento em lote ou cargas de trabalho sensíveis à segurança |
|---|
| `overlay` | Rede multi-anfitrião para clusters Docker Swarm |
|---|
| `macvlan` | Atribui um endereço MAC ao contentor; aparece como um dispositivo físico na rede |
|---|
Criar e utilizar uma rede bridge personalizada:
“`bash
Create an isolated network
docker network create my-app-network
Run containers attached to the custom network
docker run -d –name db –network my-app-network postgres:16-alpine
docker run -d –name api –network my-app-network my-api-image
Containers on the same custom bridge network can resolve each other by name
Inside 'api', you can connect to 'db' using the hostname 'db'
“`
As redes bridge personalizadas fornecem resolução DNS automática entre contentores pelo nome do contentor. A rede `bridge` predefinida não o faz — esta é uma distinção crítica que causa falhas de ligação quando os programadores assumem que os contentores na rede predefinida se podem alcançar mutuamente pelo nome.
Passo 8: Dados Persistentes com Volumes Docker
Os contentores são efémeros por design. Quaisquer dados escritos no sistema de ficheiros de um contentor são perdidos quando o contentor é removido. Para armazenamento persistente, use volumes ou bind mounts.
“`bash
Create a named volume
docker volume create pgdata
Use the volume with a container
docker run -d
–name postgres-db
-e POSTGRES_PASSWORD=securepassword
-v pgdata:/var/lib/postgresql/data
postgres:16-alpine
List volumes
docker volume ls
Inspect a volume (shows mount point on host)
docker volume inspect pgdata
Remove unused volumes
docker volume prune
“`
Volumes vs. bind mounts:
| Funcionalidade | Volume Nomeado | Bind Mount |
|---|
| — | — | — |
|---|
| Gerido pelo Docker | Sim | Não |
|---|
| Caminho do anfitrião necessário | Não | Sim |
|---|
| Portátil entre anfitriões | Sim (com drivers de volume) | Não |
|---|
| Melhor para | Dados de base de dados, estado da aplicação | Código de desenvolvimento, ficheiros de configuração |
|---|
| Mecanismo de cópia de segurança | `docker run –volumes-from` | Ferramentas padrão do sistema de ficheiros |
|---|
Passo 9: Manter o Docker Atualizado
O repositório oficial do Docker trata as atualizações através do mecanismo `apt` padrão:
“`bash
sudo apt update
sudo apt upgrade -y
“`
Para atualizar apenas os pacotes relacionados com o Docker sem atualizar o sistema inteiro:
“`bash
sudo apt install –only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
“`
Consulte as notas de lançamento do Docker antes de atualizações de versões principais, particularmente para alterações ao driver de armazenamento, tratamento da versão cgroup ou versões de API descontinuadas que possam afetar os ficheiros `compose.yml` existentes.
Docker vs. Abordagens Alternativas de Contentorização
| Funcionalidade | Docker Engine | Podman | LXC/LXD | containerd (autónomo) |
|---|
| — | — | — | — | — |
|---|
| Arquitetura do daemon | Daemon centralizado | Sem daemon | Baseado em daemon | Baseado em daemon |
|---|
| Suporte rootless | Sim (v20+) | Nativo | Limitado | Sim |
|---|
| Suporte Docker Compose | Nativo | Via `podman-compose` | Não | Não |
|---|
| Conformidade OCI | Sim | Sim | Não (formato LXC) | Sim |
|---|
| Integração Kubernetes | Via shim CRI-dockerd | CRI nativo | Não | CRI nativo |
|---|
| Suporte Windows/macOS | Docker Desktop | Limitado | Não | Não |
|---|
| Melhor para | Desenvolvimento geral e produção | Focado em segurança, rootless | Contentores de sistema, VMs | Nós Kubernetes |
|---|
Para equipas que executam cargas de trabalho contentorizadas em escala em bare metal, um ambiente de Servidores Dedicados dá-lhe controlo total sobre os parâmetros do kernel, o agendamento de I/O de armazenamento e a configuração de rede — todos os quais afetam diretamente a densidade e o desempenho dos contentores.
Lista de Verificação de Proteção para Produção
Antes de executar o Docker num ambiente de produção, resolva o seguinte:
Configuração do daemon (`/etc/docker/daemon.json`):
“`json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"userns-remap": "default",
"live-restore": true,
"no-new-privileges": true
}
“`
- `log-opts` com `max-size` e `max-file`: Sem rotação de registos, os ficheiros de registo JSON do Docker encherão o seu disco. Esta é uma das causas mais comuns de interrupções inesperadas do servidor em anfitriões contentorizados.
- `userns-remap: "default"`: Ativa o remapeamento de espaço de nomes de utilizador, para que o root do contentor (UID 0) mapeie para um UID sem privilégios no anfitrião.
- `live-restore: true`: Permite que os contentores continuem a funcionar se o daemon do Docker falhar ou for reiniciado durante uma atualização — crítico para manutenção sem tempo de inatividade.
- `no-new-privileges: true`: Impede que os processos do contentor obtenham privilégios adicionais através de binários `setuid` ou `setgid`.
Considerações sobre rede e firewall:
O Docker manipula o `iptables` diretamente e ignora as regras do `ufw` por predefinição. Um contentor com uma porta publicada (`-p 8080:80`) será acessível a partir da internet mesmo que `ufw deny 8080` esteja definido. Para aplicar regras do `ufw` sobre a manipulação do `iptables` pelo Docker, adicione `"iptables": false` ao `daemon.json` e gira o encaminhamento manualmente, ou use as redes `–network host` do Docker com regras `ufw` explícitas.
Para projetos que requerem terminação HTTPS, combine a sua aplicação contentorizada com um proxy reverso devidamente configurado (Nginx ou Traefik) e um certificado válido. Os Certificados SSL são um pré-requisito para qualquer serviço web de produção a executar por detrás de uma pilha contentorizada.
Se a sua carga de trabalho envolve inferência de machine learning, serviço de modelos ou processamento de dados acelerado por GPU dentro de contentores, o NVIDIA Container Toolkit integra-se diretamente com o Docker Engine. O GPU Hosting fornece o hardware subjacente necessário para estas cargas de trabalho.
Para equipas que gerem múltiplos projetos com gestão de contentores baseada na web, o VPS com cPanel oferece um ambiente de painel de controlo gerido que pode complementar as implementações baseadas em Docker para pilhas de aplicações mais simples.
Principais Conclusões Técnicas e Matriz de Decisão
Quando usar Docker num VPS vs. num servidor dedicado:
- Use um VPS para ambientes de desenvolvimento, staging e cargas de trabalho de produção de tráfego baixo a médio onde a densidade de contentores é de 10 a 50 contentores.
- Use um servidor dedicado quando a densidade de contentores exceder 50 instâncias, quando precisar de desempenho de I/O previsível (sem efeito de vizinho barulhento) ou quando for necessário ajuste de parâmetros do kernel (`sysctl`) para a sua carga de trabalho.
Lista de verificação operacional antes de entrar em produção:
- Configurar a rotação de registos em `daemon.json` (`max-size`, `max-file`)
- Ativar `live-restore` para sobreviver a reinícios do daemon sem tempo de inatividade dos contentores
- Usar volumes nomeados, não bind mounts, para dados de serviços com estado
- Fixar versões de imagens em todos os ficheiros `compose.yml` — nunca usar `latest` em produção
- Ativar `userns-remap` ou executar Docker rootless em anfitriões multi-inquilino
- Auditar as regras `iptables` após a instalação do Docker para confirmar que a política de firewall não é contornada
- Definir `restart: unless-stopped` em todos os serviços de longa duração
- Executar `docker system prune` numa tarefa cron agendada para evitar o esgotamento do disco
- Usar redes bridge personalizadas para toda a comunicação entre contentores — nunca depender da bridge predefinida para descoberta de serviços
FAQ
O Docker no Ubuntu usa `systemd` ou `cgroupfs` como driver cgroup por predefinição?
Desde o Docker Engine 20.10, o driver cgroup predefinido em sistemas baseados em `systemd` (incluindo todas as versões modernas do Ubuntu LTS) é `systemd`. Isto alinha-se com os requisitos do Kubernetes e evita a instabilidade causada pela execução simultânea de dois gestores cgroup. Pode verificar com `docker info | grep -i cgroup`.
Qual é a diferença entre `docker compose down` e `docker compose stop`?
`docker compose stop` para os contentores em execução, mas preserva-os a eles e às suas redes associadas. `docker compose down` para os contentores e depois remove-os juntamente com as redes criadas pelo Compose. Adicionar `–volumes` ao `down` também remove os volumes nomeados definidos no ficheiro Compose — use este sinalizador com cautela em produção, pois elimina permanentemente os dados persistentes.
Porque é que o Docker ignora as regras de firewall do `ufw` no Ubuntu?
O Docker insere as suas próprias regras `iptables` nas cadeias `DOCKER` e `DOCKER-USER`, que são avaliadas antes das regras da cadeia `INPUT` do `ufw`. Isto significa que uma porta publicada com `-p` é acessível a partir da internet independentemente da política do `ufw`. A mitigação correta é adicionar regras diretamente à cadeia `DOCKER-USER`, ou vincular as portas publicadas a uma interface específica (ex.: `-p 127.0.0.1:8080:80`) quando o acesso externo não é necessário.
Como limito a CPU e a memória que um contentor Docker pode consumir?
Use sinalizadores de restrição de recursos em tempo de execução: `docker run –memory="512m" –cpus="1.5" my-image`. No Compose, defina-os sob a chave `deploy.resources` (Compose V2) ou as chaves de nível superior `mem_limit` e `cpus`. Sem limites, um único contentor descontrolado pode esgotar os recursos do anfitrião e derrubar todos os outros contentores no mesmo anfitrião.
Posso executar Docker dentro de um contentor Docker (Docker-in-Docker)?
Sim, mas é fortemente desaconselhado para uso em produção. O padrão comum para pipelines de CI é montar o socket Docker do anfitrião (`-v /var/run/docker.sock:/var/run/docker.sock`) no contentor de CI, o que dá ao contentor controlo total sobre o daemon Docker do anfitrião — um risco de segurança significativo. Uma alternativa mais segura é usar o sinalizador `–allow security.insecure` do BuildKit ou ferramentas criadas especificamente para este fim, como Kaniko ou Buildah, que constroem imagens OCI sem necessitar de um daemon Docker.
