Docker를 이용한 머신러닝 모델 컨테이너화

학습 목표 매핑

SKALA 3기 Module 7 — 데이터 분석 Mini-project (Learning Objective 7-3)

  • Objective: 모델과 API를 Docker로 컨테이너화하여 / 로컬 + 클라우드(AWS/GCP 자유선택) 환경에서 / 두 환경 모두에서 동일하게 동작 (Bloom L6-Create)
  • Evaluation: 배포 환경 검증

Docker란?

정의: 애플리케이션을 컨테이너로 패키징하여 어느 환경에서나 동일하게 실행

  • 코드, 의존성, 설정을 모두 포함
  • OS·하드웨어 무관하게 동작
  • “내 컴퓨터에서 작동해” 문제 해결

Docker의 ML 프로젝트에서의 이점

이점설명
이식성노트북, 서버, 클라우드 동일하게 동작
재현성고정된 의존성으로 일관된 결과
확장성여러 서버에 쉽게 배포
오케스트레이션Kubernetes와 호환
CI/CD배포 파이프라인 자동화

프로젝트 파일 구성

ml-project/
├── Dockerfile              # 컨테이너 정의
├── requirements.txt        # Python 의존성 (버전 고정)
├── train.py               # 모델 훈련 스크립트
├── inference.py           # 예측 스크립트
├── main.py                # FastAPI 애플리케이션
├── .dockerignore          # Docker 제외 파일
├── data/                  # 입력 데이터
└── models/                # 훈련된 모델

단계 1: requirements.txt 작성

# requirements.txt - 정확한 버전 명시 (재현성 중요!)
 
# 핵심 데이터 과학
pandas==1.5.3
numpy==1.24.3
scikit-learn==1.2.2
 
# 시각화
matplotlib==3.7.1
seaborn==0.12.2
 
# API 및 배포
fastapi==0.95.0
uvicorn==0.21.0
pydantic==1.10.7
 
# 모델 직렬화
joblib==1.2.0
 
# 유틸리티
python-dotenv==1.0.0
requests==2.31.0

중요: 버전을 고정하지 않으면 다른 환경에서 다른 버전이 설치됨

단계 2: Dockerfile 작성

기본 구조

# Dockerfile
 
# 1. 기본 이미지 선택
FROM python:3.10-slim
 
# 2. 작업 디렉토리 설정
WORKDIR /app
 
# 3. 환경 변수 설정
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
 
# 4. requirements.txt 복사 (레이어 캐싱)
COPY requirements.txt .
 
# 5. 의존성 설치
RUN pip install --no-cache-dir -r requirements.txt
 
# 6. 애플리케이션 코드 복사
COPY train.py .
COPY inference.py .
COPY main.py .
COPY data/ ./data/
 
# 7. 모델 디렉토리 생성
RUN mkdir -p models/
 
# 8. 포트 노출 (FastAPI)
EXPOSE 8000
 
# 9. 볼륨 마운트 (데이터 영속성)
VOLUME ["/app/models", "/app/data"]
 
# 10. 기본 명령
CMD ["python", "inference.py"]

Dockerfile Best Practices 설명

1. 기본 이미지 선택

# 전체 Python 환경 (~900MB)
FROM python:3.10
 
# Slim 버전 - 가볍고 적당한 도구 포함 (~180MB) ⭐ 권장
FROM python:3.10-slim
 
# Alpine - 최소 (~50MB, 일부 도구 없음)
FROM python:3.10-alpine

권장: slim (numpy 등 컴파일 도구 필요)

2. 작업 디렉토리 (WORKDIR)

WORKDIR /app  # 이후 모든 명령이 /app에서 실행

이점:

  • 파일시스템 정렬
  • 볼륨 마운트 가능
  • 경로 명확성

3. 환경 변수

# Python bytecode 생성 방지
ENV PYTHONDONTWRITEBYTECODE=1
 
