- Docker 이미지를 만들 때, 빌드 과정에서 사용하는 여러 패키지나 라이브러리가 최종 이미지에 포함되면 불필요하게 이미지 크기가 커질 수 있다.
- 애플리케이션을 배포하기 위한 최종 이미지는 빌드에 사용된 도구나 개발 의존성을 포함할 필요가 없으므로 이런 부분을 제거하고 필요한 파일만을 포함시키는 것이 좋다.
- 이를 해결하기 위해 Multi-stage 빌드(Multi-stage Build)를 사용한다.
Multi-stage 빌드란?
Multi-stage 빌드는 Dockerfile에서 여러 개의 스테이지를 정의하여 빌드 과정과 실행 환경을 분리하는 방법이다. 각 스테이지는 독립적으로 실행되며, 이전 스테이지에서 생성된 아티팩트를 다음 스테이지로 전달하는 방식으로 진행된다. 이 방식은 최종 이미지에 필요한 파일만 남기고 불필요한 빌드 파일이나 도구는 제외시킬 수 있어 이미지 크기를 최적화하는 데 큰 도움이 된다.
효과
- 이미지 크기 최소화
- 빌드 속도 최적화
활용 예
Vue 프로젝트를 Nginx로 배포하는 경우
- 빌드 단계 (Node.js 환경에서 Vue 애플리케이션 빌드)
- 먼저 Node.js 환경에서 Vue 애플리케이션을 빌드한다. 이때 필요한 빌드 도구와 패키지는 첫 번째 스테이지에서만 사용된다.
- 실행 단계 (Nginx로 정적 파일 서빙)
- 빌드된 정적 파일을 Nginx 서버로 복사하여 실행한다. 이때, Nginx는 빌드 도구가 아닌 정적 파일만을 서빙한다.
BEFORE (Multi-stage 적용 ❌)
# 1️⃣ 빌드 단계 (Build stage)
# Node.js 14 버전을 기반 이미지로 사용하여 애플리케이션을 빌드
FROM node:14
# 작업 디렉토리를 /app으로 설정
WORKDIR /app
# package.json과 package-lock.json을 복사하여 의존성 설치
COPY package.json package-lock.json ./
# npm install을 실행하여 필요한 패키지를 설치
RUN npm install
# 애플리케이션의 소스 파일을 컨테이너에 복사
COPY . .
# 애플리케이션의 정적 파일을 생성하기 위해 build 디렉토리 생성
RUN mkdir -p /app/static && npm run build
# 2️⃣ 프로덕션 단계 (Production stage)
# 실행 환경은 Nginx를 사용하여 빌드된 애플리케이션 서빙
FROM nginx:alpine
# Nginx 설정 파일을 컨테이너의 /etc/nginx/nginx.conf로 복사
COPY nginx.conf /etc/nginx/nginx.conf
# 빌드 후 생성된 dist 디렉토리를 참조
COPY ./dist /usr/share/nginx/html
# Nginx가 사용할 포트 80을 외부에 노출
EXPOSE 80
# 컨테이너가 실행되면 Nginx 서버를 시작하도록 명령을 설정
CMD ["nginx", "-g", "daemon off;"]
AFTER (Multi-stage 적용 ⭕️)
# 1️⃣ 빌드 단계 (Build stage)
# Node.js 14 버전을 기반 이미지로 사용하여 애플리케이션을 빌드
FROM node:14 AS build
# 작업 디렉토리를 /app으로 설정
WORKDIR /app
# package.json과 package-lock.json을 복사하여 의존성 설치
COPY package.json package-lock.json ./
# npm install을 실행하여 필요한 패키지를 설치
RUN npm install
# 애플리케이션의 소스 파일을 컨테이너에 복사
COPY . .
# 애플리케이션의 정적 파일을 생성하기 위해 build 디렉토리 생성
RUN mkdir -p /app/static && npm run build
# 2️⃣ 프로덕션 단계 (Production stage)
# 실행 환경은 Nginx를 사용하여 빌드된 애플리케이션 서빙
FROM nginx:alpine
# Nginx 설정 파일을 컨테이너의 /etc/nginx/nginx.conf로 복사
COPY nginx.conf /etc/nginx/nginx.conf
# 📍빌드 단계에서 생성된 dist 디렉토리를 Nginx의 웹 서버 디렉토리인 /usr/share/nginx/html로 복사
COPY --from=build /app/dist /usr/share/nginx/html
# Nginx가 사용할 포트 80을 외부에 노출
EXPOSE 80
# 컨테이너가 실행되면 Nginx 서버를 시작하도록 명령을 설정
CMD ["nginx", "-g", "daemon off;"]
비교
두 dockerfile간 차이를 찾기 매우 어려울 수 있다. 실제로 차이는 아주 미세하다. AS 명령어를 사용해 스테이지에 이름을 부여하고, 나중에 해당 스테이지에서 파일을 복사해 사용하는 것. 그 차이이다.
- FROM node:14 AS build: 첫 번째 FROM 명령어는 빌드 환경을 정의하고, 이 환경에서 애플리케이션을 빌드한다. AS build는 이 스테이지에 이름을 부여하는데, 나중에 COPY --from=build에서 이 스테이지에서 파일을 복사할 수 있게 해준다.
- COPY --from=build: 이 명령어는 빌드 단계에서 생성된 /app/dist 디렉토리만 복사하여 Nginx 컨테이너로 복사하고, 나머지 불필요한 파일들은 포함되지 않는다.
여담
실제 이미지로 돌려본 결과로는.. single stage의 이미지 사이즈가 더 작아서 당황스러웠다..
이유 알려주실 분...!
Multi-stage
Learn about multi-stage builds and how you can use them to improve your builds and get smaller images
docs.docker.com
'Cloud > Docker' 카테고리의 다른 글
[Docker] Node.js 경량 웹서버 컨테이너화 및 MySQL 연동 (0) | 2025.02.10 |
---|---|
[Docker] Networking (0) | 2025.02.06 |
[Docker] 볼륨(Volume)을 이용한 데이터 영속성 관리 (0) | 2025.02.05 |
[Docker] 도커를 이용한 애플리케이션 컨테이너화 및 배포 과정 (0) | 2025.02.05 |