내 컴퓨터에서는 문제없이 해석되는 사설 패키지가 빌드 안에서는 404나 시간 초과로 실패할 수 있으며, Dockerfile이 원인인 경우는 드뭅니다. 빌드 단계는 다른 네트워크 네임스페이스에서 실행되고, 테스트한 셸과는 DNS 보기도 자격 증명 저장소도 다릅니다. 다음 확인을 순서대로 진행하고 처음 실패한 지점에서 멈추세요. 이후 확인은 앞의 확인이 통과했음을 전제로 하기 때문입니다. 각 단계는 판가름하는 명령을 제시합니다.

Dockerfile이 아니라 빌더를 먼저 특정하세요

BuildKit은 로컬, 컨테이너 안, 다른 머신 어디서든 동작하며 각각 기본 네트워크가 다릅니다. 먼저 docker buildx ls 를 실행하고 무엇이든 바꾸기 전에 별표가 붙은 노드를 읽으세요. 어떤 docker-container 드라이버나 클라우드 빌더도 명령을 거기서 입력했다는 이유만으로 노트북의 사설 경로를 공유하지는 않습니다. 활성 빌더가 원격이라면 남은 확인은 모두 워크스테이션이 아니라 그 호스트에서 실행해야 합니다.

빌더는 자체 리졸버 설정과 프록시 변수도함께 지닙니다. 둘 다 빌드 시점에 읽히며 빌드를 시작한 터미널에서 상속되지 않습니다.

빌드 단계 안에서 네트워크 경로를 증명하세요

의존성 호스트를 해석하고 그쪽으로 연결을 여는 일회용 스테이지를 작성하세요. 빌더 쪽에서 질문에 답하는 유일한 시험이며, 셸 쪽의 답이 아닙니다.

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/

실제 빌드가 쓰는 것과 같은 빌더, 네트워크 모드, 시크릿 마운트로 그 파일을 빌드하세요: 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용 동등한 파일입니다. 마운트 대상은 명령을 실행하는 사용자와 일치해야 하며, 그렇지 않으면 실제로 있는 설정 파일이 없다고 도구가 보고합니다.

의존성 도구가 이미지에 남기는 것을 감사하세요

임시 마운트는 자격 증명을 레이어 밖에 두지만, 도구가 다른 것을 쓰는 것까지 막지는 못합니다. 패키지 관리자는 잠금 파일, 캐시 디렉터리, 때로는 해석된 레지스트리 URL을 남깁니다.

Dockerfile을 믿지 말고 완성된 파일 시스템을 읽으세요. docker history --no-trunc image:tag 은 레이어 명령을 모두 나열하고, docker run --rm image:tag sh -c "ls -a /root" 은 실행 중인 컨테이너에 무엇이 남았는지 보여줍니다. 파일을 마운트만 했더라도 로그 줄에 찍힌 토큰은 npm ci 동안에도 그대로 유출입니다.

빌드 로그도 읽으세요. --progress=plain 은 명령 출력을 그대로 인쇄합니다. 토큰이 거기 나타나면 무엇보다 먼저 교체하고, 도구가 조용히 읽는 파일을 통해 전달하세요.

실행 시 이미지에 사설 경로가 더 이상 필요 없는지 확인하세요

빌드와 실행 중인 애플리케이션은 같은 의존성의 서로 다른 소비자이며, 사설 경로가 필요한 쪽은 전자뿐입니다. 완성된 이미지는 빌드 시 자격 증명 없이도 기동하고 실행 시 의존성을 해석하며 트래픽을 처리해야 합니다.

사설 레지스트리에 닿을 수 없는 곳에서 시험하거나, 컨테이너를 그냥 --network none 로 기동해 마운트한 시크릿 없이 부팅되는지 확인하세요. 빌드 시크릿이 있을 때만 기동하는 컨테이너는 아직 빌드 환경에 묶여 있습니다.

컨테이너가 실행 시 레지스트리를 정말 필요로 한다면 그것은 별개의 접근 결정이며, 빌드가 아니라 오케스트레이터의 시크릿 저장소에 속합니다. 두 경로를 따로 문서화해 누구도 빌드 플래그로 실행 시 풀을 고치려 하지 않게 하세요. 다섯 가지 확인이 모두 통과하면 원래 증상은 각 계층마다 이름 붙은 원인을 갖게 되고, Docker 네트워크에 대한 추측은 사라집니다.

관련 글