Journal-indexDockup / praktijknotitie
Note / build-fails-with-no-logs

Build mislukt zonder logs: zo krijg je uitvoer

Als een build mislukt zonder logs, betekent dat dat de fout al optrad voordat je build begon. Leer de vier fasen waarin dat kan gebeuren, hoe je ze uit elkaar houdt en hoe je uit elke fase uitvoer krijgt.

"Build mislukt." Geen stack trace, geen compilerfout, helemaal geen uitvoer. Een build die mislukt zonder logs is de minst bruikbare melding die een platform kan geven. Meestal betekent dit iets specifieks dat de moeite waard is om te begrijpen: de fout trad op voordat het onderdeel dat logs produceert, was gestart.

Een build bestaat niet uit één stap. Het zijn er vier, en elke stap mislukt op een andere manier.

De vier fasen

1. De broncode ophalen. Het platform clonet je repository op een ref. 2. De build voorbereiden. Het bepaalt hoe de build moet worden uitgevoerd — met een Dockerfile, buildpack of gedetecteerd framework. 3. De build uitvoeren. Je opdrachten worden uitgevoerd. Dit is de enige fase die de uitvoer produceert die je verwacht. 4. Packaging. Het resultaat wordt omgezet in een uitvoerbare image.

Als je helemaal geen logs hebt, zat de fout in fase 1 of 2. Je build is nooit uitgevoerd en kon dus niets afdrukken.

Fase 1: je code is nooit opgehaald

De symptomen zijn volledige stilte en een snelle fout — meestal binnen vijftien seconden.

Veelvoorkomende oorzaken, in volgorde:

  • De branch bestaat niet. Een service die is ingesteld om master te deployen tegen een repository die is hernoemd naar main. Dit mislukt onmiddellijk en zegt vrijwel niets.
  • Toegang is ingetrokken. De token of app-installatie die vorige maand nog werkte, is verwijderd, of de repository is verplaatst naar een organisatie waarvoor de toekenning niet meer geldt.
  • De repository is private en de verbinding is verlopen. Hetzelfde patroon als hierboven; het platform krijgt een 404 in plaats van een 403, omdat Git-providers dat teruggeven voor private repositories die je niet kunt zien.
  • Een submodule kan niet worden opgehaald. De hoofdrepository wordt gecloned, maar een submodule met een SSH-URL mislukt omdat de buildomgeving daar geen sleutel voor heeft.

De snelle controle: toont het platform een commit-hash voor de mislukte deployment? Als dat niet zo is, heeft het de code nooit opgehaald en is niets in je Dockerfile relevant.

Fase 2: het weet niet hoe het moet builden

Ook stil, omdat er nog geen buildopdracht is gekozen.

  • Geen Dockerfile op de locatie die in de configuratie staat. Een dockerfilePath die verwijst naar een pad dat is verplaatst.
  • Een monorepo zonder root. Het platform kijkt naar de root van de repository en je service staat in apps/api.
  • Detectie heeft niets gevonden. Er is geen herkend manifest, dus geen buildpack kwam overeen.
  • Een Dockerfile die niet kan worden geparsed. Een syntaxfout op regel 1 zorgt ervoor dat de build mislukt voordat er een layer wordt uitgevoerd.

Fase 3: hier bestaan de logs

Als je gedeeltelijke uitvoer ziet die abrupt stopt, zit je in fase 3. De twee meest voorkomende oorzaken zijn resources in plaats van code:

Onvoldoende geheugen. Een build die door de OOM-reaper wordt beëindigd, krijgt geen kans om daar iets over af te drukken. De log stopt gewoon midden in een stap. TypeScript-, webpack- en Vite-builds in grote codebases lopen hier regelmatig tegenaan. De aanwijzing is dat dezelfde commit op je laptop wel succesvol wordt gebouwd, omdat die meer geheugen heeft dan de builder.

Timeout. Een build die de limiet van het platform overschrijdt, wordt beëindigd. Hetzelfde symptoom: de uitvoer stopt in plaats van netjes te eindigen.

Beide zien eruit als "geen logs" als de fout vroeg genoeg optreedt.

Fase 4: de build is geslaagd, maar packaging lukt niet

Zeldzaam en specifiek: de build is geslaagd, maar het artefact klopt niet. Bijvoorbeeld een image zonder CMD of ENTRYPOINT, een architectuurmismatch of een image die te groot is voor de limiet van het platform.

De diagnostische volgorde

# 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 in die laatste uitvoer is de snelste manier om de fout te lokaliseren. Een deployment die 0,4 seconden aan het clonen besteedde en daarna mislukte, is een fout in fase 1. Een deployment die negentig seconden aan het builden was en daarna stopte, wijst op een probleem in fase 3, waarschijnlijk met geheugen.

Uitvoer krijgen als die ontbreekt

Drie technieken, in volgorde van inspanning:

Reproduceer de beperking lokaal. Niet: "werkt de build op mijn machine?" — build de code met dezelfde hoeveelheid geheugen als de builder:

docker build --memory=2g --memory-swap=2g -t test .

Als dit de fout reproduceert, heb je de oorzaak gevonden: het gaat om geheugen en niet om iets mysterieus.

Maak je build spraakzamer. De meeste buildtools zijn standaard stil over het probleem dat ze op het punt staan te veroorzaken.

# 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

Die regel met NODE_OPTIONS is het proberen op zichzelf al waard — een Node-build die stil uitvalt, loopt heel vaak tegen een heap-limiet aan. Het verhogen daarvan verhelpt builds die helemaal geen diagnostische informatie opleverden.

Bisect de Dockerfile. Commentarieer alles na de falende stap uit en voeg RUN echo "reached step N"-markeringen toe. Primitief, maar het werkt wanneer niets anders helpt.

Wat dit soort problemen helpt voorkomen

Twee dingen zijn belangrijker dan welke debuggingtechniek dan ook.

Logs streamen in plaats van samenvatten. Als uitvoer pas verschijnt nadat een build is voltooid, produceert een build die wordt beëindigd niets, omdat de samenvatting aan het einde wordt geschreven. Met streaming heb je op het moment dat de build stopt de log tot precies dat moment.

dockup logs my-project/my-api --build --follow

Fasen benoemen en timen. "Build mislukt" is één stukje informatie. "Clone: 0.4s, build: failed after 94s" is genoeg om drie van de vier bovenstaande oorzaken over te slaan zonder iets te hoeven lezen.

Veelgestelde vragen

Waarom produceert mijn build helemaal geen logs? Omdat de fout optrad voordat je buildopdrachten werden uitgevoerd — meestal bij het ophalen van de broncode of het bepalen hoe de build moet worden uitgevoerd. Geen van beide fasen produceert builduitvoer.

Waarom werkt de build lokaal wel, maar niet op het platform? Meestal is het geheugen de oorzaak. Je machine heeft daar meer van dan de builder. Reproduceer het probleem met docker build --memory=2g voordat je ergens anders zoekt.

Wat betekent een log die midden in een stap stopt? Het proces is beëindigd in plaats van zelf te stoppen. Onvoldoende geheugen of een build-timeout zijn de twee mogelijke oorzaken. De OOM-killer geeft het proces geen kans om zichzelf uit te leggen.

Heb ik een Dockerfile nodig? Niet per se — platforms kunnen veelvoorkomende projecttypen detecteren en zonder Dockerfile builden. Maar als de detectie mislukt, krijg je zelf een stille fout zonder logs. Een expliciete Dockerfile neemt dus een hele categorie onduidelijkheden weg.