Instalando e Rodando Seus Primeiros Containers

[93] Instalando e Rodando Seus Primeiros Containers

Docker na prática: instalação pelo repositório oficial, uso sem sudo, primeiros containers interativos, o ciclo de vida completo com run, ps, start, stop e rm, publicação de portas com -p e inspeção de containers em execução usando logs, stats, inspect e exec.
DevOps

11 min de leitura

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

Prática

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.

Comentários

Mais em DevOps

Geração de Infraestrutura com IA
Geração de Infraestrutura com IA

Geração de Terraform e manifestos Kubernetes por LLM com validação no laço: os…

Terraform + Ansible: Do Provisionamento à Configuração
Terraform + Ansible: Do Provisionamento à Configuração

A fronteira entre provisionar e configurar, e como cruzá-la sem trabalho…

Git na Prática: Commits, Branches e Merges sem Medo
Git na Prática: Commits, Branches e Merges sem Medo

Git no fluxo real de trabalho: os três estados de um arquivo, o ciclo…