MLflow 로 직접 기록하기
MLflow 토큰으로 플랫폼 외부(노트북·사내 서버)에서 실행한 학습을 플랫폼 실험과 모델 레지스트리에 기록하는 방법
필요 권한: 개발DEVELOPER · OPERATOR · ADMIN
플랫폼은 실험 추적과 모델 레지스트리의 백엔드로 MLflow 사용. 플랫폼 외부(노트북, 사내 GPU 서버, CI)에서 실행한 학습도 표준 MLflow 파이썬 클라이언트로 플랫폼에 기록. 필요한 것은 두 가지.
- 추적 주소:
https://<플랫폼 주소>/mlflow - MLflow 토큰: 테넌트에 고정된 토큰. 이 토큰이 테넌트 경계를 정하므로, 코드에 테넌트 설정이나
X-Tenant헤더 불필요.
토큰으로 기록한 실험 · run · 등록 모델은 그 토큰의 테넌트에서만 조회되고, 기록 즉시 플랫폼의 실험 · 모델 레지스트리 화면에 표시.
1. 토큰 발급 (화면)
- 오른쪽 위 아바타 → 계정 설정 → MLflow 토큰 관리 로 이동. 발급에는 개발DEVELOPER · OPERATOR · ADMIN 권한(DEVELOPER · OPERATOR · ADMIN) 필요.
- 토큰 이름(선택)과 ③ 만료 일수(기본 90일, 만료 없이 사용하려면 무기한) 지정 후 발급 클릭. ① 토큰은 이때 한 번만 표시. ② 이 서버의 추적 주소가 채워진
export명령을 복사해 사용.MLflow 토큰 발급 직후: ① 한 번만 표시되는 토큰 ② 추적 주소가 채워진 export 명령 ③ 만료 일수
아래 목록에서 토큰별 발급 · 만료 · 마지막 사용 시각을 확인하고, 유출이 의심되면 회수. 회수한 토큰의 요청은 401 로 거부. ADMIN 은 테넌트의 모든 토큰을, 그 밖의 멤버는 자기 토큰만 조회. 회수는 발급자나 ADMIN 이 수행. 목록의 training:… 토큰은 플랫폼이 학습 실행마다 발급하고 실행 종료 시 회수한 토큰.
1-b. 토큰 발급 (API)
CI 처럼 화면을 사용할 수 없으면 API 로 발급.
API=https://mlops.example.com
JWT=$(curl -sS -X POST "$API/auth/jwt/login" \
--data-urlencode "[email protected]" \
--data-urlencode "password=<your-password>" | jq -r .access_token)
curl -sS -X POST "$API/api/v1/mlflow/tokens" \
-H "Authorization: Bearer $JWT" -H "X-Tenant: DEMO" \
-H 'Content-Type: application/json' \
-d '{"name": "ci-train", "expires_in_days": 30}'
{"id": "9d32c814-…", "token": "<secret>", "tenant": "DEMO", "workspace": "demo",
"tracking_uri": "https://mlops.example.com/mlflow"}
expires_in_days 를 생략하면 무기한 토큰. 무기한 토큰은 회수 전까지 계속 유효하므로 반드시 필요한 곳에만 사용.
2. 환경 변수
export MLFLOW_TRACKING_URI=https://mlops.example.com/mlflow
export MLFLOW_TRACKING_TOKEN=<발급한 토큰>
토큰은 코드나 저장소에 포함하지 않고 환경 변수나 시크릿 관리 도구로 주입. 연결 확인:
import mlflow
print(mlflow.get_tracking_uri())
for exp in mlflow.search_experiments(): # 이 테넌트의 실험만 보인다
print(exp.experiment_id, exp.name)
3. 기록하기
아래 코드는 MLflow 3.13.0 기준. 플랫폼 외부에서는 set_experiment() 로 실험 이름 지정 가능. 이 호출이 금지되는 곳은 플랫폼 학습 컨테이너 내부뿐(주입된 run 과 실험이 일치하지 않아 MLflow 가 예외 발생).
import mlflow
import pandas as pd
from mlflow.models import infer_signature
mlflow.set_experiment("docs-mlflow-direct") # 없으면 만든다
with mlflow.start_run(run_name="baseline") as run:
mlflow.log_params({"epochs": 3, "lr": 0.001})
for epoch in range(3):
mlflow.log_metrics({"train/loss": 1.0 / (epoch + 1),
"eval/accuracy": 0.5 + 0.1 * epoch}, step=epoch)
mlflow.log_text("hello", "notes/summary.txt") # 아무 파일이나 아티팩트로
class Echo(mlflow.pyfunc.PythonModel):
def predict(self, context, model_input, params=None):
return model_input
df = pd.DataFrame({"x": [1.0]})
info = mlflow.pyfunc.log_model(name="model", python_model=Echo(),
signature=infer_signature(df, df))
step을 지정하면 run 상세의 메트릭 탭에 곡선 표시.- 기록한 run 은 플랫폼 실험 화면에 즉시 표시.
4. 모델 레지스트리에 등록하기
| 방법 | 코드 | 사용하는 경우 |
|---|---|---|
| 로깅과 동시에 | mlflow.pyfunc.log_model(..., registered_model_name="mnist-cnn") | 항상 등록하는 경우 |
| 로깅 뒤에 조건부로 | mlflow.register_model(info.model_uri, "mnist-cnn") | 검증 지표가 기준을 넘는 경우에만 등록 |
| 플랫폼 화면에서 | run 상세의 모델 등록 | 기록은 코드로, 등록 판단은 사용자가 |
mv = mlflow.register_model(info.model_uri, "docs-mlflow-direct")
print(mv.name, mv.version) # docs-mlflow-direct 1
register_model()에는log_model()이 반환한info.model_uri(models:/m-…)를 그대로 전달. MLflow 3 은 로깅된 모델을 run 아티팩트 외부에 저장하므로runs:/<run>/model경로를 추측해 지정하면 실패할 수 있음.- 가중치 파일만 아티팩트로 업로드한 뒤
MlflowClient().create_model_version(source="runs:/…/weights/best.pt")로 등록하는 이전 방식으로도 등록은 가능. 하지만MLmodel이 없어 서빙 이미지 생성 불가. 배포할 모델은pyfunc.log_model로 기록 필요.
스테이지
MLflow 클라이언트로 직접 등록한 버전의 스테이지는 None. 플랫폼이 등록한 버전(학습의 자동 등록, run 상세의 모델 등록)은 등록 즉시 Staging. Production 승격은 MLflow API 가 아니라 플랫폼 화면의 승격 요청 → 승인 절차로 진행. MLflow API 로 스테이지를 변경하면 차단되지는 않지만 승인 · 감사 기록이 남지 않음.
문제 해결
| 증상 | 원인과 조치 |
|---|---|
401 (Not authenticated) | 토큰이 없거나 만료 · 회수됨, 또는 테넌트가 정지됨. MLflow 토큰 관리 에서 상태를 확인하고 다시 발급. MLFLOW_TRACKING_TOKEN 이 실행 프로세스까지 전달되는지 확인 |
403 (Invalid Host header) | 이 도메인이 서버의 MLflow 허용 호스트에 없음. 운영자가 GEO_MLOPS_MLFLOW_ALLOWED_HOSTS 에 도메인을 추가해야 함(앞단 프록시) |
| 기록은 되었지만 화면에 표시되지 않음 | 다른 테넌트의 토큰으로 기록함. 토큰 발급 때의 테넌트와 화면의 테넌트가 같은지 확인 |
| 사내 프록시 뒤에서 연결 실패 | NO_PROXY 에 플랫폼 호스트를 추가하거나 HTTPS_PROXY 설정 확인 |
| 아티팩트 업로드 실패 | 서버의 오브젝트 스토리지 연결 문제일 수 있음. 운영자에게 문의 |