O Docker tornou-se uma ferramenta indispensável no desenvolvimento de software moderno, permitindo que as equipes criem, distribuam e executem aplicativos com consistência e facilidade sem precedentes. No cerne dessa mágica da conteinerização está o Dockerfile, um script simples, porém poderoso, que serve como modelo para a criação de suas imagens Docker.
Criar um processo de construção de Dockerfile eficaz é mais do que um simples exercício técnico; é um caminho para ciclos de desenvolvimento mais rápidos, aplicações menores e mais seguras e implantações mais confiáveis. Seja você um iniciante no Docker ou alguém que busca aprimorar suas habilidades, este guia irá orientá-lo nos fundamentos da escrita de Dockerfiles práticos, eficientes e seguros.
Entendendo o ecossistema Docker: uma breve revisão

Antes de mergulharmos nos Dockerfiles, vamos abordar brevemente os principais conceitos do Docker:
- Imagens: Uma imagem é um pacote executável, leve e independente, que inclui tudo o que é necessário para executar um software, incluindo o código, um ambiente de execução, bibliotecas, variáveis de ambiente e arquivos de configuração. Imagens são modelos imutáveis.
- Contêineres: Um contêiner é uma instância executável de uma imagem. Você pode criar, iniciar, parar, mover ou excluir contêineres. Eles fornecem ambientes isolados para seus aplicativos.
- Dockerfile: Este é o nosso foco. Um Dockerfile é um documento de texto que contém uma sequência de comandos que o Docker usa para montar uma imagem automaticamente.
- Docker Hub/Registros: São repositórios para armazenar e compartilhar imagens Docker, semelhantes ao GitHub para código.
Um Dockerfile bem escrito tem como objetivo produzir uma imagem que seja o mais enxuta, rápida de construir e segura possível.
Explicação das opções de compilação do Docker
- -t myapp:1.0 : Esta opção marca sua imagem com um nome (myapp) e uma versão (1.0). A marcação ajuda no controle de versão e facilita a referência a builds de imagem específicos posteriormente, especialmente ao implantar ou enviar para um registro.
- . (ponto) : Refere-se ao contexto de construção, o diretório onde o Docker deve procurar o Dockerfile e quaisquer outros arquivos necessários durante a construção (por exemplo, arquivos a serem copiados para a imagem).
O Docker compacta e envia o conteúdo deste diretório para o daemon do Docker. É importante observar que somente os arquivos dentro do contexto de construção podem ser acessados durante o processo de construção.
Instruções essenciais para um Dockerfile: os blocos de construção

