본문으로 건너뛰기

KServe 설치 + sklearn iris e2e

Epic #1082 (Model Serving 자동화) M1 PR #1104 가 추가한 services/serving/KServeDeployer + /api/v1/serving/deployments/{id}/deploy endpoint 가 실제 동작하려면 KServe controller + cert-manager 가 설치되어 있어야 한다. 본 가이드는 Issue #1111 의 운영 절차.

1. cert-manager 설치 (전제)

KServe webhook 은 TLS 가 필요 — cert-manager 가 자동 발급. AKS prod 에 미설치 시:

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--version v1.16.1 \
--set crds.enabled=true \
--kube-context aks-genos-prod

kubectl --context aks-genos-prod -n cert-manager rollout status \
deploy/cert-manager-webhook --timeout=180s

확인:

kubectl --context aks-genos-prod get pod -n cert-manager
# expected: cert-manager / cert-manager-cainjector / cert-manager-webhook 모두 Running

2. KServe controller 설치

PR #1104 의 infra/helm/kserve/ chart 를 적용:

# AKS prod
helm upgrade --install kserve infra/helm/kserve \
--namespace kserve --create-namespace \
-f infra/helm/kserve/values-azure.yaml \
--kube-context aks-genos-prod

kubectl --context aks-genos-prod -n kserve rollout status \
deploy/kserve-controller-manager --timeout=240s

Kind dev:

helm upgrade --install kserve infra/helm/kserve \
--namespace kserve --create-namespace \
-f infra/helm/kserve/values.yaml \
--kube-context kind-datax-local

확인:

kubectl --context aks-genos-prod get crd inferenceservices.serving.kserve.io
# expected: 1 row, age >= 1m

kubectl --context aks-genos-prod get clusterservingruntime kserve-mlserver
# expected: 1 row — Epic #1082 의 default runtime

3. gend-api RBAC 확인

PR #1104 가 추가한 infra/helm/gend-api/templates/rbac.yamlgend-serving-manager Role 이 gend-api ServiceAccount 에 바인딩되어 있어야 한다.

kubectl --context aks-genos-prod -n gend get role gend-serving-manager -o yaml
kubectl --context aks-genos-prod -n gend get rolebinding gend-serving-manager

부재 시 helm upgrade gend-api chart:

helm upgrade --install gend-api infra/helm/gend-api \
-n gend -f infra/helm/gend-api/values-azure.yaml \
--kube-context aks-genos-prod

4. sklearn iris e2e

scripts/e2e_kserve_iris.py 가 (a) 모델 학습 + MLflow 등록 (b) /api/v1/serving/deployments POST + deploy (c) Ready 대기 (d) /v1/models/<name>:predict 호출 + 분류 결과 단언 까지 자동화.

# 가상환경에 sklearn + mlflow + httpx 설치
pip install scikit-learn 'mlflow>=2.18' httpx

# 환경변수
export GEND_API_URL=https://gend.genon.ai
export GEND_API_JWT=$(./scripts/get_admin_token.sh) # admin JWT
export MLFLOW_TRACKING_URI=https://gend.genon.ai/mlflow
export GEND_WORKSPACE_SLUG=default

# 실행
python scripts/e2e_kserve_iris.py --cleanup

성공 출력 예:

[e2e_kserve_iris][register] training LogisticRegression
[e2e_kserve_iris][register] registered iris-classifier-e2e version=1
[e2e_kserve_iris][create] deployment id=<uuid> status=pending
[e2e_kserve_iris][deploy] deploy triggered status=deploying
[e2e_kserve_iris][wait] phase=deploying ready=False
[e2e_kserve_iris][wait] phase=ready ready=True
[e2e_kserve_iris][predict] response: {"predictions": [0, 2]}
[e2e_kserve_iris][assert] predictions OK: [0, 2]
[e2e_kserve_iris][cleanup] deleted deployment <uuid>
[e2e_kserve_iris][done] e2e PASS

재실행 시 --skip-register 옵션 (모델 재학습 생략).

5. 회귀 가드 — controller liveness

apps/api/tests/test_kserve_e2e_skip_if_no_controller.pykubectl get crd inferenceservices.serving.kserve.io 가용성 + kserve-controller-manager Ready + kserve-mlserver ServingRuntime 등록을 단언 (controller 부재 시 skip — CI/local 안전).

cd apps/api && .venv/bin/python -m pytest \
tests/test_kserve_e2e_skip_if_no_controller.py -v

KServe 미설치 환경: 3 tests SKIPPED (정상). KServe 설치 환경: 3 tests PASS.

6. 트러블슈팅

InferenceService Ready=False, RevisionMissing

증상: controller 가 InferenceService 를 reconcile 했으나 predictor pod 가 생성 안 됨.

원인 1: ServingRuntime 부재.

kubectl get clusterservingruntime
# expected: kserve-mlserver, kserve-sklearn 등

해결: helm 재배포.

원인 2: storageUri S3 secret 부재. KServe 가 s3://... 다운로드 시 kserve-s3-secret 이 필요.

kubectl -n gend get secret kserve-s3-secret
# expected: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ENDPOINT_URL

부재 시 infra/helm/kserve/templates/kserve-s3-secret.yaml example 참조하여 SealedSecret 발급.

webhook "validating.inferenceservices.kserve.io" denied the request

원인: cert-manager 가 KServe webhook TLS 발급 실패. cert-manager 가 fully Ready 인지 확인:

