플랫폼에 올리기 전에 내 PC 에서 계약을 지키는지 확인할 수 있습니다. 필요한 것은 Docker 와 로컬 MLflow 서버 하나뿐입니다. 아래 명령은 MNIST 예제 디렉터리에서 실제로 실행한 그대로이고, 로컬 MLflow 도 예제 이미지 안의 mlflow 로 띄웁니다(호스트에 파이썬 패키지를 설치할 필요가 없습니다).

1. 이미지 빌드

docker build -t example/mnist-trainer:v1 .

2. 검증용 데이터 만들기

플랫폼이 /geo/dataset 에 해 주는 일을 흉내 냅니다. --synthetic 은 네트워크 없이 숫자를 그려 넣은 합성 이미지를 만듭니다.

docker run --rm -u "$(id -u):$(id -g)" -v "$PWD:/w" -w /w \
  --entrypoint python example/mnist-trainer:v1 \
  make_sample_data.py ./sample-data --synthetic --count 400
# -> sample-data/manifest.json (400 files)

3. 로컬 MLflow 띄우기

docker run -d --name mlflow-local --network host -u "$(id -u):$(id -g)" -e HOME=/tmp \
  -v "$PWD:/w" -w /w --entrypoint mlflow example/mnist-trainer:v1 \
  server --host 127.0.0.1 --port 5500 \
  --backend-store-uri sqlite:////w/store.db --artifacts-destination /w/mlartifacts

4. 플랫폼처럼 run 을 먼저 만들고 학습 실행

플랫폼은 run 을 미리 만들어 MLFLOW_RUN_ID 로 넣어 줍니다. 똑같이 해야 검증 조건이 실전과 같아집니다.

export MLFLOW_TRACKING_URI=http://127.0.0.1:5500

RUN_ID=$(docker run --rm --network host -e MLFLOW_TRACKING_URI \
  --entrypoint python example/mnist-trainer:v1 -c "
from mlflow.tracking import MlflowClient
c = MlflowClient()
exp = c.get_experiment_by_name('local-verify')
exp_id = exp.experiment_id if exp else c.create_experiment('local-verify')
print(c.create_run(exp_id).info.run_id)" | tail -1)

docker run --rm --network host \
  -v "$PWD/sample-data:/geo/dataset:ro" \
  -e GEO_DATA_DIR=/geo/dataset \
  -e GEO_WORK_DIR=/geo/work \
  -e GEO_HP_EPOCHS=2 \
  -e GEO_PARAM_DEVICE=cpu \
  -e MLFLOW_TRACKING_URI \
  -e MLFLOW_EXPERIMENT_NAME=local-verify \
  -e MLFLOW_RUN_ID=$RUN_ID \
  example/mnist-trainer:v1
echo "exit=$?"
-> train 320 / val 80 @ cpu
-> epoch 1/2 loss=2.2821 val_acc=0.1000
-> epoch 2/2 loss=2.1825 val_acc=0.3500
-> done. best val accuracy = 0.3500
🏃 View run intrigued-hen-480 at: http://127.0.0.1:5500/#/experiments/1/runs/79676e08…
exit=0

--network host 는 MLflow 가 같은 PC 의 localhost 에 있어서 붙인 것입니다. 원격 MLflow 주소를 쓰면 필요 없습니다.

5. 확인할 것

#확인통과하지 못하면
1종료 코드가 0 인가지표가 다 남아도 실패로 기록됩니다
2MLflow 의 run($RUN_ID)에 지표 곡선이 그려지는가학습 화면이 빈 채로 돕니다. MLFLOW_RUN_ID 를 덮어쓰거나 set_experiment() 를 부르지 않았는지 확인
3param epochs 와 지표의 step 이 있는가진행률 막대 없이 "학습 중" 으로만 보입니다
4run 에 model 이 로깅되고 MLmodel · signature 가 보이는가레지스트리 등록을 건너뛰고 서빙으로 못 갑니다
5필요한 산출물이 MLflow 에 남았는가/geo/work 는 회수하지 않습니다
6아래 serve 검증이 통과하는가학습은 되지만 배포로 못 갑니다. 가장 중요한 항목입니다

MLflow 화면(http://127.0.0.1:5500)을 열어 1~5 를 눈으로 볼 수 있습니다.

6. serve 검증: 배포의 전제 조건

플랫폼의 추론 서빙은 등록된 모델 버전을 MLflow scoring server 로 그대로 띄우는 방식입니다. 로컬에서 뜨면 플랫폼에서도 뜹니다.

# 1) 학습이 로깅한 모델을 로컬 레지스트리에 등록 (플랫폼에서는 자동)
docker run --rm --network host -e MLFLOW_TRACKING_URI --entrypoint python \
  example/mnist-trainer:v1 -c "
import mlflow
from mlflow.tracking import MlflowClient
c = MlflowClient()
run = c.get_run('$RUN_ID')
m = c.search_logged_models(experiment_ids=[run.info.experiment_id],
                           filter_string=\"source_run_id = '$RUN_ID'\")[0]
mv = mlflow.register_model(f'models:/{m.model_id}', 'mnist')
print('registered', mv.name, mv.version)"

# 2) 등록된 버전을 그대로 띄운다
docker run -d --name mnist-serve --network host -e MLFLOW_TRACKING_URI \
  --entrypoint mlflow example/mnist-trainer:v1 \
  models serve -m models:/mnist/1 -h 127.0.0.1 -p 5501 --env-manager local

# 3) 요청 한 건. 200 과 JSON 예측이 오는가
python3 -c "
import base64, json
b = base64.b64encode(open('sample-data/images/00001.png','rb').read()).decode()
json.dump({'dataframe_split': {'columns': ['image_b64'], 'data': [[b]]}}, open('input.json','w'))"
curl -X POST 127.0.0.1:5501/invocations -H 'Content-Type: application/json' -d @input.json
{"predictions": [{"label": "1", "confidence": 0.11639321595430374}]}

여기서 볼 것은 응답이 오는가(모델이 로드되고 signature 대로 요청을 받는가)입니다. 합성 데이터는 학습 신호가 약해 예측이 틀려도 괜찮습니다.

가장 흔한 실패는 signature 누락, predictor.py 의 학습 코드 import, 학습 환경과 다른 pip_requirements 세 가지입니다. 셋 모두 학습 단계에서는 아무 증상이 없다가 서빙에서만 드러납니다. 그래서 여기서 잡아야 합니다.

정리

docker rm -f mlflow-local mnist-serve

다음: 플랫폼에 이미지 올리기

2026-09-21 기준 플랫폼에 맞춰 작성했습니다.

© Geo-MLOps