Resolvendo Falhas de Build do Docker: Um Guia Abrangente de Solução de Problemas
Depure falhas de build do Docker causadas por caminhos incorretos, pacotes ausentes, surpresas de cache, problemas de rede, permissões ou espaço em disco.
Resolvendo Falhas de Build do Docker: Um Guia Abrangente de Solução de Problemas
As falhas de build do Docker são mais fáceis de corrigir quando você trata a saída do build como uma transcrição. O Docker informa qual etapa falhou, qual comando foi executado e o que o comando imprimiu. O trabalho útil é encontrar o primeiro erro real, não a última linha que diz que o build falhou.
Execute o build com progresso simples quando a saída padrão esconder demais:
docker build --progress=plain -t my-app:debug .
Procure pelo número da etapa com falha, como #8, e a instrução ao lado. Se a instrução com falha for COPY, você provavelmente tem um problema de contexto de build ou caminho. Se for RUN apt-get install, você tem um problema de pacote, rede, repositório ou arquitetura. Se for RUN npm ci ou pip install, leia o erro do gerenciador de pacotes antes de alterar as configurações do Docker.
COPY failed: o arquivo não está no contexto de build
Um dos erros de build mais comuns também é um dos mais simples:
COPY failed: file not found in build context or excluded by .dockerignore
O Docker só pode copiar arquivos dentro do contexto de build, que geralmente é o argumento final para docker build:
docker build -t my-app .
Aqui . é o contexto. Um Dockerfile em um subdiretório não pode copiar ../secret.txt de fora desse contexto. O Docker intencionalmente bloqueia isso porque os builds devem ser reproduzíveis a partir de seu contexto.
Verifique três coisas:
pwd
ls -la
docker build --progress=plain -f path/to/Dockerfile .
Se o seu Dockerfile estiver em docker/Dockerfile mas o aplicativo estiver na raiz do repositório, construa a partir da raiz e aponte para o Dockerfile com -f:
docker build -f docker/Dockerfile -t my-app .
Inspecione também o .dockerignore. Ele pode excluir o arquivo que você está tentando copiar. Isso geralmente acontece com dist, target, .env ou arquivos gerados. Se o Dockerfile espera um arquivo, não o ignore a menos que o arquivo seja criado dentro do build.
Falhas na instalação de pacotes
As falhas do gerenciador de pacotes geralmente se enquadram em algumas categorias: índices de pacotes desatualizados, nomes de pacotes errados, repositórios ausentes, problemas de rede ou uso de comandos da distribuição Linux errada.
Isso falha no Alpine porque o Alpine não usa apt-get:
FROM alpine:3.20
RUN apt-get update && apt-get install -y curl
Use o gerenciador de pacotes para a imagem base:
FROM alpine:3.20
RUN apk add --no-cache curl
Para imagens Debian ou Ubuntu, mantenha a atualização e a instalação na mesma camada:
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && rm -rf /var/lib/apt/lists/*
Se apt-get install disser que não consegue localizar um pacote, confirme o nome do pacote para essa versão da distribuição. Os nomes dos pacotes diferem entre Debian, Ubuntu, Alpine, Fedora e imagens específicas de linguagem. Imagens mínimas também podem faltar ferramentas que você assume que estão presentes, como bash, curl, git, tar ou ca-certificates.
Se downloads HTTPS falharem com erros de certificado, instale certificados CA antes de usar curl, wget, git sobre HTTPS, npm, pip ou gerenciadores de pacotes de linguagem:
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
Surpresas de cache
O cache do Docker geralmente é útil, mas pode tornar um build quebrado inconsistente. Se você suspeitar de cache desatualizado, execute:
docker build --no-cache --progress=plain -t my-app:debug .
Se o build falhar apenas sem cache, você pode ter dependido de camadas antigas. Se falhar apenas com cache, verifique se um arquivo gerado ou arquivo de bloqueio de dependência mudou de uma forma que o Docker não vê onde você espera.
Para instalações de dependência, copie os arquivos de bloqueio antes da árvore de origem completa:
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Para Python:
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Este padrão não é apenas mais rápido. Ele torna as falhas de build mais fáceis de entender porque a instalação de dependências depende dos arquivos de dependência, não de cada alteração de código-fonte.
Falhas de rede e registro
Um build pode falhar antes do seu Dockerfile ser executado se o Docker não conseguir puxar a imagem base:
docker pull python:3.12-slim
Se isso falhar, corrija o acesso ao registro primeiro. Verifique a autenticação para registros privados, configurações de proxy corporativo, DNS e regras de firewall. Atrás de um proxy, o daemon do Docker precisa da configuração do proxy; definir HTTP_PROXY apenas no seu shell interativo pode não ser suficiente.
Para downloads dentro de etapas RUN, teste a URL do host e depois de um contêiner temporário no mesmo caminho de rede:
curl -I https://example.com/file.tar.gz
docker run --rm curlimages/curl -I https://example.com/file.tar.gz
Não dependa de scripts remotos não fixados se puder evitar. Um Dockerfile que faz curl de install.sh de um branch em movimento pode quebrar porque o script remoto mudou. Prefira downloads versionados e verificação de checksum para binários:
RUN curl -fsSLo tool.tar.gz https://example.com/tool-1.2.3-linux-amd64.tar.gz && echo '<sha256> tool.tar.gz' | sha256sum -c - && tar -xzf tool.tar.gz -C /usr/local/bin && rm tool.tar.gz
Substitua <sha256> pelo checksum real da página de lançamento do projeto.
Incompatibilidades de arquitetura
No Apple Silicon ou em frotas de CI mistas, as falhas de build podem vir da arquitetura. Uma imagem ou binário baixado pode ser amd64 enquanto o builder é arm64, ou vice-versa. Os sintomas incluem exec format error, pacotes ausentes para uma arquitetura ou binários que falham durante o build.
Verifique seu host e destino:
docker version
docker buildx ls
Construa para uma plataforma específica quando necessário:
docker buildx build --platform linux/amd64 -t my-app:amd64 .
Cuidado: builds multiplataforma podem ser mais lentos quando a emulação está envolvida. Para CI, builders nativos para cada plataforma são geralmente mais rápidos e menos surpreendentes.
Erros de permissão durante o build
As permissões falham em builds quando os arquivos são copiados com propriedade inesperada, scripts não são executáveis ou o Dockerfile muda para um usuário não root antes da configuração estar completa.
Se um script falhar com permission denied, inspecione-o antes de copiar suposições para o Dockerfile:
ls -l scripts/start.sh
Em seguida, corrija-o no git ou na imagem:
COPY scripts/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh
Se você usar um usuário de runtime não root, crie diretórios e defina a propriedade antes de trocar de usuário:
RUN useradd -r -u 10001 appuser && mkdir -p /app/data && chown -R appuser:appuser /app
USER appuser
COPY --chown=appuser:appuser . . é geralmente mais limpo do que copiar como root e executar um chown recursivo amplo depois.
Espaço em disco e limpeza de cache de build
Builds grandes podem falhar porque o host Docker fica sem espaço em disco. Verifique o uso do Docker:
docker system df
Remova o cache de build não utilizado quando apropriado:
docker builder prune
Tenha mais cuidado com comandos de limpeza amplos. docker system prune -a remove imagens não utilizadas, e isso pode forçar grandes re-pulls ou quebrar fluxos de trabalho que dependem de imagens locais. Use-o quando entender o impacto.
Se os builds encherem o disco regularmente, a melhor correção geralmente é contextos de build menores, builds multi-estágio e evitar arquivos temporários enormes em camadas. Limpe artefatos temporários na mesma instrução RUN que os cria.
Depurar uma etapa RUN com falha interativamente
Quando uma linha RUN longa falha, divida-a temporariamente:
RUN apt-get update
RUN apt-get install -y --no-install-recommends packageA packageB
RUN some-command-that-fails
Depois de encontrar o comando com falha, você pode combinar comandos relacionados novamente para uma imagem mais limpa.
Outro truque útil é parar em um estágio conhecido como bom. Se o seu Dockerfile tiver estágios, construa um destino:
docker build --target builder -t my-app-builder .
docker run --rm -it my-app-builder sh
A partir daí, você pode inspecionar arquivos, executar o comando com falha manualmente, verificar variáveis de ambiente e ver o que o sistema de arquivos realmente contém.
Para imagens sem shell, adicione um estágio de depuração temporário em vez de poluir a imagem de produção:
FROM builder AS debug
RUN apt-get update && apt-get install -y --no-install-recommends bash curl
Construa --target debug, investigue e remova ou ignore o destino de depuração quando terminar.
Mantenha os builds previsíveis
Um build Docker confiável é chato da melhor maneira. Use imagens base versionadas. Mantenha arquivos de bloqueio de dependência no controle de origem. Evite latest em Dockerfiles de produção, a menos que seu processo intencionalmente reconstrua e teste contra tags móveis. Mantenha .dockerignore enxuto. Torne os downloads de rede versionados e verificados. Coloque o código-fonte que muda com frequência após a instalação das dependências.
Quando um build falhar, não reescreva todo o Dockerfile de uma vez. Identifique a instrução com falha, reproduza com logs simples, isole o comando e corrija a menor causa real. Essa abordagem é mais rápida e deixa você com um Dockerfile que a próxima pessoa ainda pode entender.