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)
참고 자료