# 도커 멀티스테이징

도커 멀티스테이징 빌드(Multi-stage Build)는 하나의 Dockerfile 내에서 여러개의 빌드 단계를 정의하여, 최종 이미지에 필요한 부분만 포함하도록 설계하는 기능입니다. 이를 통해 빌드 과정에서 필요한 도구나 라이브러리를 최종 이미지에 포함시키지 않아도 되므로, 효율적이고 관리하기 쉬운 컨테이너 이미지를 생성할 수 있습니다.

전통적인 도커 빌드 방식에서는 애플리케이션을 빌드하기 위한 모든 도구와 라이브러리를 포함한 단일 이미지를 생성합니다. 이 경우, 최종 이미지에 불필요한 빌드 도구들이 포함되어 이미지 크기가 커지고 보안 취약점이 증가할 수 있습니다.

멀티스테이지 빌드는 여러 개의 빌드 단계를 정의하여, 각 단계에서 필요한 작업을 수행한 후, 최종 단계에서는 필요한 아티팩트(예: 바이너리 파일, 컴파일된 코드 등)만을 가져와서 경량의 최종 이미지를 생성합니다. 이를 통해 빌드 환경과 실행 환경을 분리할 수 있습니다.

# 멀티스테이징의 특징 및 목적

  1. 효율성
    • 빌드와 실행을 위한 별도의 스테이지를 정의하여 빌드 중간에 생성되는 불필요한 파일이나 의존성을 최종 이미지에서 제외할 수 있습니다.
  2. 이미지 크기 감소
    • 최종 실행 단계에서는 애플리케이션을 실행하는 데 필요한 파일만 포함하므로 최종 이미지의 크기가 줄어듭니다.
  3. 보안 강화
    • 빌드 도구나 중간 단계에서 필요한 파일이 최종 이미지에 포함되지 않으므로 보안 취약점을 줄일 수 있습니다.
  4. 유지보수성 향상
    • Dockerfile이 단계별로 명확히 분리되므로 더 읽기 쉽고 관리하기 용이합니다.

# 장점

  • 이미지 크기 최소화
    • 최종 이미지에 빌드 도구가 포함되지 않아 더 작고 경량화됩니다.
    • 예: node:20-alpine과 같이 경량 이미지만 포함 가능.
    • 최종 이미지는 최소한의 파일만 포함하므로 저장소 및 전송 비용 절감
    • 빌드 환경과 실행 환경을 분리하여 불필요한 의존성을 제거.
  • 보안 강화
    • 빌드 과정에서 사용된 민감한 정보(예: 환경 변수, 빌드 도구 등)가 실행 이미지에 포함되지 않음.
  • 가독성 및 유지보수성
    • 단계별로 빌드 과정을 명확히 정의하므로 Dockerfile이 더 이해하기 쉬워짐.
    • 빌드와 실행 환경을 분리하여 각각의 단계를 독립적으로 관리 가능.
  • 다양한 환경 지원 및 성능 최적화
    • 동일한 Dockerfile로 개발, 테스트, 프로덕션 환경에 맞는 이미지를 생성 가능.
    • 중간 빌드 단계에서 캐싱을 활용할 수 있어 빌드 속도 향상.

# 사용 시 주의점

  1. 캐시 활용
    • 빌드 과정에서 동일한 명령어 순서를 유지하여 캐시를 최대한 활용해야 효율적입니다.
  2. 최소화된 베이스 이미지 선택
    • 최종 스테이지에서 불필요한 용량을 줄이기 위해 alpine 같은 경량 이미지를 사용하는 것이 좋습니다.
  3. 중간 파일 제거
    • 빌드 단계에서 생성된 불필요한 파일이 최종 이미지로 넘어가지 않도록 관리.

# 멀티스테이징 빌드 예제

# 동작 방식

  1. 여러 스테이지 정의
    • FROM 명령어를 여러 번 사용하여 각 스테이지를 정의합니다.
    • 이전 단계에서 생성된 파일을 다음 단계로 복사(COPY)하여 재사용합니다.
  2. 최종 단계 선택
    • 마지막 스테이지에서 필요한 아티팩트(예: 실행 파일, 설정 파일)만 포함하여 최종 이미지를 만듭니다.

# Go 애플리케이션 예제

# 1. 빌드 스테이지
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# 2. 실행 스테이지
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
  • builder 스테이지:
    • Go 애플리케이션을 빌드하는 데 필요한 Go 런타임과 빌드 도구를 포함합니다.
    • 결과물인 실행 파일(myapp)만 생성됩니다.
  • 최종 스테이지:
    • 빌드 결과물(myapp)만 포함하고, Go 런타임이나 빌드 도구는 제외하여 이미지 크기를 최소화합니다.

# Node.js 애플리케이션 예제

# 1. 빌드 스테이지
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 2. 실행 스테이지
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
  • builder 스테이지:
    • Node.js와 npm을 사용하여 애플리케이션을 빌드합니다.
    • 빌드 결과물은 /app/dist 디렉토리에 생성됩니다.
  • 최종 스테이지:
    • nginx를 사용하여 정적 파일을 서빙합니다.
    • Node.js와 npm은 최종 이미지에 포함되지 않습니다.

# Java 애플리케이션 예제

# 1. 빌드 스테이지
FROM maven:3.9 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn package

# 2. 실행 스테이지
FROM openjdk:17
WORKDIR /app
COPY --from=builder /app/target/myapp.jar .
CMD ["java", "-jar", "myapp.jar"]
  • builder 스테이지:
    • Maven을 사용하여 Java 애플리케이션을 빌드하고 .jar 파일을 생성합니다.
  • 최종 스테이지:
    • openjdk 이미지를 사용하여 .jar 파일만 실행합니다.

도커 멀티스테이지 빌드를 활용하여 효율적이고 경량의 컨테이너 이미지를 생성할 수 있습니다. 빌드와 실행 환경을 명확히 분리하고, 이미지 크기를 최소화하며, 보안을 강화할 수 있습니다. 빌드 과정이 복잡해질 수 있으므로, 프로젝트의 요구사항에 따라 적절히 활용해야 합니다.