Docker COPY와 빌드 컨텍스트 — 디렉토리 중첩

CI·CD2분조회

Dockerfile 을 애플리케이션 저장소가 아니라 별도 저장소에 두고, CI 에서 둘을 각각 clone 해서 이미지를 빌드하는 구조였습니다. 그 CI 에서 npm install 이 실패했습니다.

ENOENT: no such file or directory, open '/service/app-v2/package.json'

WORKDIR /service/app-v2package.json 이 없다는 겁니다. 그런데 소스에는 분명히 있었습니다.

빌드 컨텍스트 루트에 통째로 든 소스 디렉토리

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

COPY app-v2 /service/app-v2 는 빌드 컨텍스트의 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 . 는 빌드 컨텍스트 루트에 복사하려는 것만 딱 있을 때 맞습니다. 루트에 다른 디렉토리가 섞여 있으면 그것까지 다 들어가고, 소스가 한 겹 중첩됩니다.

빌드 컨텍스트가 저장소 하나의 루트면 COPY . 로 충분합니다. 여러 저장소를 한 workspace 에 clone 해서 쓰는 CI 구조라면, 무엇을 복사할지 명시하는 게 안전합니다.

  1. 불러오는 중