콘텐츠로 이동
Data Prep
상세

Docker for ML

개요

Docker란?

Docker는 애플리케이션을 컨테이너라는 격리된 환경에서 실행할 수 있게 해주는 플랫폼이다. 2013년 Solomon Hykes가 공개한 이후 배포 자동화의 표준이 되었다.

왜 ML 워크로드에 Docker를 사용하는가?

문제 Docker 없이 Docker로 해결
환경 불일치 "내 컴퓨터에선 되는데..." 동일한 컨테이너 이미지 사용
의존성 충돌 Python/CUDA 버전 충돌 프로젝트별 격리 환경
재현성 환경 구축 문서화 어려움 Dockerfile이 곧 문서
배포 복잡성 서버마다 설정 다름 이미지 빌드 후 어디서든 실행
스케일링 수동 인스턴스 관리 K8s와 결합해 자동 확장

핵심 개념

개념 설명
Image 컨테이너의 템플릿. 읽기 전용. Dockerfile로 빌드
Container Image의 실행 인스턴스. 읽기/쓰기 가능
Dockerfile Image 빌드 명령어 모음
Registry Image 저장소 (Docker Hub, ECR, GCR)
Layer Image의 캐시 단위. 변경된 레이어만 재빌드
Volume 컨테이너 외부 영구 저장소 (모델, 데이터)

1. ML용 Dockerfile

1.1 기본 구조

# 베이스 이미지 선택
FROM python:3.10-slim

# 메타데이터
LABEL maintainer="team@company.com"
LABEL version="1.0"

# 환경 변수
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# 시스템 의존성
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 작업 디렉토리
WORKDIR /app

# 의존성 설치 (캐시 활용)
COPY requirements.txt .
RUN pip install -r requirements.txt

# 애플리케이션 코드
COPY src/ ./src/
COPY configs/ ./configs/

# 비root 사용자
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

# 포트 노출
EXPOSE 8000

