Docker COPY와 빌드 컨텍스트 — 디렉토리 중첩
CI 에서 이미지를 빌드하다 npm install 에서 실패했습니다.
이 CI 는 저장소 두 개를 각각 clone 하고, Dockerfile 을 애플리케이션 저장소가 아니라 별도 저장소에 두고 있어서, 하나는 소스 하나는 Dockerfile 을 받아 나란히 놓인 workspace 에서 빌드합니다. 여러 서비스가 비슷한 빌드 절차를 쓰다 보니 Dockerfile 을 한군데 모아 두는 쪽이 편했습니다.
ENOENT: no such file or directory, open '/service/app-v2/package.json'
WORKDIR /service/app-v2 에 package.json 이 없다는 겁니다. 소스 저장소에는 분명히 있으니
이미지 안으로 들어가는 과정에서 위치가 달라졌다는 얘기가 됩니다.
빌드 컨텍스트 루트에 통째로 든 소스 디렉토리
docker build 는 지정한 디렉토리를 통째로 데몬에 넘기고 Dockerfile 의 COPY 는 그 안에서만
파일을 찾고, 이 디렉토리를 빌드 컨텍스트라고 합니다. COPY 가 무엇을 어디로 넣는지는 컨텍스트
루트에 뭐가 있느냐에 달렸습니다.
CI workspace 는 저장소 두 개를 각각 clone 해서 루트에 나란히 두는 구조였습니다.
<빌드 컨텍스트 루트>/
├── app-v2/ ← 소스 저장소 (여기 package.json 있음)
│ ├── package.json
│ └── src/
├── dockerfile-repo/ ← Dockerfile 저장소
└── Dockerfile
빌드 컨텍스트가 이 루트고 Dockerfile 에는 이렇게 적혀 있었습니다.
COPY . /service/app-v2
COPY . 는 빌드 컨텍스트 전체를 디렉토리 구조를 유지한 채로 복사하니, 결과가 이렇게 됩니다.
/service/app-v2/
├── app-v2/ ← 소스 디렉토리가 한 겹 더 생김
│ ├── package.json
│ └── src/
├── dockerfile-repo/ ← 불필요한 것까지 복사
└── Dockerfile
package.json 은 /service/app-v2/app-v2/ 안에 있고 WORKDIR 은 /service/app-v2 라서,
거기서 npm install 을 돌리면 한 겹 아래 있는 package.json 이 안 보입니다. 파일이 안 복사된
게 아니라 한 층 깊이 들어간 겁니다.
해결 — COPY 대상 명시로 중첩 제거
. 대신 복사할 디렉토리를 지정했습니다.
COPY app-v2 /service/app-v2
소스에 디렉토리를 적으면 그 디렉토리 안의 내용이 목적지에 들어가서, 디렉토리 자체가 아니라 안쪽이 옮겨지고 한 겹이 사라집니다.
/service/app-v2/
├── package.json ← 원하는 위치
└── src/
WORKDIR 에서 package.json 이 보이고 dockerfile-repo 같은 필요 없는 것도 안 따라옵니다.
COPY의 두 형태
같은 목적지를 적어도 소스를 어떻게 쓰느냐에 따라 결과가 다릅니다.
COPY app-v2 /service/app-v2
# app-v2/ 안의 파일들이 /service/app-v2/ 로
# → /service/app-v2/package.json
COPY . /service/app-v2
# 컨텍스트 전체가 구조 유지한 채로
# → /service/app-v2/app-v2/package.json (한 겹 더)
COPY . 는 빌드 컨텍스트 루트에 복사할 것만 딱 있을 때 맞는 형태라, 저장소 하나를 clone 하고 그
루트에서 빌드하는 흔한 구조에서는 이게 잘 돕니다. 여러 저장소를 한 workspace 에 clone 해서 쓰는
CI 라면 루트에 다른 디렉토리가 섞여 있으니 무엇을 복사할지 명시하는 쪽이 맞습니다.
파일이 없는 것과 한 겹 아래 있는 것은 로그에 똑같이 ENOENT 로 찍히지만 원인이 다릅니다. COPY
결과가 예상과 다르면 이미지 안에서 ls 로 목적지를 확인하면 바로 나옵니다.
댓글 0