Dockerfile이 위치한 디렉토리의 부모 디렉토리를 상대 경로로 참조 해야 할 때가 있다. 이런 상황은 서로 다른 역할을 하는 여러 개의 작은 모듈을 한 저장소에서 관리할 때 많이 발생한다.
예를 들어 A, B, C 모듈을 한 저장소에서 관리하고, 모두 공통의 설정 파일인 settings.json을 참조한다고 가정하자. 프로젝트의 디렉토리 구조는 다음과 같다.
A, B, C는 각각의 Dockerfile을 따로 가지고 있고, 모두 부모 디렉토리의 settings.json을 컨테이너 이미지 안으로 복사해야 한다. 복사 명령어를 COPY ../settings.json .
으로 작성하면 될 것 같지만 'not found' 에러와 함께 실패한다.
엄밀히 말하면 Dockerfile의 부모 디렉토리에 위치한 파일을 복사하지 못하는 것이 아니라, build context 바깥의 파일을 복사할 수 없는 것이다. build context란 Docker daemon이 이미지를 빌드할 때 참조하는 경로(디렉토리 또는 URL)이다. docker build 명령어를 실행할 때 인자로 넘긴다.
빌드 명령을 실행하면 도커 클라이언트는 build context 안의 파일을 tar로 압축해 Docker daemon에게 넘긴다. docker daemon은 build context 바깥에 있는 파일을 가지고 있지 않기 때문에 COPY 명령을 수행할 수 없는 것이다. docker build의 출력을 확인해보면, FROM 명령어를 수행하기 전에 build context를 전송하는 것을 확인할 수 있다.
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 89B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 1.2s
=> [internal] load build context 0.0s
=> => transferring context: 1.26kB 0.0s
=> CACHED [1/3] FROM docker.io/library/alpine@sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f 0.0s
=> [2/3] COPY . . 0.0s
=> [3/3] RUN echo "Hello, world!"; 0.3s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:c24c0038da5b3f294287fb9e553286237163f82434bbc50251f5c546f023c9e8 0.0s
=> => naming to docker.io/library/test
보통 다음과 같은 명령어를 기계적으로 사용하기 때문에 'Dockerfile보다 상위에 있는 파일은 사용할 수가 없다.'라고 생각하게 되는 것이다.
docker build --tag myapp:0.0.1 .
이 명령어는 Dockerfile의 경로와 build context 모두 현재 디렉토리라고 전제하고 있다.
첫번째 방법은 빌드 명령을 실행할 때 build context를 부모 디렉토리로 지정하는 것이다. 이때 -f(--file)
옵션을 주어 Dockerfile의 경로를 특정해야 한다. 도커 클라이언트는 Dockerfile의 경로를 따로 지정하지 않으면 <build context>/Dockerfile
로 가정하기 때문이다.
# 부모 디렉토리에서 실행
cd ..
docker build -t A:0.0.1 -f A/Dockerfile .
docker build -t B:0.0.1 -f B/Dockerfile .
docker build -t C:0.0.1 -f C/Dockerfile .
두 번째 방법은 부모 디렉토리의 파일을 복사해놓은 베이스 이미지를 사용하는 것이다. 부모 디렉토리에 다음과 같이 Dockerfile을 작성하고 base라는 이름으로 빌드한다.
# 베이스 이미지
FROM alpine
COPY ./settings.json .
자식 디렉토리에 위치한 Dockerfile의 FROM 이미지를 베이스 이미지로 지정하면 부모 디렉토리의 파일이 이미지에 포함되어 있다.
# 자식 이미지
FROM base AS builder
...
부모의 Dockerfile을 각각 작성해야 하는 면이 번거롭지만 전체 작성한 코드의 양은 오히려 줄어든다. 첫번째 방법에서는 A, B, C 모듈의 Dockerfile이 각각 COPY 명령을 따로 수행해야 하지만 이 방법에서는 베이스 이미지에서 한 번 실행하기만 하면 되기 때문이다. 모듈들이 공통으로 사용하는 파일이나 패키지가 많다면 이 방법을 사용하는 것이 간편하다. 첫번째 방법보다 명령어 옵션이 단순해지는 장점도 있다.
https://docs.docker.com/engine/reference/commandline/build
https://stackoverflow.com/questions/24537340/docker-adding-a-file-from-a-parent-directory
덕분에 문제 해결했습니다 너무 감사합니다~