kubectl -n cert-manager get certificate -A
# expected: All READY=True

해결: cert-manager 재배포 + 5분 대기 후 KServe webhook recreate.

/v1/models/<name>:predict 401 / 403

KServe Ingress 가 oauth2-proxy 뒤에 있다면 JWT 필요. 내부 cluster IP 직호출:

kubectl port-forward -n gend svc/<deployment>-predictor 8080:80
curl localhost:8080/v1/models/<name>:predict -d '{"instances":[[5.1,3.5,1.4,0.2]]}'

모델 메모리 부족 (OOM)

기본 resource_profile=small 은 256Mi/200m. iris 같은 작은 모델은 충분하나 큰 모델은 medium (1Gi/500m) 또는 large (4Gi/2) 로 변경:

curl -X PATCH https://gend.genon.ai/api/v1/serving/deployments/<id> \
-H "Authorization: Bearer $JWT" \
-d '{"resource_profile": "medium"}'

7. Epic #1082 M2 — Mutation API + /predict 프록시 + ABAC

M2 가 추가하는 라우트 (/predictrequire_viewer, 나머지는 require_analyst):

메서드경로용도감사 키
PUT/api/v1/serving/deployments/{id}운영 노브 패치 (canary_percent / resource_profile / replicas). 식별자 컬럼 (model_name / model_version / endpoint_name) 은 422 로 거부 — re-target 은 DELETE + POST 로.model.deploy.update
POST/api/v1/serving/deployments/{id}/promote카나리 → 100% (KServeDeployer.apply_canary_traffic 호출 + DB 반영).model.deploy.promote
POST/api/v1/serving/deployments/{id}/rollback카나리 → 0%. (M1 라우트 + M2 audit emit).model.deploy.rollback
DELETE/api/v1/serving/deployments/{endpoint_name}소프트 삭제 (M1 의 하드 삭제 변경). status='deleted' + deployment_status='deleting' 로 마킹, 행 보존. 멱등.model.deploy.delete
POST/api/v1/serving/{deployment_name}/predictKServe v2 프로토콜 프록시. ABAC ModelGrant.invoke 검증 → 카나리 분기 → …-predictor.{ns}.svc.cluster.local/v2/models/{model}/infer 으로 forward.model.predict

7.1 /predict 프록시 흐름

7.2 호출 예시

# 카나리 50% 로 설정
curl -X PUT https://gend.genon.ai/api/v1/serving/deployments/$ID \
-H "Authorization: Bearer $JWT" \
-d '{"canary_percent": 50}'

# 안정 확인 후 100% 승격
curl -X POST https://gend.genon.ai/api/v1/serving/deployments/$ID/promote \
-H "Authorization: Bearer $JWT"

# 회귀 발견 시 즉시 롤백
curl -X POST https://gend.genon.ai/api/v1/serving/deployments/$ID/rollback \
-H "Authorization: Bearer $JWT"

# 추론 호출 (v2 Open Inference Protocol)
curl -X POST https://gend.genon.ai/api/v1/serving/iris-classifier/predict \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{"name": "input-0", "datatype": "FP32",
"shape": [1, 4], "data": [5.1, 3.5, 1.4, 0.2]}
]
}'

7.3 ABAC 사전 grant 등록

/predictModelGrantService.check_access(action='invoke') 가 True 일 때만 forward. 캘러가 호출 가능하려면 사전에 grant 행 필요:

curl -X POST https://gend.genon.ai/api/v1/model-grants \
-H "Authorization: Bearer $ADMIN_JWT" \
-d '{
"model_name": "iris",
"subject_type": "user",
"subject_id": "<keycloak sub>",
"action": "invoke"
}'
  • model_alias=null모든 alias 매칭 (와일드카드).
  • expires_at 누락 = 만료 없음.
  • 워크스페이스 fence: caller tenant_slug 의 workspace 로 stamp 됨.

7.4 보안 가드레일

  • URL 화이트리스트services/serving/inference_proxy.py:build_predictor_url 만이 predictor URL 을 구성. svc.cluster.local 외부 URL 은 거부. 회귀 가드: tests/test_no_kserve_endpoint_hardcoded.py.
  • ABAC fail-closed — grant 행 부재 = 403. 만료 행 (expires_at < now()) 은 매치 X.
  • 워크스페이스 fence — 행의 workspace_id ≠ caller tenant_slug workspace → 403 (404 아님 — audit 명확성).
  • 소프트 삭제status='deleted' 행에 /predict 호출 = 409. M3 에서 admin "purge" 라우트 추가 예정.

8. M3 (예정)

  • ContextGateway PII masking (/predict 입력/출력 본문).
  • DLQ — KServe 장기 장애 시 호출 retry queue.
  • Istio VirtualService — 헤더 기반 sticky-session 카나리 라우팅.
  • GPU 노드풀 + Triton ServingRuntime.
  • UI components/admin/ModelServing/ — DeployDialog / CanarySlider / PredictTester.
  • A/B Experiment + Shadow traffic + 드리프트 hook 통합.

관련

  • 부모 Epic #1082
  • 본 follow-up #1111
  • M1 PR #1104services/serving/ + Helm chart
  • KServe install #1119
  • M2 ABAC ModelGrant #1214 (사전 grant)
  • 메모리: feedback_kind_image_loading, feedback_datax_local_image_stale, project_vault_arch
  • 카나리 정책 상세: canary-deployment.md