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.yaml 의 gend-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.py 가 kubectl 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 가 추가하는 라우트 (/predict 만 require_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}/predict | KServe 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 등록
/predict 는 ModelGrantService.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≠ callertenant_slugworkspace → 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 통합.