# 실행 명령
CMD ["python", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

1.2 GPU 학습용

# NVIDIA CUDA 베이스
FROM nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04

ENV DEBIAN_FRONTEND=noninteractive

# 시스템 패키지
RUN apt-get update && apt-get install -y --no-install-recommends \
    python3.10 \
    python3-pip \
    python3.10-dev \
    git \
    wget \
    && rm -rf /var/lib/apt/lists/*

# Python 심볼릭 링크
RUN ln -s /usr/bin/python3.10 /usr/bin/python

WORKDIR /workspace

# PyTorch 설치 (CUDA 12.1)
RUN pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

# 추가 의존성
COPY requirements.txt .
RUN pip install -r requirements.txt

# 코드
COPY . .

# 학습 스크립트 실행
ENTRYPOINT ["python", "train.py"]

1.3 추론 서비스용 (멀티스테이지)

# 빌드 스테이지
FROM python:3.10 AS builder

WORKDIR /build

COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /build/wheels -r requirements.txt

# 프로덕션 스테이지
FROM python:3.10-slim

WORKDIR /app

# 빌드된 wheel 복사
COPY --from=builder /build/wheels /wheels
RUN pip install --no-cache /wheels/*

# 애플리케이션
COPY src/ ./src/
COPY models/ ./models/

# 보안: 비root 사용자
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

2. 최적화 기법

2.1 레이어 캐싱

# 나쁜 예: 코드 변경 시 모든 의존성 재설치
COPY . .
RUN pip install -r requirements.txt

# 좋은 예: 의존성 캐시 활용
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

2.2 이미지 크기 최적화

# 1. slim/alpine 베이스 사용
FROM python:3.10-slim  # ~150MB vs python:3.10 ~900MB

# 2. 불필요한 파일 제거
RUN pip install --no-cache-dir -r requirements.txt

# 3. apt 캐시 제거
RUN apt-get update && apt-get install -y --no-install-recommends \
    package1 \
    && rm -rf /var/lib/apt/lists/*

# 4. .dockerignore 사용
# .dockerignore:
# __pycache__
# *.pyc
# .git
# .env
# data/
# notebooks/

2.3 보안

# 비root 사용자
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser

# 읽기 전용 파일시스템
# docker run --read-only ...

# 권한 최소화
COPY --chown=appuser:appgroup . .

3. Docker Compose

3.1 개발 환경

# docker-compose.yml
version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - ./src:/app/src:ro  # 개발 시 코드 마운트
      - ./models:/app/models:ro
    environment:
      - MODEL_PATH=/app/models/model.pkl
      - LOG_LEVEL=DEBUG
      - MLFLOW_TRACKING_URI=http://mlflow:5000
    depends_on:
      - redis
      - mlflow

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  mlflow:
    image: ghcr.io/mlflow/mlflow:v2.9.2
    ports:
      - "5000:5000"
    environment:
      - BACKEND_STORE_URI=sqlite:///mlflow.db
      - DEFAULT_ARTIFACT_ROOT=/mlruns
    volumes:
      - mlflow_data:/mlruns
    command: mlflow server --host 0.0.0.0

  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  redis_data:
  mlflow_data:
  prometheus_data:
  grafana_data:

3.2 GPU 학습 환경

version: '3.8'

services:
  training:
    build:
      context: .
      dockerfile: Dockerfile.gpu
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1  # 또는 all
              capabilities: [gpu]
    volumes:
      - ./data:/data:ro
      - ./output:/output
      - ./checkpoints:/checkpoints
    environment:
      - CUDA_VISIBLE_DEVICES=0
      - WANDB_API_KEY=${WANDB_API_KEY}
    command: python train.py --epochs 100

  tensorboard:
    image: tensorflow/tensorflow:latest
    ports:
      - "6006:6006"
    volumes:
      - ./output/logs:/logs:ro
    command: tensorboard --logdir=/logs --host=0.0.0.0

4. 실행 명령어

4.1 기본 명령어

# 빌드
docker build -t ml-app:v1.0 .

# 태그
docker tag ml-app:v1.0 registry.example.com/ml-app:v1.0

# 푸시
docker push registry.example.com/ml-app:v1.0

# 실행
docker run -d \
  --name ml-app \
  -p 8000:8000 \
  -v $(pwd)/models:/app/models:ro \
  -e MODEL_PATH=/app/models/model.pkl \
  ml-app:v1.0

# GPU 실행
docker run --gpus all \
  -v $(pwd)/data:/data \
  ml-training:v1.0

4.2 디버깅

# 로그 확인
docker logs -f ml-app

# 컨테이너 진입
docker exec -it ml-app /bin/bash

# 리소스 사용량
docker stats ml-app

# 이미지 레이어 분석
docker history ml-app:v1.0

5. ECR (AWS) 배포

# 로그인
aws ecr get-login-password --region ap-northeast-2 | \
  docker login --username AWS --password-stdin 123456789.dkr.ecr.ap-northeast-2.amazonaws.com

# 리포지토리 생성
aws ecr create-repository --repository-name ml-app

# 빌드 및 푸시
docker build -t ml-app:v1.0 .
docker tag ml-app:v1.0 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/ml-app:v1.0
docker push 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/ml-app:v1.0

6. 모범 사례

6.1 Dockerfile 체크리스트

[ ] 적절한 베이스 이미지 선택
[ ] 레이어 캐싱 최적화
[ ] 불필요한 파일 제외 (.dockerignore)
[ ] 비root 사용자 사용
[ ] HEALTHCHECK 정의
[ ] 환경 변수로 설정 관리
[ ] 적절한 LABEL 추가
[ ] 멀티스테이지 빌드 (프로덕션)

6.2 태그 전략

# 버전 태그
ml-app:1.0.0
ml-app:1.0
ml-app:1

# 환경별 태그
ml-app:dev
ml-app:staging
ml-app:prod

# Git 기반 태그
ml-app:abc1234  # commit hash
ml-app:main-20240201  # branch-date


7. 비용 고려사항

7.1 이미지 크기와 비용

항목 영향 최적화 방법
빌드 시간 CI/CD 비용 캐시 활용, 멀티스테이지
레지스트리 저장 ECR: $0.10/GB/월 불필요한 레이어 제거
이미지 풀 시간 콜드스타트 지연 slim 이미지, 사전 풀
네트워크 전송 데이터 전송 비용 같은 리전 사용
# 이미지 크기 비교
python:3.10          ~900MB
python:3.10-slim     ~150MB
python:3.10-alpine   ~50MB (호환성 주의)

# ML 프레임워크 포함 시
pytorch/pytorch:2.0-cuda11.7-cudnn8-runtime  ~6GB

7.2 ECR 비용 최적화

# 라이프사이클 정책으로 오래된 이미지 자동 삭제
aws ecr put-lifecycle-policy --repository-name my-repo --lifecycle-policy-text '{
  "rules": [{
    "rulePriority": 1,
    "description": "Keep last 5 images",
    "selection": {
      "tagStatus": "any",
      "countType": "imageCountMoreThan",
      "countNumber": 5
    },
    "action": {"type": "expire"}
  }]
}'

# 이미지 크기 확인
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

7.3 실무 팁

1. 개발 vs 프로덕션 이미지 분리
   - 개발: 디버깅 도구 포함
   - 프로덕션: 최소 크기, 보안 강화

2. 베이스 이미지 선택
   - GPU 학습: nvidia/cuda 기반
   - 추론: python-slim 또는 distroless
   - Lambda: AWS 공식 이미지

3. 보안 스캔
   - ECR 이미지 스캔 활성화
   - Trivy, Snyk 등 사용

4. CI/CD 최적화
   - GitHub Actions 캐시 활용
   - 빌드킷 사용 (DOCKER_BUILDKIT=1)

참고 자료