Build fallita senza log: come ottenere l'output
Quando una build fallisce senza log, significa che l'errore si è verificato prima dell'avvio della build. Scopri le quattro fasi in cui può accadere, come distinguerle e come ottenere l'output da ciascuna.
"Build failed." Nessuno stack trace, nessun errore del compilatore, nessun output. Una build fallita senza log è il messaggio meno utile che una piattaforma possa produrre e, di solito, indica qualcosa di preciso che vale la pena capire: l'errore si è verificato prima dell'avvio del componente che produce i log.
Una build non consiste in un unico passaggio. Le fasi sono quattro e ciascuna può fallire in modo diverso.
Le quattro fasi
1. Recupero del codice sorgente. La piattaforma clona il repository a partire da un ref. 2. Preparazione della build. Determina come eseguire la build — Dockerfile, buildpack o framework rilevato automaticamente. 3. Esecuzione della build. Vengono eseguiti i tuoi comandi. Questa è l'unica fase che produce l'output che ti aspetti. 4. Packaging. Il risultato viene trasformato in un'immagine eseguibile.
Se non hai alcun log, l'errore si è verificato nella fase 1 o 2. La build non è mai stata eseguita, quindi non ha potuto stampare nulla.
Fase 1: il codice non è mai stato recuperato
I sintomi sono silenzio totale e un errore rapido — di solito in meno di quindici secondi.
Cause comuni, in ordine:
- Il branch non esiste. Un servizio configurato per eseguire il deployment di
mastersu un repository che è stato rinominato inmain. L'errore si verifica immediatamente e non fornisce quasi nessuna informazione. - L'accesso è stato revocato. Il token o l'installazione dell'app che funzionava il mese scorso è stata rimossa, oppure il repository è stato spostato in un'organizzazione per cui l'autorizzazione non è più valida.
- Il repository è privato e la connessione è scaduta. Stesso scenario: la piattaforma riceve un 404 invece di un 403, perché è il codice che i provider Git restituiscono per i repository privati che non puoi visualizzare.
- Non è possibile recuperare un submodule. Il repository principale viene clonato, ma un submodule che usa un URL SSH fallisce perché l'ambiente di build non dispone della relativa chiave.
Il controllo più rapido: la piattaforma mostra un hash di commit per il deployment fallito? Se non lo mostra, il codice non è mai stato recuperato e nulla nel tuo Dockerfile è rilevante.
Fase 2: non sa come eseguire la build
Anche questa fase è silenziosa, perché non è ancora stato scelto alcun comando di build.
- Nessun Dockerfile nel percorso configurato. Un
dockerfilePathche punta a un percorso che è stato spostato. - Un monorepo senza root. La piattaforma sta cercando nella root del repository, mentre il tuo servizio si trova in
apps/api. - Il rilevamento non ha trovato nulla. Non è stato individuato alcun manifest riconosciuto, quindi nessun buildpack corrisponde al progetto.
- Un Dockerfile che non può essere analizzato. Un errore di sintassi alla riga 1 causa il fallimento prima dell'esecuzione di qualsiasi layer.
Fase 3: è qui che esistono i log
Se visualizzi un output parziale che si interrompe bruscamente, sei nella fase 3 e le due cause più comuni riguardano le risorse, non il codice:
Memoria esaurita. Una build terminata dall'OOM reaper non riesce a stampare alcuna informazione al riguardo. Il log si interrompe semplicemente nel mezzo di uno step. Le build di TypeScript, webpack e Vite su codebase di grandi dimensioni incontrano spesso questo problema; l'indizio è che lo stesso commit viene compilato correttamente sul tuo laptop, che dispone di più memoria del builder.
Timeout. Una build che supera il limite della piattaforma viene terminata. Lo stesso sintomo: l'output si interrompe invece di terminare normalmente.
Entrambi i casi possono sembrare una situazione di "assenza di log" se l'errore si verifica abbastanza presto.
Fase 4: la build è riuscita, ma il packaging non è possibile
Caso raro e specifico: la build è riuscita, ma l'artefatto non è corretto. Potrebbe trattarsi di un'immagine senza CMD o ENTRYPOINT, di un'architettura non compatibile o di un'immagine troppo grande per il limite della piattaforma.
L'ordine della diagnosi
# Is there a commit hash? If not, stage 1.
dockup deployments my-project/my-api --json
# Build logs of the latest deployment, streamed as it goes
dockup logs my-project/my-api --build --follow
# The full record, including which stage took how long
dockup status my-project/my-api --json
stageTimings nell'ultimo output è il modo più rapido per individuare l'errore. Un deployment che ha impiegato 0,4 secondi per il clone e poi è terminato con un errore si è interrotto nella fase 1. Uno che ha impiegato novanta secondi per la build e poi si è interrotto presenta un problema di fase 3, molto probabilmente legato alla memoria.
Come ottenere l'output quando non ce n'è
Tre tecniche, in ordine di impegno richiesto:
Riproduci localmente il vincolo. Non limitarti a chiederti "la build funziona sulla mia macchina": eseguila con la stessa quantità di memoria disponibile sul builder:
docker build --memory=2g --memory-swap=2g -t test .
Se in questo modo riproduci l'errore, lo hai individuato: è un problema di memoria, non qualcosa di misterioso.
Rendi la build più verbosa. La maggior parte degli strumenti di build, per impostazione predefinita, non fornisce molte informazioni sul problema che sta per terminarli.
# Print progress so a truncated log still shows where it stopped
RUN npm ci --loglevel verbose
RUN NODE_OPTIONS="--max-old-space-size=3072" npm run build
Vale la pena provare anche solo quella riga con NODE_OPTIONS: una build Node che termina senza messaggi è molto spesso limitata dall'heap e aumentare il limite risolve build che non producevano alcuna informazione diagnostica.
Esegui un bisect del Dockerfile. Commenta tutto ciò che segue lo step che causa l'errore e aggiungi marker RUN echo "reached step N". È un metodo rudimentale, ma funziona quando nient'altro è efficace.
Come ridurre questo tipo di problema
Due aspetti sono più importanti di qualsiasi tecnica di debugging.
Log trasmessi in streaming anziché riepilogati. Se l'output viene mostrato solo al termine della build, una build terminata forzatamente non produce nulla, perché il riepilogo viene scritto alla fine. Lo streaming garantisce che il log disponibile al momento dell'errore contenga tutto fino all'istante in cui la build si è interrotta.
dockup logs my-project/my-api --build --follow
Fasi con nome e durata. "Build failed" è una sola informazione. "Clone: 0.4s, build: failed after 94s" è sufficiente per escludere tre delle quattro cause precedenti senza leggere altro.
Domande frequenti
Perché la mia build non produce alcun log? Perché è fallita prima dell'esecuzione dei comandi di build — di solito durante il recupero del codice sorgente o nell'individuazione della modalità di build. Nessuna delle due fasi produce output della build.
Perché la build funziona localmente ma non sulla piattaforma?
Nella maggior parte dei casi, per la memoria. La tua macchina ne dispone di più rispetto al builder. Riproduci il problema con docker build --memory=2g per confermarlo prima di cercare altrove.
Che cosa significa un log che si interrompe nel mezzo di uno step? Il processo è stato terminato invece di uscire normalmente. Le due possibilità sono memoria esaurita o timeout della build; l'OOM killer non dà al processo la possibilità di spiegare che cosa è successo.
Mi serve un Dockerfile? Non necessariamente: le piattaforme possono rilevare i tipi di progetto più comuni ed eseguire la build senza Dockerfile. Tuttavia, il fallimento del rilevamento è di per sé un errore silenzioso e privo di log, quindi un Dockerfile esplicito elimina un'intera categoria di ambiguità.
