Індекс журналуDockup / польова нотатка
Note / build-fails-with-no-logs

Збірка завершилася з помилкою без логів: як отримати вивід

Якщо збірка завершилася з помилкою без логів, це означає, що збій стався ще до запуску самої збірки. Дізнайтеся про чотири етапи, на яких це може статися, як їх розрізнити та як отримати вивід із кожного.

«Збірка завершилася з помилкою». Немає stack trace, помилки компілятора — взагалі жодного виводу. Збірка, що завершилася з помилкою без логів, — це найменш корисне повідомлення, яке може згенерувати платформа. Зазвичай воно означає цілком конкретну річ, яку варто зрозуміти: збій стався до запуску компонента, що створює логи.

Збірка — це не один крок. Їх чотири, і на кожному з них збій має інший вигляд.

Чотири етапи

1. Отримання вихідного коду. Платформа клонує ваш репозиторій на вказаному ref. 2. Підготовка збірки. Вона визначає, як виконати збірку — за допомогою Dockerfile, buildpack або визначеного framework. 3. Запуск збірки. Виконуються ваші команди. Це єдиний етап, який створює очікуваний вами вивід. 4. Пакування. Результат перетворюється на готовий до запуску image.

Якщо логів немає взагалі, збій стався на етапі 1 або 2. Збірка не запускалася, тому вона не могла нічого вивести.

Етап 1: код не було отримано

Симптоми — цілковита тиша та швидкий збій, зазвичай менш ніж за п’ятнадцять секунд.

Поширені причини, у такому порядку:

  • Гілки не існує. Сервіс налаштований на розгортання з master, а репозиторій перейменували на main. Це призводить до миттєвого збою майже без пояснень.
  • Доступ відкликано. Токен або інсталяцію app, які працювали минулого місяця, видалили, або репозиторій перемістили до організації, на яку дозвіл більше не поширюється.
  • Репозиторій приватний, а підключення більше не дійсне. Ситуація така сама, як вище: платформа отримує 404, а не 403, оскільки саме так Git-провайдери відповідають для приватних репозиторіїв, яких ви не можете бачити.
  • Не вдається отримати submodule. Основний репозиторій клонується, але submodule, що використовує SSH URL, не вдається отримати, оскільки в середовищі збірки немає відповідного ключа.

Швидка перевірка: чи показує платформа хеш коміту для невдалого розгортання? Якщо ні, код не було отримано, і ніщо у вашому Dockerfile тут не має значення.

Етап 2: платформа не знає, як виконати збірку

Тут також панує тиша, оскільки команду збірки ще не вибрано.

  • У вказаному конфігурацією місці немає Dockerfile. dockerfilePath вказує на шлях, який змінився.
  • Monorepo без root. Платформа перевіряє корінь репозиторію, а ваш сервіс розташований у apps/api.
  • Нічого не знайдено під час визначення. Відсутній розпізнаний manifest, тому жоден buildpack не підійшов.
  • Dockerfile не вдається розібрати. Синтаксична помилка в першому рядку призводить до збою ще до запуску будь-якого layer.

Етап 3: саме тут з’являються логи

Якщо ви бачите частковий вивід, який раптово обривається, ви перебуваєте на етапі 3. Дві найпоширеніші причини тут пов’язані не з кодом, а з ресурсами:

Нестача пам’яті. Якщо збірку завершив OOM reaper, вона не встигає нічого повідомити про це. Лог просто обривається посеред кроку. Збірки TypeScript, webpack і Vite у великих codebase регулярно стикаються з цією проблемою. Показова ознака — той самий коміт без проблем збирається на вашому ноутбуці, де пам’яті більше, ніж у builder.

Тайм-аут. Збірка, що перевищила ліміт платформи, примусово завершується. Симптом той самий: вивід обривається, а не завершується коректно.

Якщо збій стався достатньо рано, обидві ситуації виглядають як «відсутність логів».

Етап 4: збірка завершилася, але результат не вдається запакувати

Рідкісний і специфічний випадок: збірка успішна, але artefact неправильний. Наприклад, image без CMD або ENTRYPOINT, несумісна архітектура чи 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 в останньому виводі — найшвидший спосіб визначити місце збою. Якщо розгортання витратило 0,4 секунди на клонування, а потім завершилося зі статусом failed на етапі 1, причина саме там. Якщо воно витратило дев’яносто секунд на збірку, а потім зупинилося, це проблема етапу 3, найімовірніше пов’язана з пам’яттю.

Як отримати вивід, якщо його немає

Три методи, у порядку зростання складності:

Відтворіть обмеження локально. Не просто перевіряйте, «чи збирається це на моїй машині», а виконайте збірку з таким самим обсягом пам’яті, як у builder:

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

Якщо це відтворює збій, ви знайшли причину: проблема в пам’яті, а не в чомусь загадковому.

Зробіть збірку більш інформативною. Більшість 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, яка завершується без повідомлень, дуже часто впирається в heap limit, і його збільшення виправляє збірки, які не створили жодної діагностичної інформації.

Виконайте бісекцію Dockerfile. Закоментуйте все після проблемного кроку та додайте маркери RUN echo "reached step N". Метод грубий, але він працює, коли більше нічого не допомагає.

Що зменшує ймовірність таких проблем

Є дві речі, важливіші за будь-який метод налагодження.

Потокова передача логів замість підсумкових логів. Якщо вивід з’являється лише після завершення збірки, примусово завершена збірка нічого не покаже, оскільки підсумок записується наприкінці. Потокова передача означає, що під час збою ви матимете лог аж до моменту, коли він стався.

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

Етапи з назвами та вимірюванням часу. «Збірка завершилася з помилкою» — це один біт інформації. «Клонування: 0,4 с, збірка: збій через 94 с» достатньо, щоб відкинути три з чотирьох наведених вище причин, навіть не читаючи решту.

Поширені запитання

Чому моя збірка взагалі не створює логів? Тому що збій стався до запуску команд збірки — зазвичай під час отримання вихідного коду або визначення способу його збірки. Жоден із цих етапів не створює вивід збірки.

Чому локально збірка працює, а на платформі — ні? Найчастіше причина в пам’яті. На вашій машині її більше, ніж у builder. Відтворіть збірку за допомогою docker build --memory=2g, щоб підтвердити це, перш ніж перевіряти щось інше.

Що означає лог, який обривається посеред кроку? Процес було примусово завершено, а не припинено штатно. Два основні кандидати — нестача пам’яті та тайм-аут збірки. OOM killer не дає процесу можливості пояснити, що сталося.

Чи потрібен мені Dockerfile? Не обов’язково: платформи можуть визначати поширені типи проєктів і виконувати збірку без нього. Але збій під час визначення — це теж непомітна помилка без логів, тому явний Dockerfile усуває цілий клас неоднозначностей.