DockerFile은 base image를 바탕으로 image를 커스터마이징한 것을 기록해 놓은 문서(파일)이다. 이 문서가 준비되면, docker build
명령어를 통해 이미지를 빌드할 수 있다. 이때, Dockerfile
내의 지시어들이 순서대로 실행되어 최종 이미지가 생성된다. Dockerfile
은 애플리케이션의 빌드와 배포 과정을 표준화하고 자동화할 수 있으며, 이는 개발의 효율성을 높이는 데 기여할 수 있다.
FROM
base image(기반 이미지)를 지정하며, 모든 Dockerfile
은 FROM
명령어로 시작해야 하며, 이는 빌드 과정의 기점이 되는 이미지(base image)를 정의한다.
FROM base image
# FROM ubuntu:18.04
LABEL
이미지에 컨테이너의 메타데이터를 추가한다.
LABEL maintainer = "name@example.com"
RUN
이미지 빌드 과정 중에 명령어를 실행하며, 주로 패키지 설치나 설정 파일 변경에 사용된다.
FROM base image
에 포함된 /bin/sh
를 사용하여 처리 ⇒ /bin/sh
로 처리하지 않는 경우 배열로 명시RUN apt-get update && apt-get install -y python
COPY
COPY <source_file> <image 내 절대경로>
컨테이너 빌드 시, 호스트(로컬)의 파일이나 디렉토리를 이미지(도커) 내부로 복사하며, 애플리케이션의 소스 코드를 이미지에 추가할 때 주로 사용된다.
/path/
와 같이 /
로 끝나는 경우 path 경로(디렉토리)를 한 번 더 감싸서 생성COPY . /app
ADD
COPY
명령어와 유사하지만, 원격 URL에서 파일을 추가하거나 로컬의 압축 파일을 압축 해제하며 파일을 추가할 수 있다.
/path/
와 같이 /
로 끝나는 경우 path 경로(디렉토리)를 한 번 더 감싸서 생성ADD https://example.com/big.tar.xz /usr/src/things/
ADD *.txt /tmp/
ADD project.tar.gz /project
ADD ./ /project
CMD
컨테이너가 시작될 때 실행할 기본 명령어를 정의하며, Dockerfile
내에서 한 번만 사용할 수 있다.
ENTRYPOINT
와 같이 사용 시 매개 변수만 전달하는 기능[출처: 유튜브 - 얄팍한 코딩사전]
CMD ["python", "./app/app.py"] # 배열 형태
ENTRYPOINT
CMD와 함께 사용하며, 명령어 지정 시 사용한다. 컨테이너가 시작될 때 실행할 명령어를 설정하며, CMD
와 함께 사용되어 애플리케이션의 실행 방식을 정의할 수 있다.
ENTRYPOINT ["python"]
CMD ["app.py"]
CMD
vsENTRYPOINT
컨테이너 실행 시 넘기는 인자를 CMD는 덮어쓰기, ENTRYPOINT는 인자로 전달
동일한 이미지로 테스트, 운영 환경 각각의 엔트리포인트를 다르게 주어 초기에 읽는 스크립트를 다르게 하여 서로 다른 동작을 지정할 수 있다.
EXPOSE
컨테이너가 리스닝할 포트를 지정한다. host와 연결만을 목적으로 하며 외부로 노출되지는 않는다. 따라서 컨테이너 실행 시 host의 포트와 매핑 과정이 필요하다.
EXPOSE 80
ENV
환경 변수를 설정하며, 애플리케이션 설정에 사용된다. 컨테이너 실행 시 -e
옵션으로 재지정(덮어쓰기)도 가능하다. RUN
, CMD
, ENTRYPOINT
에 적용가능하다.
ENV API_KEY="YOUR_API_KEY"
WORKDIR
RUN
, CMD
, ENTRYPOINT
, COPY
, ADD
명령어가 실행될 작업 디렉토리를 설정한다.
WORKDIR /app
USER
명령을 실행할 사용자를 정의하며, 웬만하면 root 외의 사용자를 지정하는 것이 보안에 좋다. 사용자가 지정된 이후의 라인에서 처리되는 CMD
, RUN
, ENTRYPOINT
는 해당 사용자의 행위로 해석된다.
FROM ubuntu
RUN mkdir -m 1777 /share
RUN touch /share/hello.txt
RUN useradd student
USER student
RUN touch /share/student.txt
CMD ls -l /share
VOLUME
볼륨 경로를 지정한다. 만약 호스트의 특정 경로와 연결하지 못한다면 docker run -v
옵션으로 볼륨을 설정한다.
도커 이미지의 빌드는 도커파일에 명시된 나열된 명령어 순으로 수행되기 때문에 도커파일의 명령어의 순서는 중요하다. 각 명령어 라인은 하나의 이미지 레이어에 대응되며, 아래 그림에서도 알 수 있듯이 이러한 레이어들이 여러 층으로 쌓여 컨테이너 이미지를 형성한다.
[출처: 도커 공식 홈페이지]
[출처: https://subicura.com/]
도커 파일 빌드 시, 베이스 이미지로부터 한층 한층 레이어를 쌓게 되는데, 이때 레이어의 명령어 수행 후 컨테이너를 종료하고 해당 시점의 파일시스템을 이미지로 저장한다. 이후 새로운 명령어가 실행되면 이전 이미지 위에 새로운 레이어가 추가되는 방식으로 최종 이미지를 완성한다.
alpine
이미지는 매우 작은 크기로 필요한 최소한의 기능만 포함한다.Dockerfile
에서 멀티 스테이지 빌드를 사용하여 빌드 단계에만 필요한 도구를 최종 이미지에서 제외시킨다. 이 방식을 사용하면 최종 이미지에는 애플리케이션 실행에 필요한 파일과 디펜던시만 포함된다. 멀티 스테이지 빌드는 여러 개의 FROM
명령어를 사용하여 구현되며, 각 스테이지는 독립적인 베이스 이미지를 가질 수 있다. 첫 번째 스테이지에서는 빌드에 필요한 도구와 소스 코드를 컴파일하는 데 필요한 작업을 수행한다. 이후 스테이지에서는 첫 번째 스테이지에서 생성된 아티팩트만을 가져와서 최종 이미지를 생성한다. 이렇게 하면 불필요한 빌드 도구나 중간 생성물을 최종 이미지에서 제외할 수 있어 이미지 크기가 상당히 줄어든다.RUN
명령어에서 && rm -rf /path/to/temporary/files
와 같이 제거하여 이미지 크기를 줄인다.RUN
, COPY
, ADD
명령어는 새로운 레이어를 생성한다. 이러한 명령어를 적절히 조합하여 가능한 한 적은 수의 레이어를 생성하도록 Dockerfile
을 최적화한다.FROM ubuntu
RUN mkdir -m 1777 /share \
&& touch /share/hello.txt \
&& useradd student
COPY
와 ADD
명령어를 신중하게 사용하기: COPY
와 ADD
는 필요한 파일만 이미지에 추가하도록 사용한다. .dockerignore
파일을 사용하여 불필요한 파일이 이미지에 포함되지 않도록 설정할 수 있다.프로젝트 진행 중 쿠버네티스 환경에서 무거운 패키지를 설치할 때마다 워커노드(2GB 메모리 용량 지님)가 다운되는 현상이 발생했다. 따라서 메모리 사용량 확인 결과 기존 백엔드 이미지로 올라간 컨테이너의 파드가 많은 용량(1GB 이상)을 차지하고 있었음을 확인하였다. 따라서 npm ci
, 경량 베이스 이미지, 멀티 스테이지 빌드를 활용하여 이미지 최적화 결과 기존 1.15GB의 이미지를 202MB까지 줄일 수 있었다.
# 1단계: 빌드 스테이지
FROM node:alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# 2단계: 런타임 스테이지
FROM node:alpine
WORKDIR /app
# 빌드 단계에서 production dependencies만 복사
COPY --from=build /app /app
EXPOSE 8080
CMD ["node", "server.js"]