Model Serving (KServe Automation)
GenD 의 Model Serving 은 MLflow Registry 에 등록된 모델을 한 번의 API 호출로 K8s 클러스터에 InferenceService 로 배포합니다. 사용자는 UI 또는 CLI 에서 "Deploy" 를 누르면 gend-api 가 MLflow Registry 를 조회해 S3 storageUri 를 합성하고, KServe controller 가 Predictor Pod 를 prawled 합니다.
본 가이드는 M1 마일스톤 (Epic #1082) 의 운영 가능 기능을 다룹니다. M2 (정식 카나리/ContextGateway 통합/UI) 와 M3 (GPU/A-B/드리프트) 는 후속 가이드에서 다룹니다.
핵심 결정
| 항목 | 선택 | 근거 |
|---|---|---|
| 컨트롤러 | KServe 0.13.1 | MLflow 1급 시민, GenD K8s 스택과 정합, CRD 학습 곡선 낮음 |
| 배포 모드 | RawDeployment (Knative/Istio 없음) | 기존 gend-ingress + oauth2-proxy + Prometheus 재사용. cold start 없음 (min=1) |
| 런타임 | kserve-mlserver 기본 | sklearn/xgboost/lightgbm/pyfunc 자동 매핑, modelFormat=mlflow |
| S3 backing | SeaweedFS (dev) / Ceph RGW (prod) | S3 호환, 두 환경 모두 검증 |
| API 인증 | Keycloak JWT (기존) | M2 에서 /predict 경로에 ContextGateway/PII 가드 추가 |
M1 scope
구현됨
- DB 컬럼 9종 추가 (
deployment_status,k8s_resource_name,k8s_namespace,last_apply_at,last_ready_at,error_message,traffic_splitJSONB,serving_runtime,resource_profile) + CHECK constraint + 인덱스. KServeDeployer— server-side apply / patch / delete / get_status (fieldManager=gend-api). idempotent.MLflowArtifactResolver—s3://passthrough +mlflow-artifacts:재작성.- 라우터 3종:
POST /api/v1/serving/deployments/{deployment_id}/deployPOST /api/v1/serving/deployments/{deployment_id}/rollbackGET /api/v1/serving/deployments/{deployment_id}/k8s-status
infra/helm/kservechart (Secret + SA + ServiceMonitor + RawDeployment patch).infra/helm/gend-api/templates/rbac.yaml(Role + RoleBinding, ns 한정).- 회귀 가드 단언 (
test_db_models_package,test_protected_routers_registration). - 시드 (
scripts/seed_kserve_demo.py) + 32 신규 단위/통합 테스트.
M1 한정 / 명시적 미구현
- 카나리 — DB
canary_percent컬럼은 유지하지만 K8s 트래픽 분할은 미구현. M2 에서 Istio VirtualService 또는 KServe ServerlesscanaryTrafficPercent중 ADR 결정 후 정식 구현. M1 의 rollback 은canary_percent=0리셋만. - /predict 프록시 —
InferenceClient.predict()는 NotImplementedError. 운영 호출자는 KServe Service URL (<name>.gend.svc.cluster.local) 을 cluster-internal 로 직접 호출. M2 에서 ContextGateway 경유 라우트 추가. - Watch 루프 —
deploying → ready전환은 사용자가/k8s-status를 폴링할 때 lazy 하게 반영. M2 에서 background task (kubernetes_asyncio watch API) 로 대체. - MLflow signature 검증 —
MLflowArtifactResolver.resolve()가 signature 필드를 빈 dict 로 반환. M3 에서 페이로드 스키마 검증 게이트. - UI —
DeployDialog/ModelDeploymentList/CanarySlider등은 M2 stream 의 ui-dev 산출물.
흐름
DB row state machine:
API 요약
POST /api/v1/serving/deployments/{deployment_id}/deploy
JWT 보호 (require_analyst). 워크스페이스 fence (tenant_slug → Workspace 매칭, 403).
응답:
{
"deployment_id": "...",
"endpoint_name": "iris-classifier",
"deployment_status": "deploying",
"k8s_resource_name": "iris-classifier",
"k8s_namespace": "gend",
"last_apply_at": "2026-05-26T03:14:15+00:00",
"error_message": null,
"message": "Applied InferenceService 'iris-classifier' in namespace 'gend'. Poll /k8s-status for readiness."
}
오류:
| 시나리오 | 코드 | DB 변화 |
|---|---|---|
| MLflow 모델 미등록 | 404 | deployment_status=failed, error_message 기록 |
| MLflow 5xx / 네트워크 | 503 | deployment_status=failed |
| KServe RBAC 403 | 403 | deployment_status=failed |
| 알 수 없는 deployment_id | 404 | 변화 없음 |
| 워크스페이스 mismatch | 403 | 변화 없음 |
POST /api/v1/serving/deployments/{deployment_id}/rollback
canary_percent=0 리셋 + 현재 row 의 model_version 으로 재적용. K8s 가
없으면 409 (먼저 /deploy 호출 필요).
GET /api/v1/serving/deployments/{deployment_id}/k8s-status
exists/ready/conditions/url/latest_ready_revision. K8s 리소스
404 는 exists=False (200) — UI 가 "아직 미배포" 상태로 렌더.
ready=True 감지 시 side-effect 로 deployment_status='ready' +
last_ready_at 자동 기록 (M1 의 watch 대체).
환경별 차이
Kind dev (datax-local)
- KServe 0.13.1 controller 를 upstream release manifest 로 설치:
kubectl apply -f https://github.com/kserve/kserve/releases/download/v0.13.1/kserve.yaml helm install kserve infra/helm/kserve(기본값 = SeaweedFS dev)make seed-demo후python scripts/seed_kserve_demo.py로 demo row 추가
AKS prod (aks-genos-prod)
- KServe controller 동일 (cert-manager 의존, AKS 에 사전 설치 완료)
helm install kserve infra/helm/kserve -f infra/helm/kserve/values-azure.yamlkserve-s3-secret은 SealedSecret 으로 사전 생성 (Ceph RGW key)- ServiceMonitor 가
release=gend-prom으로 Prometheus 에 자동 등록
RBAC
- ServiceAccount
gend-api(nsgend) —deployment.yaml상단에 선언. - Role
gend-serving-manager(infra/helm/gend-api/templates/rbac.yaml):serving.kserve.io/inferenceservices(verbs: get/list/watch/create/update/patch/delete)serving.kserve.io/inferenceservices/status(get)apps/deployments,core/services|events|pods,autoscaling/horizontalpodautoscalers(get/list/watch)
- RoleBinding — ns
gend한정 (cross-namespace 금지).
이 5종 외 어떤 리소스도 gend-api 가 manipulate 할 수 없습니다 (Acceptance Criterion #7).
잔여 작업
M2 (다음 마일스톤):
- ContextGateway 통합
/predict라우트 + Presidio PII 가드 - KServe
canaryTrafficPercent또는 Istio VirtualService 정식 카나리 - Watch 루프 (
deploying → ready자동 전이) - UI:
DeployDialog,ModelDeploymentList,CanarySlider,DeploymentMetricsPanel - gend-cli
gend serving *5 명령어
M3 (그 다음):
- GPU 노드풀 + Triton ServingRuntime
- A/B Experiment + Shadow traffic
- MLflow signature 검증 게이트
- 드리프트 모니터링 hook (Kafka topic 발행)
관련
- Epic #1082 — Gap 2 MLflow → KServe 자동화
- ADR #1001 — ModelDeployment 명명 충돌 해소 (PR #1056)
- 인접 Epic #1083 — Dagster × MLflow 배치 추론 (Gap 3)
- 코드:
apps/api/src/gend_api/services/serving/(4 모듈)apps/api/src/gend_api/routers/serving.py(확장)apps/api/src/gend_api/db/models/serving.py(9 컬럼 추가)infra/helm/kserve/(Helm chart)infra/helm/gend-api/templates/rbac.yaml(Role + RoleBinding)