Vamos explorar as instruções mais comuns e como usá-las de forma eficaz.
- DE: Todo Dockerfile deve começar com uma instrução FROM. Ela especifica a imagem base sobre a qual sua imagem será construída.
- Finalidade: Selecionar um ponto de partida, geralmente um sistema operacional (como Ubuntu:22.04) ou um ambiente de execução de aplicativos pré-configurado (como node:18-alpine).
- Exemplo : FROM python:3.9-slim
- Boa prática: Escolha a imagem base mínima que atenda às necessidades da sua aplicação. As versões Alpine são muito compactas, mas usam a biblioteca libc musl, que pode apresentar problemas de compatibilidade com alguns pacotes dependentes de C. As versões Slim são um bom meio-termo, oferecendo uma versão simplificada de uma distribuição padrão (como o Debian) com a biblioteca glibc.
- WORKDIR: Esta configuração define o diretório de trabalhoDiretório de trabalho para quaisquer instruções subsequentes de RUN, CMD, ENTRYPOINT, COPY e ADD.
- Finalidade: Definir o contexto do diretório atual dentro da imagem para operações de arquivo e execuções de comandos subsequentes. Se o diretório não existir, o Docker o cria.
- Exemplo: WORKDIR /usr/src/app
- Boa prática: Use caminhos absolutos para WORKDIR. Alterar diretórios usando WORKDIR várias vezes geralmente é mais limpo do que encadear comandos cd dentro de instruções RUN.
- CÓPIA: Copia arquivos ou diretórios do seu contexto de compilação para o sistema de arquivos da imagem.
- Finalidade: Adicione o código do seu aplicativo, arquivos de configuração e outros recursos necessários à imagem.
- Exemplo:
Dockerfile
WORKDIR /usr/src/app
COPY package.json ./
COPY src/ ./src/
- Boa prática: Para copiar arquivos de forma simples, prefira o comando COPY em vez do ADD. O COPY é mais transparente. O ADD possui recursos extras, como download de URLs e extração de arquivos tar, que podem ser menos previsíveis. Para maior clareza e segurança, geralmente é melhor usar o comando RUN com curl, wget e tar se você precisar baixar e extrair arquivos.
- CORRER: Executa comandos em uma nova camada sobre a imagem atual e confirma os resultados. Isso é usado para instalar software, criar diretórios, compilar código, etc.
- Finalidade: Modificar o sistema de arquivos da imagem instalando pacotes, executando scripts de compilação ou configurando parâmetros.
- Exemplo:
Dockerfile
RUN apt-get update && apt-get install -y --no-install-recommends \
nginx \
curl \
&& rm -rf /var/lib/apt/lists/*
- Boa prática: encadeie comandos relacionados usando `&&` e limpe arquivos temporários ou caches do gerenciador de pacotes (como `rm -rf /var/lib/apt/lists/*` para Debian/Ubuntu ou `yum clean all` para CentOS/RHEL) na mesma instrução `RUN`. Isso minimiza o número de camadas e reduz o tamanho da imagem, pois cada instrução `RUN` cria uma nova camada.
- AMBIENTE: Define as variáveis de ambiente disponíveis durante o processo de construção (após serem definidas) e quando os contêineres são executados a partir da imagem.
- Finalidade: Fornecer valores de configuração, caminhos ou configurações necessárias para sua aplicação ou scripts de compilação.
- Exemplo:
Dockerfile
ENV NODE_ENV=produção
ENV APP_PORT=3000
- Boa prática: Use variáveis de ambiente (ENV) para dados de configuração não sensíveis. Métodos de injeção em tempo de execução devem ser usados em vez de incorporá-los à imagem com variáveis de ambiente para segredos.
- ARG: Define uma variável de tempo de compilação que os usuários podem passar usando a flag `--build-arg` durante a compilação do Docker.
- Objetivo: Permitir a parametrização do processo de construção sem modificar o Dockerfile.
- Exemplo: Dockerfile
Dockerfile
ARG APP_VERSION=1.0.0
ENV APP_VERSION_ENV=${APP_VERSION}
RUN echo “Construindo versão ${APP_VERSION_ENV}”
- Construa com: bash docker build – build-arg APP_VERSION=1.2.3 -t myapp.
- Observação: as variáveis ARG não estão disponíveis no contêiner em execução, a menos que sejam definidas explicitamente como uma variável de ambiente (ENV), conforme mostrado acima.
- EXPOR: Informa o Docker que o contêiner está escutando nas portas de rede especificadas em tempo de execução.
- Finalidade: Este documento serve principalmente como documentação para o criador de imagens e para o usuário. Ele não publica a porta.
Exemplo:
Dockerfile
EXPOSE 8080
- Nota: Para tornar a porta acessível a partir do host, utilize a flag -p ou -P com o comando docker run (ex.: docker run -p 8080:8080 myimage).
- CMD e PONTO DE ENTRADA: Defina qual comando será executado quando um contêiner for iniciado.
- CMD ["executável", "param1", "param2"] : Fornece valores padrão para um contêiner em execução. Esses valores padrão podem ser facilmente substituídos adicionando um comando ao comando `docker run`. Se você tiver vários comandos `CMD`, apenas o último terá efeito.
- ENTRYPOINT ["executable", "param1", "param2"] : Configura um contêiner para ser executado como um arquivo executável. Os argumentos passados para o comando docker run são anexados ao comando ENTRYPOINT.
- Exemplo (padrão típico):
Dockerfile
ENTRYPOINT [“python”, “app.py”] # Comando principal
CMD [“–help”] # Argumento padrão se nenhum for fornecido em docker run
- Boa prática: Use CMD se desejar um comando padrão facilmente substituível. Use ENTRYPOINT para criar uma imagem que se comporte como um executável específico, frequentemente usando CMD para fornecer argumentos padrão. Para aplicações web, CMD ["npm", "start"] ou CMD ["python", "manage.py", "runserver"] são padrões.
Principais práticas recomendadas para Dockerfiles otimizados

Escrever um Dockerfile funcional é apenas o começo. Otimizá-lo traz benefícios significativos.
- Aproveite o cache de compilação de forma eficaz: O Docker constrói imagens em camadas e tenta reutilizar camadas de construções anteriores, se possível (cache). Para maximizar os acertos no cache:
- Ordene as instruções da menos alterada para a mais alterada. Por exemplo, instale as dependências (que mudam com menos frequência) antes de copiar o código-fonte do seu aplicativo (que muda com frequência).
Dockerfile
# Bom exemplo de cache para um aplicativo Node.js
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./ # As dependências mudam com menos frequência
RUN npm ci --omit=dev # Esta camada é armazenada em cache se os arquivos do pacote não mudarem
COPY . . # O código-fonte muda com frequência, então é o último
CMD ["node", "server.js"]
- Mantenha suas imagens pequenas: Imagens menores são mais rápidas para baixar, enviar e implantar, além de terem uma superfície de ataque reduzida.
- Use imagens base mínimas: as variantes Alpine, slim ou distroless são significativamente menores do que as imagens completas do sistema operacional.
- Limpeza na mesma camada RUN: Remova caches ou arquivos temporários desnecessários usando a mesma instrução RUN após a instalação dos pacotes. Por exemplo:
- Debian/Ubuntu: apt-get clean && rm -rf /var/lib/apt/lists/*
- CentOS/RHEL: yum clean all ou dnf clean all
- Alpino: rm -rf /var/cache/apk/*
- Adote construções em várias etapas: Essa é uma das maneiras mais eficazes de reduzir o tamanho da imagem, especialmente para linguagens compiladas ou aplicativos com etapas de compilação (como front-ends em JavaScript).
- Conceito: Utilize um único estágio (um bloco FROM) com todas as suas ferramentas de compilação e dependências de desenvolvimento para compilar/construir sua aplicação. Em seguida, inicie um novo estágio a partir de uma imagem base de tempo de execução mínima e utilize COPY – from=
… para copiar apenas os artefatos compilados necessários para este estágio final e limpo. - Exemplo (Aplicação Go Simplificada): Dockerfile
- Conceito: Utilize um único estágio (um bloco FROM) com todas as suas ferramentas de compilação e dependências de desenvolvimento para compilar/construir sua aplicação. Em seguida, inicie um novo estágio a partir de uma imagem base de tempo de execução mínima e utilize COPY – from=
Dockerfile
# Etapa 1: Construção
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Etapa 2: Tempo de execução
FROM debian:bullseye-slim
WORKDIR /app
COPY –from=builder /app/myapp .
CMD [“./myapp”]
- A fase de construção inclui o SDK do Go, mas a imagem final contém apenas o binário compilado e o sistema operacional Alpine mínimo.
- Priorizar a segurança:
- Executar como um usuário sem privilégios de root: Por padrão, os contêineres são executados como root. Isso representa um risco de segurança. Crie um usuário e um grupo dedicados e sem privilégios de root no seu Dockerfile e use a instrução USER para alternar para ele.
Dockerfile
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# … COPIE os arquivos e altere a propriedade para appuser:appgroup …
USER appuser
- Instale apenas os pacotes necessários: cada pacote representa uma vulnerabilidade potencial. Evite instalar ferramentas de depuração ou utilitários de desenvolvimento em imagens de produção (use builds em múltiplas etapas para isso).
- Não codifique segredos diretamente no arquivo Dockerfile: evite colocar senhas, chaves de API ou outros segredos diretamente nele (por exemplo, em variáveis de ambiente). Utilize métodos de injeção em tempo de execução, como segredos do Docker, segredos do Kubernetes ou variáveis de ambiente fornecidas de forma segura durante a execução.
- Utilize o .dockerignore de forma eficaz: Crie um arquivo .dockerignore na raiz do seu contexto de build (no mesmo nível do seu Dockerfile). Liste os arquivos e diretórios que devem ser excluídos do envio para o daemon do Docker (por exemplo, .git, node_modules se instalado na imagem, configurações locais da IDE, *.log).
- Benefícios: Acelere o processo de construção do Docker reduzindo o tamanho do contexto de construção e evitando que arquivos sensíveis ou desnecessários sejam incluídos na sua imagem.
Além do básico: melhorias adicionais

Depois de se familiarizar com o que foi mencionado acima, considere estas opções para criar Dockerfiles ainda melhores:
- BuildKit: o mecanismo de compilação mais recente do Docker, geralmente ativado por padrão. Ele oferece melhor desempenho (compilações paralelas), cache aprimorado e recursos avançados, como segredos de compilação (RUN – mount=type=secret,…) e montagens de cache (RUN – mount=type=cache,…) para gerenciadores de pacotes. Certifique-se de que esteja ativo ou habilite-o com DOCKER_BUILDKIT=1.
- Análise estática de Dockerfiles: Utilize ferramentas como o Hadolint (hadolint Dockerfile) para analisar seu Dockerfile estaticamente em busca de erros, violações de estilo e conformidade com as melhores práticas antes de compilá-lo.
Dicas rápidas para solucionar problemas comuns
- Mensagem de erro "Arquivo não encontrado" durante as operações COPY ou ADD : Verifique novamente o caminho de origem (ele é relativo à raiz do contexto de compilação) e certifique-se de que o arquivo não esteja excluído pelo .dockerignore.
- Builds lentos: revise a ordem das instruções para otimizar o cache. Combine comandos RUN sempre que possível. Certifique-se de que seu arquivo .dockerignore seja completo.
- Imagens grandes: Use compilações em várias etapas! Limpe as camadas em modo de execução. Escolha imagens base minimalistas.
Dockerfiles no seu fluxo de trabalho DevOps
Um Dockerfile é uma peça fundamental da "Infraestrutura como Código"
- Controle de versão: Sempre faça o commit do seu Dockerfile no seu repositório Git junto com o código da sua aplicação.
- Integração CI/CD: Automatize o processo de compilação e envio de imagens Docker em seus pipelines de Integração Contínua/Entrega Contínua (por exemplo, GitHub Actions, Jenkins, GitLab CI). Isso garante compilações e implantações consistentes e repetíveis.
Conclusão: Construindo uma base para o sucesso
Criar Dockerfiles eficazes é um investimento que traz benefícios significativos em termos de velocidade de desenvolvimento, confiabilidade operacional e segurança da aplicação. Ao dominar as instruções fundamentais, implementar as melhores práticas, como builds em múltiplas etapas e gerenciamento cuidadoso das camadas da imagem, e priorizar a segurança, você pode produzir imagens Docker enxutas, eficientes e robustas.
Este guia fornece uma base sólida. À medida que você continua sua jornada com o Docker, continue explorando, experimentando e aprimorando seus Dockerfiles.
Pronto para aprimorar suas práticas de Docker e DevOps?
Na Seahawk Media , somos especialistas em ajudar empresas a explorar todo o potencial da conteinerização, das tecnologias em nuvem e dos pipelines de CI/CD otimizados. Nossa equipe de especialistas está à disposição para auxiliar você a otimizar a implantação de seus aplicativos, aprimorar a segurança ou acelerar seu ciclo de desenvolvimento.