Un paquet privé qui se résout sans problème sur votre machine peut échouer dans le build avec un 404 ou un délai d’attente dépassé, et le Dockerfile en est rarement la cause. L’étape de build s’exécute dans un autre espace de noms réseau, avec une autre vue du DNS et un autre magasin d’identifiants que le shell dans lequel vous avez testé. Passez ces contrôles dans l’ordre et arrêtez-vous au premier qui échoue, car chaque contrôle suivant suppose que le précédent a réussi. Chaque étape nomme la commande qui tranche.

Commencez par nommer le builder, pas le Dockerfile

BuildKit peut être local, conteneurisé ou tourner sur une autre machine, et chacun a son propre réseau par défaut. Lancez docker buildx ls et lisez le nœud marqué d’un astérisque avant de modifier quoi que ce soit. Un docker-container pilote ou un builder cloud ne partage pas la route privée de votre portable simplement parce que vous y avez tapé la commande. Si le builder actif est distant, chaque contrôle restant doit s’exécuter contre cet hôte et non contre votre poste de travail.

Le builder porte aussi sa propre configuration de résolveur et ses variables de proxy. Les deux sont lues au moment du build et ne sont pas héritées du terminal qui l’a lancé.

Prouvez le chemin réseau depuis l’intérieur d’une étape de build

Écrivez une étape jetable qui résout l’hôte de la dépendance et ouvre une connexion vers lui. C’est le seul test qui répond à la question pour le builder et non pour votre shell.

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/

Construisez ce fichier avec le même builder, le même mode réseau et les mêmes montages de secrets que votre build réel : docker buildx build --builder remote0 -f Dockerfile.probe --progress=plain .. Si getent n’affiche rien, la panne vient de la résolution de noms et aucun identifiant ne la corrigera. Si le nom se résout mais que curl signale un délai dépassé, l’obstacle est la route ou la politique de sortie devant le registre.

Supprimez la sonde dès qu’elle a répondu. C’est une étape de diagnostic et elle ne devrait jamais être étiquetée ni poussée.

Montez l’identifiant au lieu de le copier

Une fois la route opérationnelle, l’authentification est l’échec suivant dans la file. Un montage de secret BuildKit expose un fichier à une seule étape RUN sans l’écrire dans une couche, ce dont l’outil de dépendances a besoin et ce que l’image ne doit pas conserver.

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc     npm ci --omit=dev

Le secret doit être fourni depuis l’extérieur du Dockerfile : docker buildx build --secret id=npmrc,src=/home/me/.npmrc .. Le fichier reste chez le client et n’entre jamais dans l’historique de l’image. Un ARG ou une ENV contenant le même jeton ne font pas ce travail, car les deux sont enregistrés dans la configuration de l’image.

Une image sans root a besoin de son propre chemin cible, par exemple /home/app/.npmrc ou le fichier équivalent pour pip, Maven ou Cargo. La cible du montage doit correspondre à l’utilisateur qui exécute la commande, sinon l’outil signalera un fichier de configuration manquant alors qu’il est bien là.

Auditez ce que l’outil de dépendances laisse dans l’image

Un montage temporaire garde l’identifiant hors d’une couche, mais il n’empêche pas l’outil d’écrire autre chose. Les gestionnaires de paquets laissent des fichiers de verrouillage, des répertoires de cache et parfois l’URL du registre résolue.

Lisez le système de fichiers final au lieu de faire confiance au Dockerfile. docker history --no-trunc image:tag liste chaque commande de couche, et docker run --rm image:tag sh -c "ls -a /root" montre ce qui a survécu dans le conteneur en cours d’exécution. Même lorsque le fichier n’a été que monté, un jeton écrit dans une ligne de journal pendant npm ci reste une fuite.

Lisez aussi le journal de build, car --progress=plain imprime la sortie des commandes mot pour mot. Si le jeton y apparaît, faites-le tourner avant tout le reste et faites-le passer par un fichier que l’outil lit discrètement.

Vérifiez que l’image d’exécution n’a plus besoin du chemin privé

Le build et l’application en cours d’exécution sont deux consommateurs différents de la même dépendance, et seul le premier a besoin de la route privée. Une image terminée doit démarrer, résoudre ses dépendances d’exécution et servir du trafic sans l’identifiant de build.

Testez-le là où le registre privé est injoignable, ou démarrez simplement le conteneur avec --network none et confirmez qu’il démarre sans le secret monté. Un conteneur qui ne démarre que lorsque le secret de build est présent reste couplé à l’environnement de build.

Si le conteneur a réellement besoin du registre à l’exécution, c’est une décision d’accès distincte, et elle relève du magasin de secrets de l’orchestrateur et non du build. Documentez les deux chemins séparément pour que personne ne tente de réparer un pull d’exécution avec un drapeau de build. Quand les cinq contrôles passent, le symptôme d’origine a une cause nommée à chaque couche au lieu d’une supposition sur le réseau Docker.

Articles similaires