Theme:

이미지를 빌드했는데 크기가 2GB이고, 내부에 어떤 패키지가 들어 있는지 정확히 모른다면 프로덕션에 올려도 괜찮을까요?

이미지 경량화가 중요한 이유

이미지 크기는 단순한 디스크 사용량 문제가 아닙니다.

  • 배포 속도: 이미지가 클수록 pull에 시간이 오래 걸립니다. 장애 시 롤백도 느려집니다.
  • 보안 공격 표면: 포함된 패키지가 많을수록 취약점이 존재할 확률이 높아집니다.
  • 네트워크 비용: 레지스트리에서 이미지를 pull할 때마다 대역폭을 소비합니다.
  • 콜드 스타트: Kubernetes에서 노드에 이미지가 없으면 처음 pull하는 시간이 Pod 시작 시간에 포함됩니다.

경량화 기법

1. 베이스 이미지 선택

DOCKERFILE
# 나쁜 예: 전체 OS 포함 (~900MB)
FROM node:20

# 보통: Debian slim (~200MB)
FROM node:20-slim

# 좋은 예: Alpine Linux (~140MB)
FROM node:20-alpine

# 더 좋은 예: distroless (~120MB)
FROM gcr.io/distroless/nodejs20-debian12

2. 멀티스테이지 빌드

빌드 도구는 빌드 스테이지에만 두고, 결과물만 런타임 스테이지로 복사합니다.

DOCKERFILE
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
CMD ["node", "dist/server.js"]

3. 패키지 설치 최적화

DOCKERFILE
# apt: 추천하지 않는 패키지 제외 + 캐시 정리
RUN apt-get update \
    && apt-get install -y --no-install-recommends curl ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# apk: 캐시 없이 설치
RUN apk add --no-cache curl

4. 불필요한 파일 제거

.dockerignore로 빌드 컨텍스트에서 제외합니다.

TEXT
# .dockerignore
.git
.github
node_modules
*.md
docs/
tests/
.env*
.vscode
.idea
coverage/

5. 레이어 합치기

DOCKERFILE
# 나쁜 예: 파일이 이전 레이어에 남아있음
RUN wget https://example.com/large-file.tar.gz
RUN tar xzf large-file.tar.gz
RUN rm large-file.tar.gz

# 좋은 예: 하나의 레이어에서 모두 처리
RUN wget https://example.com/large-file.tar.gz \
    && tar xzf large-file.tar.gz \
    && rm large-file.tar.gz

6. 이미지 크기 분석 도구

BASH
# docker history로 레이어별 크기 확인
docker history myapp:latest

# dive로 상세 분석 (추천)
dive myapp:latest

# 이미지 크기 비교
docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}"

dive는 각 레이어에 어떤 파일이 추가/변경/삭제되었는지 시각적으로 보여줍니다.

보안 스캐닝

왜 스캐닝이 필요한가

이미지에 포함된 OS 패키지, 언어 라이브러리에는 알려진 취약점(CVE)이 있을 수 있습니다. 빌드 시점에는 안전하더라도, 이후에 새로운 CVE가 발견될 수 있습니다.

Trivy

오픈소스 취약점 스캐너로, 가장 널리 사용됩니다.

BASH
# 설치 (macOS)
brew install trivy

# 이미지 스캔
trivy image myapp:latest

# 심각도 필터링 (HIGH, CRITICAL만)
trivy image --severity HIGH,CRITICAL myapp:latest

# JSON 출력
trivy image --format json --output result.json myapp:latest

# 취약점이 발견되면 실패 (CI에서 활용)
trivy image --exit-code 1 --severity CRITICAL myapp:latest

출력 예시:

PLAINTEXT
myapp:latest (alpine 3.19.1)
Total: 3 (HIGH: 2, CRITICAL: 1)

┌──────────────┬────────────────┬──────────┬────────┬──────────────────────┐
│   Library    │ Vulnerability  │ Severity │ Status │   Fixed Version      │
├──────────────┼────────────────┼──────────┼────────┼──────────────────────┤
│ libssl3      │ CVE-2024-1234  │ CRITICAL │ fixed  │ 3.1.4-r5            │
│ libcrypto3   │ CVE-2024-5678  │ HIGH     │ fixed  │ 3.1.4-r5            │
│ curl         │ CVE-2024-9999  │ HIGH     │ fixed  │ 8.5.0-r1            │
└──────────────┴────────────────┴──────────┴────────┴──────────────────────┘

