도커 이미지 빌드와 빌드 컨텍스트의 이해

도커 이미지는 컨테이너를 실행하기 위한 설계도와 같습니다. 이 이미지를 생성하는 데 사용되는 핵심 도구가 바로 Dockerfile입니다. Dockerfile은 이미지를 빌드하기 위한 일련의 명령어를 담고 있는 텍스트 파일입니다. 이제 간단한 웹 서버 이미지를 Dockerfile을 사용하여 구축하는 과정을 살펴보겠습니다.

먼저, 아래와 같은 내용의 Dockerfile을 생성합니다. 이 파일은 빌드 컨텍스트의 루트 디렉토리에 위치한다고 가정합니다.

FROM nginx:alpine
COPY ./html/index.html /usr/share/nginx/html/index.html
RUN echo "웹 서버가 준비되었습니다!" > /usr/share/nginx/html/status.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

이 Dockerfile은 nginx:alpine 기본 이미지 위에 특정 파일을 복사하고, 상태 페이지를 추가하며, 포트를 노출하고, Nginx를 시작하도록 지시합니다. 이제 이 Dockerfile과 html/index.html 파일이 들어있는 디렉토리에서 다음 명령어를 실행하여 이미지를 빌드합니다.

$ docker build -t custom-web:1.0 .
Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM nginx:alpine
 ---> 2c26f0928236
Step 2/4 : COPY ./html/index.html /usr/share/nginx/html/index.html
 ---> d9a1b1c2e3f4
Step 3/4 : RUN echo "웹 서버가 준비되었습니다!" > /usr/share/nginx/html/status.html
 ---> Running in 1a2b3c4d5e6f
 ---> f5a4b3c2d1e0
Removing intermediate container 1a2b3c4d5e6f
Step 4/4 : EXPOSE 80
 ---> Running in 9a8b7c6d5e4f
 ---> 7b6a5c4d3e2f
Removing intermediate container 9a8b7c6d5e4f
Successfully built 7b6a5c4d3e2f
Successfully tagged custom-web:1.0

명령어 출력에서 각 Step이 개별적인 레이어(Layer)로 처리되고 있음을 볼 수 있습니다. 각 RUN 또는 COPY와 같은 명령은 중간 컨테이너를 생성하여 실행된 후, 그 변경 사항이 새로운 이미지 레이어로 커밋되고, 중간 컨테이너는 삭제됩니다. 이 과정을 통해 최종 이미지가 생성됩니다.

도커 빌드 컨텍스트의 중요성

위 docker build 명령어의 마지막에 있는 .(점)은 많은 초보 사용자들을 혼란스럽게 합니다. 이 .은 단순히 Dockerfile이 위치한 디렉토리를 지칭하는 것이 아니라, 빌드 컨텍스트(Build Context) 경로를 지정하는 것입니다.

도커는 클라이언트-서버 아키텍처로 동작합니다. 즉, 우리가 터미널에서 실행하는 docker 명령어는 도커 데몬(서버)에게 REST API 호출을 보내는 클라이언트일 뿐입니다. 이미지를 빌드하는 실제 작업은 도커 데몬이 수행합니다. 따라서 Dockerfile 내에서 COPY나 ADD와 같은 명령어를 사용하여 로컬 파일을 이미지 안으로 복사해야 할 때, 도커 데몬이 해당 파일에 접근할 수 있어야 합니다.

빌드 컨텍스트는 바로 이 문제를 해결합니다. docker build 명령이 실행되면, 지정된 빌드 컨텍스트 경로(예: .) 아래의 모든 파일과 디렉토리가 하나의 압축 파일로 묶여 도커 데몬으로 전송됩니다. 도커 데몬은 이 압축 파일을 해제하여 빌드에 필요한 모든 로컬 파일에 접근하게 됩니다.

예를 들어, Dockerfile에 다음과 같은 줄이 있다면:

COPY ./app/main.py /usr/src/app/

이것은 docker build 명령을 실행한 현재 디렉토리 아래의 app/main.py 파일을 복사하는 것이 아니라, 빌드 컨텍스트 경로 아래의 app/main.py 파일을 복사하는 것입니다. 따라서 COPY 명령어의 소스 경로는 항상 빌드 컨텍스트 루트를 기준으로 하는 상대 경로여야 합니다.

COPY ../data.txt /tmp/와 같이 컨텍스트 범위를 벗어나는 경로를 사용하거나, COPY /etc/config.json /etc/와 같은 절대 경로를 사용하려는 시도가 실패하는 이유도 여기에 있습니다. 도커 데몬은 빌드 컨텍스트 내의 파일만 접근할 수 있기 때문입니다. 만약 컨텍스트 외부의 파일이 필요하다면, 빌드하기 전에 해당 파일을 컨텍스트 디렉토리 안으로 복사해야 합니다.

빌드 컨텍스트의 전송 과정은 docker build 명령어 실행 시 출력되는 메시지에서도 확인할 수 있습니다:

$ docker build -t my-app:latest .
Sending build context to Docker daemon 3.072kB
...

이해하지 못하고 빌드 컨텍스트를 잘못 지정하면 예기치 않은 문제를 겪을 수 있습니다. 예를 들어, 불필요하게 큰 디렉토리(예: 전체 하드 드라이브의 루트)를 컨텍스트로 지정하면, 빌드에 필요 없는 수 기가바이트의 파일들이 도커 데몬으로 전송되어 빌드 속도가 현저히 느려지거나 실패할 수 있습니다.

효율적인 이미지 빌드를 위해서는 Dockerfile을 가능한 한 비어 있는 디렉토리나 프로젝트의 루트 디렉토리에 배치하고, 오직 이미지 빌드에 필요한 파일들만 해당 컨텍스트 디렉토리에 포함시키는 것이 좋습니다. 만약 특정 파일이나 디렉토리가 빌드 컨텍스트에 포함되지 않기를 원한다면, .dockerignore 파일을 사용하여 .gitignore와 유사한 방식으로 제외 규칙을 정의할 수 있습니다. .dockerignore에 명시된 파일들은 도커 데몬으로 전송되는 컨텍스트에서 자동으로 제외됩니다.

일반적으로 Dockerfile이라는 파일 이름을 사용하며, 이를 빌드 컨텍스트 디렉토리에 두는 것이 관례입니다. 그러나 필요에 따라 -f 플래그를 사용하여 Dockerfile의 이름이나 위치를 명시적으로 지정할 수도 있습니다. 예를 들어 docker build -f my.Dockerfile -t my-app . 와 같이 사용할 수 있습니다.

태그: docker Dockerfile Image Building Build Context Containerization

10월 9일 04:24에 게시됨