Docker build не стига до частна зависимост: проверете това
Частен пакет, който хостът изтегля, може да се провали в самата сборка. Назовете builder-а, докажете маршрута и монтирайте токена като тайна за сборката.
Частен пакет, който се решава без проблем на вашата машина, може да се провали с 404 или с изтичане на времето вътре в сборката, а Dockerfile рядко е причината. Стъпката на сборката работи в различно мрежово пространство, с различен изглед на DNS и различно хранилище на идентификационни данни от обвивката, в която сте тествали. Мини през тези проверки по ред и спри на първата, която се провали, защото всяка следваща предполага, че предишната е минала. Всяка стъпка назовава командата, която решава въпроса.
Започнете с името на builder-а, не с Dockerfile
BuildKit може да е локален, в контейнер или на друга машина, и всяко от тези има собствена мрежа по подразбиране. Изпълнете docker buildx ls и прочетете възела, отбелязан със звездичка, преди да промените каквото и да е. Драйвер docker-container или облачен builder не споделя частния маршрут на вашия лаптоп само защото сте въвели командата там. Ако активният builder е отдалечен, всяка останала проверка трябва да се изпълни на този хост, а не на вашата работна станция.
Builder-ът носи и свои настройки на резолвера и променливи на прокси. И двете се четат по време на сборката и не се наследяват от терминала, който я е стартирал.
Докажете мрежовия път от вътрешността на стъпка от сборката
Напишете временен етап, който решава хоста на зависимостта и отваря връзка към него. Това е единственият тест, който отговаря на въпроса за builder-а, а не за вашата обвивка.
FROM alpine:3.20
RUN apk add --no-cache curl bind-tools && getent hosts registry.example.internal && curl -sS -o /dev/null -w "%{http_code}
" https://registry.example.internal/simple/
Съберете този файл със същия builder, мрежов режим и монтирания на тайни, които ползва реалната ви сборка: docker buildx build --builder remote0 -f Dockerfile.probe --progress=plain .. Ако getent не отпечата нищо, проблемът е в разрешаването на имена и никакви идентификационни данни няма да го поправят. Ако името се решава, но curl съобщи за изтичане на времето, пречката е маршрутът или политиката за изход пред регистъра.
Изтрийте сондата, щом отговори. Това е диагностичен етап и не бива никога да бъде маркиран или изпращан.
Монтирайте идентификационните данни, вместо да ги копирате
След като пътят работи, удостоверяването е следващият провал в редицата. Монтирането на тайна в BuildKit дава файл на една стъпка RUN без да го записва в слой — това е нужно на инструмента за зависимости и това не бива да остава в образа.
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci --omit=dev
Тайната трябва да се подаде отвън на Dockerfile: docker buildx build --secret id=npmrc,src=/home/me/.npmrc .. Файлът остава при клиента и никога не влиза в историята на образа. А ARG или ENV със същия токен не вършат тази работа, защото и двете се записват в конфигурацията на образа.
Образ без root се нуждае от собствен целеви път, например /home/app/.npmrc или еквивалентния файл за pip, Maven или Cargo. Целта на монтирането трябва да съвпада с потребителя, който изпълнява командата, иначе инструментът ще съобщи за липсващ конфигурационен файл, който всъщност е налице.
Проверете какво оставя инструментът за зависимости в образа
Временното монтиране държи идентификационните данни извън слоя, но не спира инструмента да запише нещо друго. Мениджърите на пакети оставят lock файлове, кеш директории и понякога решената регистърна връзка.
Четете готовата файлова система, вместо да вярвате на Dockerfile. docker history --no-trunc image:tag изброява всяка команда на слой, а docker run --rm image:tag sh -c "ls -a /root" показва какво е оцеляло в работещия контейнер. И дори когато файлът е само монтиран, токен, записан в ред от лога по време на npm ci пак е изтичане.
Прочетете и лога на сборката, защото --progress=plain отпечатва изхода на командите дословно. Ако токенът се появи там, сменете го преди всичко друго и го подавайте чрез файл, който инструментът чете тихо.
Уверете се, че образът за изпълнение вече не се нуждае от частния път
Сборката и работещото приложение са двама различни консуматори на една и съща зависимост и само първият се нуждае от частния маршрут. Готовият образ трябва да стартира, да решава зависимостите си по време на изпълнение и да обслужва трафик без идентификационните данни от сборката.
Тествайте го там, където частният регистър е недостъпен, или просто стартирайте контейнера с --network none и потвърдете, че се вдига без монтираната тайна. Контейнер, който стартира само при наличието на тайната от сборката, все още е свързан със средата на сборка.
Ако контейнерът наистина се нуждае от регистъра по време на изпълнение, това е отделно решение за достъп и мястото му е в хранилището за тайни на оркестратора, а не в сборката. Документирайте двата пътя отделно, за да не се опитва някой да поправи изтегляне по време на изпълнение с флаг за сборка. Когато и петте проверки минат, първоначалният симптом има назована причина на всяко ниво, вместо догадка за мрежата на Docker.