Go 애플리케이션을 Docker 이미지로 빌드할 때, 불필요하게 큰 이미지 크기로 인해 고민하는 경우가 많습니다. 이 글에서는 CGO 사용 여부에 따라 Go 프로젝트의 Dockerfile을 최적화하는 두 가지 모범 사례를 소개합니다.
1. CGO를 사용하지 않는 경우
Go 애플리케이션은 종종 CGO 없이 빌드될 수 있으며, 이 경우 scratch 이미지를 기반으로 매우 작고 효율적인 런타임 이미지를 만들 수 있습니다. scratch 이미지는 아무것도 포함하지 않는 최소한의 베이스 이미지로, 오직 리눅스 커널만 접근 가능합니다.
최적화 전략은 다음과 같습니다:
- 먼저,
golang공식 이미지를 사용하여 Go 애플리케이션의 바이너리를 빌드합니다. - 빌드된 바이너리를
scratch이미지로 복사합니다. - 애플리케이션 실행에 필요한 경우, TLS 인증서와 같은 추가 파일도 함께 복사합니다.
다음은 CGO를 사용하지 않는 경우의 Dockerfile 예시입니다:
# 빌드 단계: golang 이미지를 사용하여 바이너리 생성
FROM golang:1.19-alpine AS builder
# 작업 디렉토리 설정
WORKDIR /build
# 소스 코드 복사
COPY . .
# Go 모듈 의존성 다운로드 (선택 사항, 캐싱 활용)
RUN go mod download
# CGO 비활성화, OS 및 아키텍처 지정 후 바이너리 빌드
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o myapp ./cmd/server/main.go
# TLS 인증서 복사 (https 통신 필요시)
RUN apk add --no-cache ca-certificates
# --- 런타임 단계 ---
# scratch 이미지를 사용하여 최소 이미지 생성
FROM scratch
WORKDIR /app
# 빌드 단계에서 생성된 바이너리를 복사
COPY --from=builder /build/myapp .
# 빌드 단계에서 복사된 TLS 인증서 복사
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 애플리케이션 실행 환경 변수 설정 (예: 포트)
ENV APP_PORT=8080
# 애플리케이션이 사용할 포트 노출
EXPOSE 8080
# 컨테이너 시작 시 실행할 명령어
ENTRYPOINT ["/app/myapp"]
2. CGO를 사용하는 경우
CGO를 사용해야 하는 경우, scratch 이미지만으로는 빌드된 바이너리가 제대로 작동하지 않을 수 있습니다. 이 경우, alpine과 같은 가볍고 필수적인 시스템 라이브러리를 포함하는 베이스 이미지를 사용하는 것이 좋습니다. alpine 이미지는 매우 작기 때문에 (일반적으로 5MB 내외) 여전히 효율적인 이미지 크기를 유지할 수 있습니다.
Dockerfile은 다음과 같이 수정됩니다:
# 빌드 단계: golang 이미지를 사용하여 바이너리 생성
FROM golang:1.19-alpine AS builder
# 작업 디렉토리 설정
WORKDIR /build
# 소스 코드 복사
COPY . .
# Go 모듈 의존성 다운로드 (선택 사항, 캐싱 활용)
RUN go mod download
# OS 및 아키텍처 지정 후 바이너리 빌드 (CGO 활성화 상태)
RUN GOOS=linux GOARCH=amd64 go build -o myapp ./cmd/server/main.go
# --- 런타임 단계 ---
# alpine 이미지를 사용하여 필수 라이브러리 포함
FROM alpine:latest
WORKDIR /app
# 빌드 단계에서 생성된 바이너리를 복사
COPY --from=builder /build/myapp .
# 애플리케이션 실행에 필요한 다른 파일 (예: 설정 파일, 정적 파일) 복사
# COPY --from=builder /app/configs /app/configs
# COPY --from=builder /app/static /app/static
# 애플리케이션 실행 환경 변수 설정 (예: 포트)
ENV APP_PORT=8080
# 애플리케이션이 사용할 포트 노출
EXPOSE 8080
# 컨테이너 시작 시 실행할 명령어
ENTRYPOINT ["/app/myapp"]
위 예시에서 GOOS 및 GOARCH 환경 변수를 설정하여 빌드 시 대상 운영체제 및 아키텍처를 명시하는 것이 중요합니다. 또한, 애플리케이션이 실행되는 동안 필요한 모든 종속성 (예: 설정 파일, 정적 파일)도 런타임 이미지에 포함시켜야 합니다.