# 출력 버퍼링 비활성화 (즉시 로그 출력)
ENV PYTHONUNBUFFERED=1
 
# 커스텀 변수
ENV MODEL_PATH=/app/models/model.pkl
ENV DATA_PATH=/app/data

4. 레이어 캐싱 최적화 ⭐ 중요

❌ 느린 방법 - 코드 변경 시 의존성 재설치:

FROM python:3.10-slim
WORKDIR /app
COPY . .                          # 전체 복사 (먼저)
RUN pip install -r requirements.txt

✅ 빠른 방법 - 의존성 캐싱:

FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .           # 의존성만 먼저
RUN pip install -r requirements.txt
COPY . .                          # 코드는 나중에

원리: Docker는 변경 없는 레이어를 캐싱

  • requirements.txt 변경 없음 → pip install 스킵
  • 코드만 변경 → 코드 복사만 다시 실행

5. 이미지 크기 최소화

# --no-cache-dir: pip 캐시 스킵
RUN pip install --no-cache-dir -r requirements.txt
 
# .dockerignore 파일로 불필요한 파일 제외

.dockerignore:

__pycache__/
*.pyc
.git/
.gitignore
notebooks/
*.ipynb
.env
venv/
.pytest_cache/
*.log
README.md

6. 볼륨 마운트

VOLUME ["/app/models", "/app/data"]

용도:

  • 호스트와 데이터 공유
  • 모델 영속성
  • 재사용 가능한 볼륨

단계 3: Docker 이미지 빌드

# 기본 빌드
docker build -t ml-model:1.0 -f Dockerfile .
 
# 이미지 크기 확인
docker images ml-model:1.0
 
# 결과
# REPOSITORY   TAG    IMAGE ID        CREATED      SIZE
# ml-model     1.0    abc123def456    5 hours ago  512MB

단계 4: Docker 컨테이너 실행

기본 실행

# 훈련 실행
docker run ml-model:1.0 python train.py
 
# 예측 실행
docker run ml-model:1.0 python inference.py
 
# 대화형 셸
docker run -it ml-model:1.0 /bin/bash

볼륨 마운트 (데이터 공유)

# Linux/Mac
docker run -v $(pwd)/data:/app/data \
           -v $(pwd)/models:/app/models \
           ml-model:1.0 python train.py
 
# Windows PowerShell
docker run -v ${PWD}\data:/app/data `
           -v ${PWD}\models:/app/models `
           ml-model:1.0 python train.py

효과:

  • 로컬 data/ → 컨테이너 /app/data 매핑
  • 로컬 models/ ← 컨테이너 /app/models 출력 저장

환경 변수 전달

docker run -e MODEL_PATH=/app/models/model.pkl \
           -e BATCH_SIZE=32 \
           ml-model:1.0 python inference.py

FastAPI를 위한 포트 매핑

# 컨테이너 포트 8000 → 호스트 포트 5000
docker run -p 5000:8000 ml-model:1.0
 
# 또는 같은 포트
docker run -p 8000:8000 ml-model:1.0

실제 예제: 훈련 및 예측 파이프라인

train.py

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
import joblib
import os
 
# 데이터 로드
data = pd.read_csv('/app/data/training.csv')
X = data.drop('target', axis=1)
y = data['target']
 
# 모델 훈련
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X, y)
 
# 모델 저장
os.makedirs('/app/models', exist_ok=True)
joblib.dump(clf, '/app/models/model.pkl')
print("✅ 모델 훈련 및 저장 완료!")

inference.py

import pandas as pd
import joblib
 
# 모델 로드
model = joblib.load('/app/models/model.pkl')
 
# 테스트 데이터 로드
data = pd.read_csv('/app/data/test.csv')
 
# 예측
predictions = model.predict(data)
print(predictions)

완전한 워크플로우

# 1. 이미지 빌드
docker build -t ml-pipeline:1.0 .
 
