Dockerfile에 부모 디렉토리의 파일을 복사 하는 방법

computerphilosopher·2021년 5월 2일
14

문제 상황

Dockerfile이 위치한 디렉토리의 부모 디렉토리를 상대 경로로 참조 해야 할 때가 있다. 이런 상황은 서로 다른 역할을 하는 여러 개의 작은 모듈을 한 저장소에서 관리할 때 많이 발생한다.

예를 들어 A, B, C 모듈을 한 저장소에서 관리하고, 모두 공통의 설정 파일인 settings.json을 참조한다고 가정하자. 프로젝트의 디렉토리 구조는 다음과 같다.

  • /
    • settings.json
    • A
      • Dockerfile
      • ...
    • B
      • Dockerfile
      • ...
    • C
      • Dockerfile
      • ...

A, B, C는 각각의 Dockerfile을 따로 가지고 있고, 모두 부모 디렉토리의 settings.json을 컨테이너 이미지 안으로 복사해야 한다. 복사 명령어를 COPY ../settings.json . 으로 작성하면 될 것 같지만 'not found' 에러와 함께 실패한다.

Dockerfile보다 상위 디렉토리에 위치한 파일을 COPY 할 수 없는 이유

엄밀히 말하면 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

1개의 댓글

comment-user-thumbnail
2023년 10월 12일

덕분에 문제 해결했습니다 너무 감사합니다~

답글 달기