Seu primeiro Dockerfile do zero: passo a passo
Docker & Containers · Por Kevin Marcondes · Publicado em · Atualizado em · 7 min de leitura
Este guia ensina, na prática, como criar seu primeiro Dockerfile do zero, construir a imagem e rodar um container com uma pequena aplicação de exemplo. Você entenderá as diretivas essenciais, como funciona o cache de camadas, .dockerignore e um ajuste simples de segurança. Siga os passos numerados e valide tudo no final.
Pré-requisitos e visão rápida do que você fará
Você precisará de:
- Docker instalado e em execução. Em Linux, seu usuário deve poder usar o Docker sem sudo.
- Editor de texto e terminal.
- Algum espaço em disco para imagens.
Vamos criar um app simples, escrever o Dockerfile, fazer o build, executar e otimizar com .dockerignore e usuário não‑root. Se quiser comparar quando usar containers ou VMs, leia o comparativo Docker vs VMs para HA.
- Docker instalado (Desktop, Engine ou similar)
- Terminal com acesso a internet
- Editor de texto para criar arquivos do projeto
# Verifique se o Docker está funcionando
docker --version
# (Opcional) Teste rápido do daemon
docker run --rm hello-world
Estrutura básica de um Dockerfile
Um Dockerfile descreve como montar uma imagem a partir de camadas. Cada instrução cria ou influencia uma camada em cache. Abaixo, as diretivas mais usadas e um exemplo mínimo. Use nomes de imagem leves (ex.: variantes slim) para reduzir tamanho.
| Diretiva | Para que serve | Exemplo |
|---|---|---|
| FROM | Imagem base inicial | FROM python:3.12-slim |
| WORKDIR | Define diretório de trabalho | WORKDIR /app |
| COPY | Copia arquivos para a imagem | COPY . . |
| RUN | Executa comandos de build | RUN pip install -r requirements.txt |
| EXPOSE | Documenta a porta usada | EXPOSE 8000 |
| CMD | Comando padrão ao iniciar | CMD ["python", "app.py"] |
# Exemplo mínimo
FROM alpine:3.20
CMD ["echo", "Hello from Docker!"]
Prepare o projeto de exemplo (Flask)
Vamos criar uma API mínima em Flask para validar rede e logs. No Windows, se preferir, crie os arquivos manualmente em vez de usar o heredoc. Os arquivos serão: requirements.txt e app.py.
mkdir hello-docker && cd hello-docker
# requirements.txt
cat > requirements.txt <<'REQ'
flask
REQ
# app.py
cat > app.py <<'PY'
from flask import Flask
app = Flask(__name__)
@app.get("/")
def index():
return {"message": "Hello from Docker"}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
PY
Escreva o Dockerfile passo a passo
Crie um arquivo chamado Dockerfile na raiz do projeto. Primeiro copiamos dependências para aproveitar o cache e depois o código. Exponha a porta do app e defina o comando de inicialização.
# Dockerfile (versão inicial)
FROM python:3.12-slim
# Impede criação de .pyc e ativa logs imediatos no stdout/stderr
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Define diretório de trabalho
WORKDIR /app
# Copia dependências primeiro para aproveitar cache
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copia o restante do código
COPY . .
# Documenta a porta usada
EXPOSE 8000
# Comando padrão
CMD ["python", "app.py"]
Faça o build e entenda o cache de camadas
O build cria a imagem executando as instruções do Dockerfile. Alterações em arquivos copiados antes invalidam o cache das etapas seguintes. Modificar apenas o código, mantendo requirements.txt igual, reaproveita o cache de dependências. Para CI/CD, automatize estes comandos (veja o guia de CI/CD com GitHub Actions).
# Execute a partir da pasta do projeto (onde está o Dockerfile)
docker build -t hello-docker:dev .
# Verifique a imagem criada
docker image ls | grep hello-docker
# Rode novamente para observar o uso de cache
docker build -t hello-docker:dev .
# (Opcional) Inspecione o histórico de camadas
docker image history hello-docker:dev
Rode o container e mapeie a porta
Agora vamos subir o container e mapear a porta 8000 do host para a 8000 do container. Use -d para rodar em background. Para parar, use docker stop. Mapear portas permite acessar a aplicação no navegador.
# Executa em foreground (Ctrl+C para parar)
docker run --rm -p 8000:8000 --name hello hello-docker:dev
# Ou em background (daemon)
# docker run -d --rm -p 8000:8000 --name hello hello-docker:dev
# Em outro terminal, teste:
# curl http://localhost:8000
Otimize: .dockerignore e usuário não‑root
Reduza o contexto de build e melhore segurança. O .dockerignore evita enviar arquivos desnecessários para o daemon. Rodar como não‑root reduz impacto de falhas. Se sua imagem roda uma API voltada à internet, veja práticas de segurança em APIs seguras com OAuth2 e rate limiting.
# .dockerignore (crie este arquivo na raiz)
__pycache__/
*.pyc
.env
.git
.gitignore
README*
# Dockerfile (otimizado)
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# Camada de dependências (cacheável)
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copia o código
COPY . .
# Cria usuário não-root e ajusta permissões
RUN adduser --disabled-password --gecos "" appuser \
&& chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]
Como confirmar que funcionou e solucionar problemas
Verifique a aplicação, logs e saúde do container. Em caso de erro, confira mensagens e portas. Para padronizar logs da sua app, considere adotar logs estruturados; veja o guia de logs em JSON e correlação.
- Não abre no navegador: confirme se mapeou -p 8000:8000 e acesse http://localhost:8000.
- Porta já em uso: troque para -p 8080:8000 e tente novamente.
- Permissão negada ao usar USER: garanta chown -R appuser:appuser /app no Dockerfile.
- Dependência não encontrada: atualize requirements.txt e faça build sem cache (use --no-cache).
- Daemon inacessível: inicie o serviço Docker/Desktop e verifique permissões do usuário (no Linux, grupo docker).
- Imagem grande: use base slim, .dockerignore e limpe caches de build quando possível.
# Teste HTTP e logs
curl -s http://localhost:8000 | jq .
docker ps --filter name=hello
docker logs -f hello
# (Opcional) Acesse o shell do container para inspeção
# docker exec -it hello sh
Perguntas frequentes
O que é um Dockerfile e por que eu preciso dele?
É um arquivo de texto com instruções para construir uma imagem de container. Ele garante builds reproduzíveis, padroniza ambiente e facilita distribuição da sua aplicação. Sem um Dockerfile, você dependeria de ambientes manuais e menos previsíveis.
Preciso ter Python instalado localmente para seguir o tutorial?
Não. Você só cria os arquivos de código e quem fornece o Python é a própria imagem base no build e na execução. Se preferir outro runtime, adapte FROM, portas e o comando CMD de acordo com sua stack.
Qual a diferença entre CMD e ENTRYPOINT?
CMD define o comando padrão quando o container inicia, podendo ser sobrescrito pela linha de comando. ENTRYPOINT define o executável principal; argumentos adicionais podem vir de CMD. Use ENTRYPOINT quando o processo principal não deve mudar.
É obrigatório usar .dockerignore?
Não é obrigatório, mas é altamente recomendado. Ele reduz o contexto de build, acelera o processo e evita vazar arquivos sensíveis para a imagem. Inclua diretórios de build, caches, VCS e segredos que não devem ir para a imagem.
Como reduzir o tempo de build e aproveitar cache?
Copie e instale dependências antes do código-fonte, pois elas mudam com menos frequência. Evite RUN desnecessários, una comandos quando fizer sentido e mantenha a imagem base enxuta. Se alterar apenas o código, as camadas de dependências serão reaproveitadas.
Como publicar a imagem em um registry?
Escolha um registry, faça login com a CLI, tagueie sua imagem com o namespace do registry e faça o push. Em pipelines de CI/CD, automatize login, build, tag e push. Verifique políticas de acesso e nomes consistentes para ambientes.
Pronto para o próximo passo?
Automatize builds e deploys com pipelines. Comece com GitHub Actions para criar, testar e publicar imagens a cada push.
Ler guia de CI/CDVeja também
- tutorial: implementar Canary releases com Traefik em Docker Compose usando headers e weight routing para deploys graduais de aplicações PHP
- Tutorial: configurar HAProxy com Let's Encrypt e healthchecks para balanceamento de tráfego entre containers Docker em VPS (passo a passo)
- Tutorial prático: implementar mTLS entre Nginx e microserviços usando smallstep CA em Docker Compose
- Tutorial: configurar cgroup v2 e systemd ResourceControl para limitar CPU, memória e I/O de containers Docker no Debian