Trivy의 다양한 스캔 대상

BASH
# 파일 시스템 스캔 (빌드 전에 소스 코드 검사)
trivy fs --scanners vuln,secret .

# Dockerfile 자체의 문제 점검
trivy config Dockerfile

# SBOM 생성
trivy image --format spdx-json --output sbom.json myapp:latest

Docker Scout

Docker Desktop에 내장된 보안 분석 도구입니다.

BASH
# Docker Desktop 최신 버전에서 사용 가능
docker scout cves myapp:latest

# 권고 사항 확인
docker scout recommendations myapp:latest

# SBOM 확인
docker scout sbom myapp:latest

# 베이스 이미지 업데이트 제안
docker scout compare --to myapp:previous myapp:latest

Snyk

BASH
# 설치 및 인증
npm install -g snyk
snyk auth

# 이미지 스캔
snyk container test myapp:latest

# 모니터링 (지속적으로 새 취약점 알림)
snyk container monitor myapp:latest

CI 파이프라인에 통합

YAML
# GitHub Actions 예시
name: Security Scan
on: push

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: 1

      - name: Upload scan results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif

SBOM (Software Bill of Materials)

SBOM은 이미지에 포함된 모든 소프트웨어 구성 요소의 목록입니다. 식품의 성분표와 같은 역할입니다.

SBOM이 필요한 이유

  • 새로운 CVE 발견 시, 해당 패키지를 포함한 이미지를 빠르게 식별
  • 라이선스 컴플라이언스 확인
  • 공급망 보안 (Supply Chain Security) 확보

SBOM 생성

BASH
# Trivy로 SPDX 형식 SBOM 생성
trivy image --format spdx-json -o sbom.spdx.json myapp:latest

# CycloneDX 형식
trivy image --format cyclonedx -o sbom.cdx.json myapp:latest

# Docker Scout으로 SBOM 확인
docker scout sbom myapp:latest

# Syft로 SBOM 생성 (Anchore 프로젝트)
syft myapp:latest -o spdx-json > sbom.json

이미지 서명

이미지가 신뢰할 수 있는 빌드 파이프라인에서 생성되었는지 검증할 수 있습니다.

BASH
# Cosign으로 이미지 서명 (Sigstore 프로젝트)
cosign sign myregistry.com/myapp:v1.0.0

# 서명 검증
cosign verify myregistry.com/myapp:v1.0.0

# 키 없는 서명 (keyless signing — OIDC 기반)
cosign sign --yes myregistry.com/myapp:v1.0.0

실전 체크리스트

빌드 전

  • .dockerignore 작성
  • 경량 베이스 이미지 선택 (alpine, slim, distroless)
  • 멀티스테이지 빌드 적용
  • 비루트 사용자(USER) 설정
  • 베이스 이미지 버전 고정

빌드 후

  • docker history로 레이어 크기 확인
  • dive로 불필요한 파일 확인
  • Trivy/Docker Scout으로 취약점 스캔
  • CRITICAL/HIGH 취약점 해결
  • SBOM 생성

CI/CD

  • 빌드 파이프라인에 스캐닝 통합
  • CRITICAL 취약점 발견 시 빌드 실패 설정
  • 정기적인 베이스 이미지 업데이트
  • SBOM 아카이빙

정리

  • 이미지 경량화는 배포 속도, 보안, 비용 모든 측면에서 중요합니다.
  • 경량 베이스 이미지 + 멀티스테이지 빌드가 가장 효과적인 경량화 조합입니다.
  • Trivy, Docker Scout, Snyk 같은 도구로 이미지에 포함된 취약점을 사전에 발견합니다.
  • SBOM을 생성하면 새로운 CVE 발견 시 영향받는 이미지를 빠르게 식별할 수 있습니다.
  • CI 파이프라인에 보안 스캐닝을 통합하여, 취약한 이미지가 프로덕션에 배포되는 것을 방지합니다.
댓글 로딩 중...