Docker 기반 ML 워크플로우
학습과 추론 역할을 Docker로 분리한 ML 워크플로우 구성
Repository개인 저장소
malit9999/docker-ml-workflow-mission-15(새 탭에서 열림)무엇을 하는 미션인가
학습하는 사람과 추론하는 사람이 다른 상황을 컨테이너로 재현하는 미션입니다. 연구자 1이 모델을 학습해 아티팩트를 남기고, 연구자 2가 그 아티팩트만 받아 추론합니다. 혼자 수행하면서 두 역할을 각각의 실행 환경으로 분리했습니다.
데이터는 학생 성적(공부 시간·이전 점수·수면 시간 등)이고, 목표값은 Performance Index입니다.
컨테이너 구성
# researcher2/docker-compose.yml (발췌)
services:
researcher1:
image: minseong9999/mission15-researcher1:latest
volumes:
- ./shared:/app/output # model.pkl, test.csv → 공유 폴더로
jupyter:
image: jupyter/scipy-notebook:latest
ports: ["8888:8888"]
volumes:
- ./:/home/jovyan/work
environment:
- JUPYTER_TOKEN=mission15
command: bash -c "pip install -q scikit-learn==1.5.2 joblib==1.4.2 && start-notebook.sh"
depends_on:
researcher1:
condition: service_completed_successfully # 학습이 끝나야 추론 시작
- researcher1 — 학습 전용. 끝나면 종료되는 배치 컨테이너입니다. 결과물을 볼륨으로 내보냅니다
- jupyter — 추론 환경. 공유 폴더를 마운트해
model.pkl을 읽습니다
재현 가능하게 만들려고 한 두 가지
① 실행 순서를 compose가 보장합니다. condition: service_completed_successfully로,
학습 컨테이너가 정상 종료된 뒤에만 Jupyter가 뜹니다. 아티팩트가 없는 상태로 추론이
시작되는 경우를 막습니다.
② 라이브러리 버전을 맞춥니다. pickle로 저장한 모델은 저장 시점과 로드 시점의
scikit-learn 버전이 다르면 깨질 수 있어, 추론 컨테이너가 뜰 때
scikit-learn==1.5.2, joblib==1.4.2를 고정 설치합니다.
경로를 환경변수로 뺀 이유
같은 train.py가 로컬에서도, 컨테이너에서도 돌아야 했습니다.
# researcher1/train.py — 로컬 기본값, 컨테이너에서는 환경변수로 덮어쓴다
DATA_DIR = os.environ.get("DATA_DIR", os.path.join(_BASE_DIR, "data"))
OUTPUT_DIR = os.environ.get("OUTPUT_DIR", os.path.join(_BASE_DIR, "output"))
학습은 GridSearchCV로 Ridge 하이퍼파라미터를 고르고 model.pkl로 저장합니다.
전처리 함수는 추론 노트북에서 같은 코드를 다시 쓰도록 맞춰 뒀습니다 — 학습과 추론의
전처리가 어긋나는 문제를 코드 공유로 막는 방식입니다.
결과
researcher2/result.csv에 입력 피처와 Predicted Performance Index가 함께 저장됩니다.
회귀 성능 지표(RMSE 등)를 저장소에 기록해 두지는 않았습니다.
한계
- 이미지 빌드용 Dockerfile이 저장소에 없습니다. 학습 이미지는 Docker Hub에 올린 것을 참조합니다
- 추론 컨테이너는 기성 Jupyter 이미지에 실행 시점
pip install을 얹는 방식이라, 이미지 자체로 고정된 환경은 아닙니다 - 오케스트레이션은 로컬 Compose까지이며 배포 환경은 다루지 않았습니다