Risoluzione degli errori di build Docker: una guida completa alla risoluzione dei problemi
Debug degli errori di build Docker causati da percorsi errati, pacchetti mancanti, sorprese della cache, problemi di rete, permessi o spazio su disco.
Risoluzione degli errori di build Docker: una guida completa alla risoluzione dei problemi
Gli errori di build Docker sono più facili da risolvere quando tratti l'output della build come una trascrizione. Docker ti dice quale passaggio è fallito, quale comando è stato eseguito e cosa ha stampato il comando. Il lavoro utile è trovare il primo vero errore, non l'ultima riga che dice che la build è fallita.
Esegui la build con progresso semplice quando l'output predefinito nasconde troppo:
docker build --progress=plain -t my-app:debug .
Cerca il numero del passaggio fallito, ad esempio #8, e l'istruzione accanto. Se l'istruzione fallita è COPY, probabilmente hai un problema di contesto di build o di percorso. Se è RUN apt-get install, hai un problema di pacchetto, rete, repository o architettura. Se è RUN npm ci o pip install, leggi l'errore del gestore pacchetti prima di modificare le impostazioni di Docker.
COPY failed: il file non è nel contesto di build
Uno degli errori di build più comuni è anche uno dei più semplici:
COPY failed: file not found in build context or excluded by .dockerignore
Docker può copiare solo i file all'interno del contesto di build, che di solito è l'argomento finale di docker build:
docker build -t my-app .
Qui . è il contesto. Un Dockerfile in una sottodirectory non può copiare ../secret.txt dall'esterno di quel contesto. Docker lo blocca intenzionalmente perché le build dovrebbero essere riproducibili dal loro contesto.
Controlla tre cose:
pwd
ls -la
docker build --progress=plain -f path/to/Dockerfile .
Se il tuo Dockerfile si trova in docker/Dockerfile ma l'app è nella radice del repository, esegui la build dalla radice e punta al Dockerfile con -f:
docker build -f docker/Dockerfile -t my-app .
Controlla anche .dockerignore. Potrebbe escludere il file che stai cercando di copiare. Questo accade spesso con dist, target, .env o file generati. Se il Dockerfile si aspetta un file, non ignorarlo a meno che il file non venga creato all'interno della build.
Errori di installazione dei pacchetti
Gli errori del gestore pacchetti di solito rientrano in alcune categorie: indici dei pacchetti obsoleti, nomi di pacchetti errati, repository mancanti, problemi di rete o utilizzo di comandi della distribuzione Linux sbagliata.
Questo fallisce su Alpine perché Alpine non usa apt-get:
FROM alpine:3.20
RUN apt-get update && apt-get install -y curl
Usa il gestore pacchetti per l'immagine di base:
FROM alpine:3.20
RUN apk add --no-cache curl
Per immagini Debian o Ubuntu, mantieni aggiornamento e installazione nello stesso layer:
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && rm -rf /var/lib/apt/lists/*
Se apt-get install dice che non riesce a trovare un pacchetto, conferma il nome del pacchetto per quella versione della distribuzione. I nomi dei pacchetti differiscono tra Debian, Ubuntu, Alpine, Fedora e immagini specifiche per linguaggio. Le immagini minime potrebbero anche mancare di strumenti che presumi siano presenti, come bash, curl, git, tar o ca-certificates.
Se i download HTTPS falliscono con errori di certificato, installa i certificati CA prima di usare curl, wget, git su HTTPS, npm, pip o gestori pacchetti per linguaggi:
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
Sorprese della cache
La cache di Docker è di solito utile, ma può rendere una build rotta incoerente. Se sospetti una cache obsoleta, esegui:
docker build --no-cache --progress=plain -t my-app:debug .
Se la build fallisce solo senza cache, potresti aver fatto affidamento su layer vecchi. Se fallisce solo con la cache, controlla se un file generato o un file di blocco delle dipendenze è cambiato in un modo che Docker non vede dove ti aspetti.
Per installazioni di dipendenze, copia i file di blocco prima dell'intero albero del codice sorgente:
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Per Python:
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Questo schema non è solo più veloce. Rende gli errori di build più facili da capire perché l'installazione delle dipendenze dipende dai file delle dipendenze, non da ogni modifica del codice sorgente.
Errori di rete e registro
Una build potrebbe fallire prima che il tuo Dockerfile venga eseguito se Docker non riesce a scaricare l'immagine di base:
docker pull python:3.12-slim
Se fallisce, risolvi prima l'accesso al registro. Controlla l'autenticazione per registri privati, impostazioni del proxy aziendale, DNS e regole del firewall. Dietro un proxy, il demone Docker necessita della configurazione del proxy; impostare HTTP_PROXY solo nella tua shell interattiva potrebbe non essere sufficiente.
Per download all'interno di passaggi RUN, testa l'URL dall'host e poi da un contenitore temporaneo sullo stesso percorso di rete:
curl -I https://example.com/file.tar.gz
docker run --rm curlimages/curl -I https://example.com/file.tar.gz
Non dipendere da script remoti non bloccati se puoi evitarlo. Un Dockerfile che esegue curl su install.sh da un ramo in movimento può rompersi perché lo script remoto è cambiato. Preferisci download con versione e verifica del checksum per i binari:
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
Sostituisci <sha256> con il checksum reale dalla pagina di rilascio del progetto.
Disallineamenti di architettura
Su Apple Silicon o flotte CI miste, gli errori di build possono derivare dall'architettura. Un'immagine o un binario scaricato potrebbe essere amd64 mentre il builder è arm64, o viceversa. I sintomi includono exec format error, pacchetti mancanti per un'architettura o binari che falliscono durante la build.
Controlla il tuo host e target:
docker version
docker buildx ls
Esegui la build per una piattaforma specifica quando necessario:
docker buildx build --platform linux/amd64 -t my-app:amd64 .
Fai attenzione: le build cross-platform possono essere più lente quando è coinvolta l'emulazione. Per CI, i builder nativi per ogni piattaforma sono spesso più veloci e meno sorprendenti.
Errori di permesso durante la build
I permessi falliscono nelle build quando i file vengono copiati con proprietà inaspettate, gli script non sono eseguibili o il Dockerfile passa a un utente non root prima che la configurazione sia completa.
Se uno script fallisce con permission denied, controllalo prima di copiare supposizioni nel Dockerfile:
ls -l scripts/start.sh
Poi risolvi o in git o nell'immagine:
COPY scripts/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh
Se usi un utente runtime non root, crea le directory e imposta la proprietà prima di cambiare utente:
RUN useradd -r -u 10001 appuser && mkdir -p /app/data && chown -R appuser:appuser /app
USER appuser
COPY --chown=appuser:appuser . . è spesso più pulito che copiare come root ed eseguire un ampio chown ricorsivo in seguito.
Spazio su disco e pulizia della cache di build
Le build grandi possono fallire perché l'host Docker esaurisce lo spazio su disco. Controlla l'utilizzo di Docker:
docker system df
Rimuovi la cache di build inutilizzata quando appropriato:
docker builder prune
Fai più attenzione con comandi di pulizia ampi. docker system prune -a rimuove le immagini inutilizzate e questo può forzare grandi re-pull o rompere flussi di lavoro che si basano su immagini locali. Usalo quando capisci l'impatto.
Se le build riempiono regolarmente il disco, la soluzione migliore è di solito contesti di build più piccoli, build multi-stadio ed evitare file temporanei enormi nei layer. Pulisci gli artefatti temporanei nella stessa istruzione RUN che li crea.
Debug di un passaggio RUN fallito in modo interattivo
Quando una lunga riga RUN fallisce, dividila temporaneamente:
RUN apt-get update
RUN apt-get install -y --no-install-recommends packageA packageB
RUN some-command-that-fails
Una volta trovato il comando fallito, puoi combinare di nuovo i comandi correlati per un'immagine più pulita.
Un altro trucco utile è fermarsi a uno stadio noto come buono. Se il tuo Dockerfile ha stadi, costruisci un target:
docker build --target builder -t my-app-builder .
docker run --rm -it my-app-builder sh
Da lì puoi ispezionare i file, eseguire il comando fallito manualmente, controllare le variabili d'ambiente e vedere cosa contiene effettivamente il filesystem.
Per immagini senza shell, aggiungi uno stadio di debug temporaneo piuttosto che inquinare l'immagine di produzione:
FROM builder AS debug
RUN apt-get update && apt-get install -y --no-install-recommends bash curl
Esegui la build con --target debug, investiga, poi rimuovi o ignora il target debug quando hai finito.
Mantieni le build prevedibili
Una build Docker affidabile è noiosa nel modo migliore. Usa immagini di base con versione. Mantieni i file di blocco delle dipendenze nel controllo del codice sorgente. Evita latest nei Dockerfile di produzione a meno che il tuo processo non ricostruisca e testi intenzionalmente contro tag mobili. Mantieni .dockerignore stretto. Rendi i download di rete con versione e verificati. Metti il codice sorgente che cambia frequentemente dopo l'installazione delle dipendenze.
Quando una build fallisce, non riscrivere l'intero Dockerfile in una volta. Identifica l'istruzione fallita, riproduci con log semplici, isola il comando e risolvi la causa reale più piccola. Questo approccio è più veloce e ti lascia con un Dockerfile che la prossima persona può ancora capire.