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

Grafana: Dashboards e Alertas que Fazem Sentido
Grafana: Dashboards e Alertas que Fazem Sentido

Dashboards que respondem perguntas em vez de acumular gráficos…

Trabalhando em Equipe com Git Flow
Trabalhando em Equipe com Git Flow

Git Flow como modelo de organização de branches: os papéis de main e develop…

O Que é um Container e Por Que Isso Mudou Tudo
O Que é um Container e Por Que Isso Mudou Tudo

O que é um container e por que ele mudou a entrega de software: o problema…