Неуспешен build без логове: как да получите изход
Когато build завърши неуспешно без логове, това означава, че проблемът е възникнал преди самият build да започне. Научете кои са четирите етапа, на които това може да се случи, как да ги различите и как да получите изход от всеки от тях.
„Build неуспешен.“ Няма stack trace, няма compiler error, няма никакъв изход. Неуспешен build без логове е най-малко полезното съобщение, което една платформа може да покаже, и обикновено означава нещо конкретно, което си струва да разберете: проблемът е възникнал преди да стартира процесът, който генерира логове.
Build не е една стъпка. Той се състои от четири, като всеки етап може да се провали по различен начин.
Четирите етапа
1. Извличане на source кода. Платформата клонира repository-то ви при даден ref. 2. Подготовка на build-а. Тя определя как да извърши build-а — Dockerfile, buildpack или разпозната framework. 3. Изпълнение на build-а. Вашите команди се изпълняват. Това е единственият етап, който генерира изхода, който очаквате. 4. Packaging. Резултатът се превръща в runnable image.
Ако изобщо нямате логове, проблемът е възникнал в етап 1 или 2. Вашият build не е стартирал и следователно не е могъл да изведе нищо.
Етап 1: кодът ви изобщо не е получен
Симптомите са пълна тишина и бърз failure — обикновено за по-малко от петнадесет секунди.
Често срещани причини, подредени по вероятност:
- Branch-ът не съществува. Service, конфигуриран да deploy-ва
master, към repository, което е преименувано наmain. Това се проваля незабавно и почти не показва информация. - Достъпът е отнет. Token-ът или app installation-ът, който е работил миналия месец, е премахнат, или repository-то е преместено в organisation, за която grant-ът вече не важи.
- Repository-то е private и връзката е прекъсната. Случаят е същият като по-горе; платформата получава 404 вместо 403, защото така Git providers отговарят за private repository-та, които нямате право да виждате.
- Submodule не може да бъде извлечен. Основното repository се клонира, но submodule, който използва SSH URL, се проваля, защото build environment-ът няма ключ за него.
Бързата проверка: показва ли платформата commit hash за неуспешния deployment? Ако не го показва, кодът изобщо не е бил получен и нищо във вашия Dockerfile няма отношение към проблема.
Етап 2: платформата не знае как да извърши build
Тук също няма изход, защото все още не е избрана build команда.
- Няма Dockerfile на посоченото място.
dockerfilePathсочи към път, който е бил променен. - Monorepo без root. Платформата проверява root директорията на repository-то, а вашият service се намира в
apps/api. - При detection не е намерено нищо. Няма разпознат manifest, затова не е намерен подходящ buildpack.
- Dockerfile не може да бъде parse-нат. Syntax error на първия ред води до failure, преди да е стартирал който и да е layer.
Етап 3: тук има логове
Ако виждате частичен изход, който внезапно спира, се намирате в етап 3 и двете най-чести причини са свързани с resources, а не с кода:
Недостатъчно memory. Build, прекратен от OOM reaper-а, не успява да изведе информация за причината. Логът просто спира по средата на стъпката. Build-ове с TypeScript, webpack и Vite върху големи codebase-и често се сблъскват с това, а показателният признак е, че същият commit се build-ва успешно на вашия laptop, който разполага с повече memory от builder-а.
Timeout. Build, който надвиши лимита на платформата, се прекратява. Симптомът е същият: изходът спира, вместо да завърши.
И двете причини изглеждат като „няма логове“, ако failure-ът настъпи достатъчно рано.
Етап 4: build-ът е успешен, но не може да бъде package-нат
Рядък и специфичен случай: build-ът е завършил успешно, но artefact-ът е неправилен. Image без CMD или ENTRYPOINT, несъответствие в architecture-та или image, който е прекалено голям за лимита на платформата.
Ред на диагностика
# 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 в последния изход е най-бързият начин да локализирате проблема. Deployment, който е прекарал 0.4 секунди в clone и след това е завършил с failure на етап 1. Ако build-ът е продължил деветдесет секунди и след това е спрял, проблемът е в етап 3, най-вероятно заради memory.
Как да получите изход, когато няма такъв
Три техники, подредени според необходимото усилие:
Възпроизведете ограничението локално. Не „build-ва ли се на моята машина“ — build-вайте със същото количество memory, с което разполага builder-ът:
docker build --memory=2g --memory-swap=2g -t test .
Ако това възпроизвежда failure-а, открили сте причината и тя е свързана с memory, а не с нещо мистериозно.
Направете build-а си по-подробен. Повечето build tools по подразбиране не дават достатъчно информация за проблема, който предстои да ги прекрати.
# 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
Този ред с NODE_OPTIONS си струва да бъде изпробван самостоятелно — Node build, който спира безшумно, много често е ограничен от heap лимита, а увеличаването му решава build-ове, за които изобщо не е изведена диагностична информация.
Използвайте bisect на Dockerfile. Закоментирайте всичко след проблемната стъпка и добавете RUN echo "reached step N" markers. Груб подход, но работи, когато нищо друго не помага.
Как да намалите вероятността за този тип проблеми
Две неща са по-важни от всяка debugging техника.
Стриймвайте логовете вместо да използвате обобщени логове. Ако изходът се показва едва след приключването на build-а, build, който е прекратен, не показва нищо, защото summary-то се записва накрая. При streaming разполагате с лог до самия момент на прекратяването.
dockup logs my-project/my-api --build --follow
Използвайте етапи с имена и измерено време. „Build неуспешен“ е един bit информация. „Clone: 0.4s, build: failed after 94s“ е достатъчно, за да изключите три от четирите причини по-горе, без да четете нищо друго.
Често задавани въпроси
Защо build-ът ми изобщо не генерира логове? Защото failure-ът е настъпил, преди да се изпълнят build командите ви — обикновено при извличането на source кода или при определянето как да бъде извършен build-ът. Нито един от тези етапи не генерира build output.
Защо build-ът работи локално, но не и на платформата?
Най-често причината е memory. Вашата машина разполага с повече memory от builder-а. Възпроизведете проблема с docker build --memory=2g, за да го потвърдите, преди да търсите другаде.
Какво означава лог, който спира по средата на стъпка? Процесът е бил прекратен, вместо да завърши нормално. Двете най-вероятни причини са недостатъчно memory или build timeout, а OOM killer-ът не дава на процеса възможност да обясни какво се е случило.
Нужен ли ми е Dockerfile? Не непременно — платформите могат да разпознават често срещани типове проекти и да извършват build без него. Но неуспешният detection сам по себе си е безшумен failure без логове, затова явният Dockerfile премахва цял клас неясноти.
