O artigo O Que é um Container e Por Que Isso Mudou Tudo explicou o que são containers e por que eles existem. Este artigo é inteiramente prático: instalar o Docker, entender os primeiros comandos e desenvolver fluência no ciclo de vida de um container — criar, inspecionar, parar, remover.
Ao final deste artigo, rodar um servidor web completo em um container será algo tão natural quanto abrir um arquivo de texto.
Instalando o Docker
No Ubuntu e distribuições baseadas em Debian:
A forma recomendada é usar o repositório oficial do Docker, não o pacote do apt padrão — que frequentemente está desatualizado:
# Remove versões antigas, se existirem
sudo apt remove docker docker-engine docker.io containerd runc
# Instala dependências
sudo apt update
sudo apt install ca-certificates curl gnupg
# Adiciona a chave GPG oficial do Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Adiciona o repositório oficial
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Instala o Docker Engine
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Permitindo rodar Docker sem sudo:
Por padrão, o Docker requer privilégios de root. Para usar sem sudo, adiciona-se o usuário ao grupo docker:
sudo usermod -aG docker $USER
newgrp docker
É necessário fazer logout e login novamente para que a mudança de grupo tenha efeito completo.
Verificando a instalação:
docker version
docker info
O docker version mostra as versões do cliente e do daemon. O docker info exibe informações detalhadas sobre o ambiente — número de containers, imagens, configurações de rede e storage driver.
O Primeiro Container
docker run hello-world
O que acontece nessa sequência quando esse comando é executado:
O Docker verifica se a imagem hello-world existe localmente. Não existe na primeira vez, então ele a baixa do Docker Hub. Em seguida, cria um container a partir dessa imagem, executa o processo definido nela — que imprime uma mensagem explicativa — e encerra o container logo após.
A saída confirma que a instalação está funcionando e o Docker consegue se comunicar com o Docker Hub.
Rodando Containers Interativos
Para explorar um container Ubuntu como se fosse uma máquina Linux completa:
docker run -it ubuntu:22.04 bash
O flag -i mantém o stdin aberto (interativo). O flag -t aloca um pseudo-terminal. Juntos, permitem que se use o container como um terminal interativo.
Dentro do container, é possível executar qualquer comando Linux:
cat /etc/os-release
apt update && apt install -y curl
curl --version
exit
Ao sair com exit, o container para. Ele não é removido automaticamente — apenas para.
O Ciclo de Vida de um Container
# Lista containers em execução
docker ps
# Lista todos os containers, incluindo os parados
docker ps -a
# Inicia um container parado
docker start <container_id ou nome>
# Para um container em execução
docker stop <container_id ou nome>
# Remove um container parado
docker rm <container_id ou nome>
# Para e remove em um único comando
docker rm -f <container_id ou nome>
Os containers podem ser referenciados pelo ID completo, pelos primeiros caracteres do ID (o suficiente para ser único) ou pelo nome. Se nenhum nome for especificado no docker run, o Docker gera um nome aleatório — sempre composto por um adjetivo e o nome de um cientista famoso, como suspicious_turing ou friendly_curie.
Para nomear um container explicitamente:
docker run --name meu-ubuntu -it ubuntu:22.04 bash
Rodando um Servidor Web
Um dos casos de uso mais comuns: rodar um Nginx em um container e acessá-lo pelo navegador.
docker run -d -p 8080:80 --name servidor-web nginx:alpine
Três flags importantes aqui:
-d (detached) — executa o container em segundo plano, liberando o terminal.
-p 8080:80 — mapeia a porta 8080 do host para a porta 80 do container. O formato é sempre porta-do-host:porta-do-container. Qualquer requisição que chegar na porta 8080 da máquina local será redirecionada para a porta 80 dentro do container.
--name servidor-web — nomeia o container para facilitar referências futuras.
Verificando se está funcionando:
curl http://localhost:8080
Ou simplesmente abrindo http://localhost:8080 no navegador. A página de boas-vindas do Nginx confirma que o container está respondendo.
Inspecionando Containers em Execução
# Logs do container — equivalente ao tail do arquivo de log
docker logs servidor-web
# Logs em tempo real
docker logs -f servidor-web
# Estatísticas de uso de recursos em tempo real
docker stats servidor-web
# Informações detalhadas em JSON
docker inspect servidor-web
# Executa um comando dentro de um container em execução
docker exec -it servidor-web sh
O docker exec é extremamente útil para depuração: permite entrar em um container que já está rodando sem interrompê-lo. Isso é diferente do docker run, que cria um novo container — o exec entra no container existente.
Dentro do container Nginx, é possível inspecionar os arquivos de configuração:
cat /etc/nginx/nginx.conf
ls /usr/share/nginx/html
exit
Gerenciando Imagens
# Lista imagens locais
docker images
# Baixa uma imagem sem criar um container
docker pull postgres:16
# Remove uma imagem
docker rmi nginx:alpine
# Remove imagens, containers parados e cache não utilizados
docker system prune
# Remove tudo, incluindo imagens não utilizadas
docker system prune -a
O docker system prune é útil para liberar espaço em disco, especialmente em máquinas de desenvolvimento onde muitas imagens se acumulam. Em servidores de produção, deve ser usado com cautela — imagens que parecem não utilizadas podem ser necessárias para reiniciar containers.
Passando Variáveis de Ambiente
A maioria das imagens oficiais é configurada via variáveis de ambiente. O PostgreSQL, por exemplo:
docker run -d \
--name banco-dados \
-e POSTGRES_USER=devops \
-e POSTGRES_PASSWORD=senha123 \
-e POSTGRES_DB=minha_app \
-p 5432:5432 \
postgres:16
O flag -e define variáveis de ambiente dentro do container. Para verificar que o banco subiu corretamente:
docker logs banco-dados
# Conectando ao banco de dentro do container
docker exec -it banco-dados psql -U devops -d minha_app
Um Fluxo Completo de Trabalho
Reunindo os comandos em um cenário realista — subir um ambiente de desenvolvimento com Nginx e PostgreSQL, inspecionar, e depois limpar tudo:
# Sobe os serviços
docker run -d --name web -p 8080:80 nginx:alpine
docker run -d --name db -e POSTGRES_PASSWORD=senha123 -p 5432:5432 postgres:16
# Verifica o que está rodando
docker ps
# Inspeciona logs
docker logs web
docker logs db
# Testa o Nginx
curl http://localhost:8080
# Para tudo
docker stop web db
# Remove os containers
docker rm web db
# Verifica que não sobrou nada
docker ps -a
O Que Vem a Seguir
Até aqui foram usadas imagens prontas do Docker Hub. No próximo artigo será abordado o Dockerfile — o arquivo que descreve como construir uma imagem customizada para uma aplicação própria. É o passo que transforma o Docker de uma ferramenta de consumo em uma ferramenta de construção.
Referências para Aprofundamento
Documentação oficial
- Install Docker Engine on Ubuntu — docs.docker.com — Guia oficial de instalação do Docker no Ubuntu, sempre atualizado com o método recomendado.
- Docker CLI Reference — docs.docker.com — Referência completa de todos os comandos do Docker CLI, com exemplos e opções.
Prática
- Play with Docker — Ambiente Docker gratuito no navegador. Permite praticar todos os comandos deste artigo sem instalar nada localmente.
- Docker Getting Started Tutorial — docs.docker.com — Tutorial oficial progressivo do Docker. Os primeiros três passos cobrem exatamente o conteúdo deste artigo.
Leitura complementar
- Docker Hub — hub.docker.com — O registro público de imagens. Vale explorar as imagens oficiais do Nginx, PostgreSQL e Ubuntu para entender como são documentadas e quais variáveis de ambiente aceitam.
Exercícios
Exercício 1
O que -i e -t fazem individualmente em docker run -it ubuntu:22.04 bash? E por que combinar -it com -d não faz sentido?
Ver resposta
✓ Resposta: O -i mantém o stdin aberto, permitindo que você digite e que o que for digitado chegue ao processo dentro do container. O -t aloca um pseudo-terminal, que é o que entrega prompt, formatação e edição de linha. Sozinho, o -i funciona mas sem prompt algum; sozinho, o -t dá a aparência de terminal sem receber o que você digita. Juntos formam um shell utilizável. Já o -d faz o oposto: desanexa o container e devolve o terminal imediatamente. Combinado a um shell interativo, o container sobe em segundo plano com um bash sem ninguém do outro lado — e costuma encerrar sozinho, por não ter entrada.
Exercício 2
Ao digitar exit dentro do container Ubuntu, o que acontece com ele? Por que docker ps não o mostra, mas docker ps -a sim?
Ver resposta
✓ Resposta: Ele para, mas não é removido: continua existindo em estado exited, com seu sistema de arquivos intacto — inclusive o curl que você instalou lá dentro. O docker ps lista apenas containers em execução, enquanto o docker ps -a lista todos, parados incluídos. É exatamente por isso que containers se acumulam sem que ninguém perceba: cada docker run sem --rm deixa um resto ocupando disco. Para retomar de onde parou, use docker start seguido de docker exec; para descartar de vez, docker rm.
Exercício 3
Em -p 8080:80, qual número é a porta do host e qual é a do container? O que aconteceria se o comando fosse -p 80:8080 com a imagem nginx:alpine?
Ver resposta
✓ Resposta: O formato é sempre porta-do-host:porta-do-container. Logo, 8080 é a porta da sua máquina e 80 é a porta dentro do container, onde o Nginx escuta. Invertendo para -p 80:8080, o Docker encaminharia a porta 80 do host para a porta 8080 do container — onde não há nada escutando, já que o Nginx da imagem continua na 80. O detalhe cruel é que o container sobe normalmente e aparece como Up no docker ps: o erro não se manifesta em lugar nenhum além da conexão recusada no navegador.
Exercício 4
Qual a diferença entre docker run e docker exec? Por que o exec é a ferramenta certa para depurar um problema?
Ver resposta
✓ Resposta: O docker run cria um container novo a partir de uma imagem. O docker exec executa um comando dentro de um container que já está rodando. A diferença decide a investigação: quem roda docker run de novo para "entrar e ver o que houve" recebe um container limpo, sem nenhum vestígio do estado que causou a falha — os arquivos gerados, os logs, a configuração alterada em tempo de execução, nada disso está lá. O docker exec -it servidor-web sh entra no processo vivo, com tudo exatamente como está, e sem interrompê-lo.
Exercício 5
Os três comandos abaixo respondem a perguntas diferentes sobre um container. Qual pergunta cada um responde?
docker logs -f servidor-web
docker stats servidor-web
docker inspect servidor-web
Ver resposta
✓ Resposta: O logs -f responde "o que a aplicação está dizendo?", acompanhando a saída em tempo real, como um tail -f. O stats responde "quanto ela está consumindo?", mostrando CPU, memória, rede e I/O ao vivo — é o que distingue uma aplicação lenta de uma aplicação sufocada por limite de recurso. O inspect responde "como esse container foi montado?", devolvendo em JSON as portas mapeadas, volumes, variáveis de ambiente, rede e comando de entrada. Boa parte dos problemas de container é, na verdade, um desses três em desacordo com o que se imaginava.
Exercício 6
Por que instalar o Docker pelo repositório oficial em vez do pacote padrão do apt? E adicionar o usuário ao grupo docker traz alguma implicação de segurança?
Ver resposta
✓ Resposta: O pacote do apt padrão costuma estar defasado, o que significa recursos ausentes e correções de segurança não aplicadas; o repositório oficial entrega o Docker Engine atual e já traz os plugins buildx e compose na mesma instalação. Quanto ao grupo: o daemon roda como root e o socket do Docker pertence ao grupo docker, então pertencer a ele dispensa o sudo. A implicação de segurança é real e costuma ser subestimada — quem está nesse grupo pode subir um container montando o sistema de arquivos do host e, a partir dali, obter acesso equivalente a root. Não é uma permissão intermediária entre usuário comum e administrador: na prática, é a mesma coisa que conceder sudo irrestrito.