Docker para Desenvolvedores: Imagens, Contêineres e Compose
Você provavelmente já ouviu falar do Docker e de como ele pode simplificar seu fluxo de trabalho de desenvolvimento. Mas se você é novo no mundo dos contêineres, a terminologia e os comandos podem ser avassaladores. Este guia corta o ruído e oferece uma compreensão prática das imagens, contêineres e Compose do Docker — para que você possa começar a usá-los de forma eficaz hoje mesmo.
O que é Docker e por que você deveria se importar?
Docker é uma plataforma que empacota sua aplicação e suas dependências em uma unidade padronizada chamada contêiner. Contêineres são leves, portáveis e isolados entre si, o que significa que você pode executar sua aplicação de forma consistente no seu laptop, na máquina de um colega ou em um servidor de produção.
O principal benefício? "Funciona na minha máquina" se torna coisa do passado. O Docker garante que o ambiente em que seu código é executado seja idêntico em todos os lugares.
Imagens vs. Contêineres: Os Blocos de Construção
Antes de começar, você precisa entender a diferença entre uma imagem e um contêiner.
- Imagem: Um modelo somente leitura que contém o código da sua aplicação, runtime, bibliotecas e dependências. Pense nela como um snapshot ou um projeto.
- Contêiner: Uma instância executável de uma imagem. Você pode iniciar, parar, mover e excluir contêineres. Vários contêineres podem ser executados a partir da mesma imagem.
Resumindo: a imagem é a receita, e o contêiner é o prato que você prepara a partir dela.
Comandos Essenciais do Docker para Desenvolvedores
Aqui estão os comandos que você usará com mais frequência:
docker pull <image>– Baixa uma imagem de um registro como o Docker Hub.docker images– Lista todas as imagens locais.docker run <image>– Cria e inicia um contêiner a partir de uma imagem.docker ps– Lista os contêineres em execução. Adicione-apara ver todos os contêineres, incluindo os parados.docker stop <container>– Para um contêiner em execução de forma graciosa.docker rm <container>– Remove um contêiner parado.docker rmi <image>– Remove uma imagem.docker exec -it <container> bash– Abre um shell interativo dentro de um contêiner em execução.
Por exemplo, para executar um servidor web Nginx simples:
docker run -d -p 8080:80 nginx
Isso baixa a imagem nginx (se ainda não estiver presente), inicia um contêiner em modo detached (-d) e mapeia a porta 8080 do seu host para a porta 80 do contêiner (-p 8080:80).
Construindo Sua Própria Imagem com um Dockerfile
Para empacotar sua aplicação, você cria um Dockerfile — um arquivo de texto com instruções. Aqui está um exemplo simples para uma aplicação Node.js:
# Use um runtime oficial do Node.js como imagem pai
FROM node:18-alpine
# Defina o diretório de trabalho
WORKDIR /usr/src/app
# Copie o package.json e instale as dependências
COPY package*.json ./
RUN npm install
# Copie o restante do código da aplicação
COPY . .
# Exponha a porta em que a aplicação é executada
EXPOSE 3000
# Defina o comando para executar a aplicação
CMD ["node", "app.js"]
Construa a imagem com:
docker build -t my-node-app .
Depois, execute-a:
docker run -p 3000:3000 my-node-app
Simplificando Aplicações Multi-Contêiner com Docker Compose
Aplicações do mundo real geralmente precisam de vários serviços: um servidor web, um banco de dados, um cache, etc. Gerenciá-los individualmente com docker run se torna tedioso. O Docker Compose permite definir e executar aplicações multi-contêiner usando um arquivo YAML.
Crie um arquivo docker-compose.yml:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: example
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Depois, inicie tudo com:
docker compose up -d
O Compose cuida da rede, dos volumes e das dependências para você. Para parar e remover contêineres, redes e volumes, execute docker compose down.
Boas Práticas para Docker no Desenvolvimento
- Use .dockerignore: Exclua arquivos desnecessários (como
node_modules) do contexto de build para acelerar as builds. - Aproveite builds multi-stage: Mantenha sua imagem final pequena separando as dependências de tempo de build do runtime.
- Fixe as versões: Especifique tags exatas de imagens (por exemplo,
node:18.16.0-alpine) para garantir reprodutibilidade. - Execute como não-root: Crie um usuário no seu Dockerfile e mude para ele com
USERpara melhorar a segurança. - Use volumes para dados persistentes: Bancos de dados e outros serviços com estado devem armazenar dados em volumes, não dentro do contêiner.
Armadilhas Comuns e Como Evitá-las
- Imagens inchadas: Comece a partir de uma imagem base mínima (como Alpine) e limpe os caches de pacotes.
- Conflitos de porta: Certifique-se de que as portas do host não estejam em uso. Use
docker pspara verificar. - Ignorar os logs: Use
docker logs <container>para depurar problemas. - Não usar o Compose para desenvolvimento local: Mesmo aplicações simples se beneficiam da configuração declarativa do Compose.
FAQ
Qual é a diferença entre uma imagem Docker e um contêiner?
Uma imagem é um modelo somente leitura que define a aplicação e seu ambiente. Um contêiner é uma instância em execução dessa imagem. Você pode criar muitos contêineres a partir de uma única imagem.
Preciso do Docker Compose para uma aplicação de contêiner único?
Não necessariamente. Para aplicações simples, docker run é suficiente. No entanto, o Compose facilita o gerenciamento de variáveis de ambiente, volumes e redes, mesmo para um único serviço, e simplifica o escalonamento posteriormente.
Como faço para persistir dados no Docker?
Use volumes. Volumes são armazenados fora do sistema de arquivos do contêiner e persistem mesmo se o contêiner for excluído. No Compose, defina um volume e monte-o em um caminho no contêiner (por exemplo, volumes: - mydata:/var/lib/data).
Pronto para otimizar seu fluxo de trabalho com Docker? Quando precisar analisar logs de contêineres, experimente nosso Analisador de Logs Nginx para analisar e visualizar dados de log rapidamente — perfeito para depurar servidores web conteinerizados.