# 2. 훈련 (데이터·모델 볼륨 마운트)
docker run -v $(pwd)/data:/app/data \
           -v $(pwd)/models:/app/models \
           ml-pipeline:1.0 python train.py
 
# 3. 예측
docker run -v $(pwd)/data:/app/data \
           -v $(pwd)/models:/app/models \
           ml-pipeline:1.0 python inference.py

고급: 다단계 빌드 (Multi-stage Build)

빌드 환경과 런타임 환경을 분리하여 이미지 크기 줄이기:

# Stage 1: Builder
FROM python:3.10 as builder
 
WORKDIR /build
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
 
# Stage 2: Runtime
FROM python:3.10-slim
 
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
 
# Builder에서 설치된 패키지만 복사
COPY --from=builder /root/.local /root/.local
 
# PATH 업데이트
ENV PATH=/root/.local/bin:$PATH
 
# 코드 복사
COPY train.py inference.py main.py ./
COPY data/ ./data/
 
RUN mkdir -p models/
 
CMD ["python", "inference.py"]

이점: 최종 이미지 크기 ~30% 감소 (빌드 도구 제외)

Docker Hub에 이미지 배포

1. Docker Hub 계정 생성

https://hub.docker.com/

2. 이미지 태그 및 푸시

# Docker Hub 계정으로 로그인
docker login
 
# 이미지 태그 (username/repository:tag)
docker tag ml-model:1.0 username/ml-model:1.0
docker tag ml-model:1.0 username/ml-model:latest
 
# Docker Hub에 푸시
docker push username/ml-model:1.0
docker push username/ml-model:latest
 
# 다른 컴퓨터에서 다운로드
docker pull username/ml-model:1.0
docker run -p 8000:8000 username/ml-model:1.0

Docker Compose (복합 애플리케이션)

API + 데이터베이스 같은 여러 서비스 관리:

# docker-compose.yml
version: '3.8'
 
services:
  model-api:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - ./models:/app/models
      - ./data:/app/data
    environment:
      - MODEL_PATH=/app/models/model.pkl
    depends_on:
      - db
    
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_PASSWORD=postgres
    volumes:
      - pgdata:/var/lib/postgresql/data
 
volumes:
  pgdata:

실행:

docker-compose up

문제 해결

# 컨테이너 로그 확인
docker logs container_id
 
# 실행 중인 컨테이너 목록
docker ps
 
# 모든 이미지 목록
docker images
 
# 이미지 제거
docker rmi image_id
 
# 이미지 레이어 확인
docker history ml-model:1.0

Module 7 실전 체크리스트

  • requirements.txt 작성 (버전 고정)
  • Dockerfile 생성
  • .dockerignore 파일 작성
  • 로컬에서 이미지 빌드 (docker build)
  • 볼륨 마운트로 훈련 실행
  • 예측 실행 (저장된 모델로)
  • FastAPI 컨테이너 포트 매핑 테스트
  • Docker Hub에 이미지 푸시
  • AWS EC2 또는 Google Cloud Run에 배포
  • 로컬과 클라우드 환경에서 동일하게 동작 확인

Best Practices 요약

  1. python:3.10-slim 기본 이미지 사용
  2. ✅ requirements.txt에 정확한 버전 명시
  3. ✅ pip install 전에 requirements.txt 복사 (캐싱)
  4. --no-cache-dir 사용하여 크기 감소
  5. ✅ .dockerignore 파일 작성
  6. ✅ PYTHONDONTWRITEBYTECODE=1 설정
  7. ✅ PYTHONUNBUFFERED=1 설정
  8. ✅ WORKDIR로 파일시스템 정렬
  9. ✅ 복합 앱은 Docker Compose 사용
  10. ✅ 이미지 버전 관리 (semantic versioning)

참고 자료

타 소스와의 연계

end-to-end-data-science-project (전체 프로젝트 - 7-1) fastapi-ml-serving (FastAPI 배포 - 7-2) github-documentation-standards (GitHub 문서화 - 7-4) containerization (컨테이너화 개념)