본문 바로가기
빌드노트 검색

빌드노트

파일은 있는데 Docker COPY가 not found라면 마지막 빌드 경로를 본다

Dockerfile을 고르는 -f와 COPY 입력 범위를 정하는 마지막 빌드 경로를 구분한다. 같은 파일의 성공·not found 비교를 바탕으로 적용되는 ignore 파일을 찾고 필요한 경로만 허용하는 방법을 설명한다.

저장소에 payload/probe.txt가 있는데 Docker 빌드의 COPY는 not found로 끝난다. 먼저 빌드 명령의 마지막 경로와 그 컨텍스트에 적용되는 ignore 파일을 확인한다. 일반적인 로컬 파일 COPY는 빌드 컨텍스트의 루트를 기준으로 원본을 찾는다. -f로 고른 Dockerfile의 폴더가 그 기준을 정하지는 않는다.

같은 Dockerfile도 마지막 경로에 따라 결과가 달라진다

명령을 실행하는 현재 폴더 아래에 docker/Dockerfile과 payload/probe.txt가 있고, Dockerfile에 다음 복사 지시가 있는 경우를 보자.

COPY payload/probe.txt /probe.txt

Docker CLI와 실행 중인 Linux 엔진을 사용하는 빌드다. 아래 명령의 -f docker/Dockerfile은 읽을 Dockerfile을 고르고, 마지막 docker 또는 .은 빌드가 접근할 파일 범위인 컨텍스트를 정한다. 상대 경로는 명령을 실행하는 현재 폴더에서 읽어야 한다. 빌드 컨텍스트 (새 탭에서 열림), -f 옵션 (새 탭에서 열림)

다음은 같은 입력에 대해 마지막 경로만 바꾼 명령이다. 첫 명령은 이 구조에서 의도한 실패다. 실제 프로젝트에 적용할 때는 컨텍스트를 넓히기 전에, 아래에서 설명하는 ignore 규칙으로 필요한 파일만 포함하도록 범위를 정한다.

docker build --progress=plain -f docker/Dockerfile docker
docker build --progress=plain -f docker/Dockerfile .

별도 합성 폴더에서 직접 실행했을 때 첫 명령은 종료 코드 1로 끝났고, 실패 로그에는 다음 문자열이 있었다. 빌드마다 달라지는 checksum 참조 식별자는 생략했다.

"/payload/probe.txt": not found

첫 명령이 선택한 컨텍스트는 docker/다. 그 바깥에 있는 payload/는 입력에 없다. 마지막 경로를 .으로 바꾼 두 번째 명령은 종료 코드 0으로 끝났다. 성공한 이미지에서 꺼낸 파일의 SHA-256도 원본과 같았다. 같은 파일과 Dockerfile을 두고 마지막 경로 하나만 바꿔 성공 여부가 달라진 것이다. 이 오류의 /payload/probe.txt는 컨텍스트에서 찾던 경로이며, 호스트 디스크의 루트를 검색했다는 뜻이 아니다.

내 명령을 확인할 때도 마지막 경로를 먼저 펼쳐 본다. 그 폴더 아래에 COPY 원본 경로가 실제로 있는가? 없다면 필요한 파일을 담는 가장 좁은 공통 상위 폴더를 컨텍스트로 고르고, COPY 경로를 그 루트에 맞춘다. COPY ../payload/probe.txt ...로 컨텍스트 밖을 읽는 방법은 사용할 수 없다. 공식 문서상 밖으로 나가는 ../는 원본 경로에서 제거된다. 이 동작은 문서로 확인했다. COPY 원본 경로 규칙 (새 탭에서 열림)

경로가 맞다면 적용되는 ignore 파일을 확인한다

컨텍스트 안에 파일이 있어도 ignore 규칙에 걸리면 COPY 입력에서 빠진다. 같은 합성 폴더의 루트 .dockerignore에 payload/probe.txt 한 줄을 넣고 두 번째 명령을 다시 실행하자, 종료 코드 1과 같은 not found가 나왔다. 이 환경에서는 CopyIgnoredFile 경고도 함께 표시됐다. 따라서 오류 문구 하나만으로 컨텍스트 밖인지 ignore 제외인지 구별할 수는 없다. CopyIgnoredFile 설명 (새 탭에서 열림)

기본으로 읽는 파일은 컨텍스트 루트의 `.dockerignore`다. 다만 -f docker/Dockerfile을 쓸 때 docker/Dockerfile.dockerignore가 있으면 그 전용 파일이 루트 규칙보다 우선한다. 루트 파일을 고쳤는데 결과가 같다면 Dockerfile 옆의 전용 파일도 확인한다. 전용 파일 우선순위는 공식 문서에서 확인했으며 이번 실행에서는 전용 파일을 만들지 않았다. ignore 파일 위치와 우선순위 (새 탭에서 열림)

ignore 파일을 통째로 비우기보다 필요한 경로만 다시 허용한다. 이 작은 입력에서 파일 하나만 허용한 규칙은 다음과 같다. 실제 앱은 빌드에 필요한 다른 파일도 허용해야 하므로 이 두 줄을 그대로 덮어쓰지 말고 자신의 규칙에 맞춘다.

**
!payload/probe.txt

**로 하위 경로까지 제외하고 !로 필요한 파일만 예외로 둔다. 같은 파일에 여러 규칙이 맞으면 마지막 규칙이 결정하므로, 뒤에 다시 제외하는 줄이 없는지도 확인한다. 패턴과 예외 규칙 (새 탭에서 열림)

이 규칙으로 두 번째 빌드는 다시 성공했다. 같은 규칙을 적용한 별도 COPY . /snapshot/ 이미지에서도 일반 파일은 payload/probe.txt 하나였고, 합성 .env와 같은 폴더의 불필요한 파일은 없었다. 이는 복사 가능한 파일의 범위를 확인한 결과다. 빌드 정의에 필요한 Dockerfile과 ignore 파일의 전달까지 사라졌다는 의미는 아니다.

수정 뒤에도 실패하면 현재 명령의 컨텍스트 루트, 그 루트 기준 원본 경로, 실제 적용되는 ignore 파일을 다시 대조한다. 파일이 없다는 빌드 오류가 아니라 엔진 연결 오류라면 이 경로 비교의 결과로 판단할 수 없다. 비밀 파일이 빌드 중 필요하다면 전체 폴더를 허용하기 전에 빌드 secret 전달 방식 (새 탭에서 열림)을 검토한다.

2026년 10월 21일 KST에 Windows 11·Docker Desktop 4.92.0의 Linux Engine 29.8.0·Buildx 0.37.1에서 확인했다. 운영 소스 대신 FROM scratch와 합성 텍스트를 썼으며, 본문의 빌드 명령도 격리된 폴더에서 실행했다. 확인 범위는 로컬 디렉터리 컨텍스트의 일반 COPY다. ../ 처리와 전용 ignore 우선순위는 문서 확인에 한정하며, Compose·원격 빌더·COPY --from·secret mount는 실행하지 않았다.

← 전체 글 보기