Published on

KServe+Istio 카나리 배포 - 503·드리프트 대응

Authors

모델 배포에서 가장 비싼 장애는 “새 모델이 잘못된 걸 늦게 알아차리는 것”입니다. 그래서 카나리 배포로 일부 트래픽만 새 모델에 흘려보고, 지표가 나빠지면 즉시 롤백하는 흐름이 중요합니다.

KServe는 InferenceService 단위로 모델 서버를 운영하고, Istio는 라우팅과 트래픽 분할(가중치 기반)을 담당합니다. 둘을 결합하면 “모델 리비전 단위 카나리 + 서비스 메시 수준의 관측/제어”를 깔끔하게 만들 수 있습니다.

이 글은 다음을 목표로 합니다.

  • KServe에서 안정(stable)과 카나리(canary) 리비전을 동시에 띄우고 Istio로 가중치 라우팅하기
  • 카나리 중 자주 만나는 503의 대표 원인과 대응 체크리스트 정리하기
  • 모델 드리프트(성능 저하, 입력 분포 변화) 감지와 자동 롤백/승격 전략 만들기

아키텍처 개요: KServe 리비전 + Istio 트래픽 분할

핵심은 “KServe가 만든 백엔드(리비전)들”을 “Istio VirtualService/DestinationRule로 가중치 분할”하는 것입니다.

  • KServe
    • InferenceService로 predictor를 정의
    • canaryTrafficPercent로 카나리 비율을 선언하거나(환경에 따라 내부적으로 Istio 리소스 생성)
    • 또는 직접 Istio 리소스를 만들어 더 세밀하게 제어
  • Istio
    • DestinationRule로 subset(예: stable, canary) 정의
    • VirtualService로 weight 기반 라우팅

주의할 점은 설치 조합에 따라 KServe가 Istio 리소스를 자동 생성하기도 하고, Knative 기반/RawDeployment 기반 여부에 따라 리비전과 라우팅 방식이 달라질 수 있다는 것입니다. 운영 표준을 정할 때는 “누가 라우팅 리소스를 소유하는가”를 먼저 합의하는 게 좋습니다.

전제: 네임스페이스와 사이드카, mTLS

Istio를 쓰는 순간, 모델 파드에는 Envoy 사이드카가 붙습니다. 이때 다음이 흔한 전제 조건입니다.

  • 네임스페이스에 사이드카 자동 주입 레이블
  • mTLS 모드(STRICT/PERMISSIVE) 정리
  • Ingress Gateway 또는 내부 Mesh 라우팅 경로 확정

사이드카 관련 종료/라이프사이클 이슈는 모델 서버의 graceful shutdown과도 충돌할 수 있습니다. 종료 순서 문제로 요청이 끊기는 케이스는 아래 글도 함께 보면 좋습니다.

KServe InferenceService: stable + canary 선언

가장 단순한 형태는 KServe의 카나리 필드를 사용하는 것입니다. 아래 예시는 predictor로 sklearnserver를 쓰는 형태(예시)입니다.

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: churn-model
  namespace: ml
spec:
  predictor:
    sklearn:
      storageUri: s3://ml-models/churn/stable/
  canaryTrafficPercent: 10
  canaryPredictor:
    sklearn:
      storageUri: s3://ml-models/churn/canary/

포인트는 다음과 같습니다.

  • predictor가 stable
  • canaryPredictor가 canary
  • canaryTrafficPercent가 트래픽 비율

환경에 따라 KServe가 Istio 리소스를 자동으로 만들어주지만, 운영에서 “더 확실한 통제(서브셋, 헤더 기반, 사용자군 기반, 실험 플래그 연동)”가 필요하면 직접 Istio 라우팅을 정의하는 편이 낫습니다.

Istio로 직접 트래픽 분할하기: DestinationRule + VirtualService

직접 제어 방식의 기본은 다음입니다.

  1. stable, canary를 구분할 라벨을 파드에 부여
  2. DestinationRule의 subset에 라벨 셀렉터를 매핑
  3. VirtualService에서 weight로 분할

먼저 DestinationRule 예시입니다.

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: churn-model-dr
  namespace: ml
spec:
  host: churn-model-predictor.ml.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
    - name: stable
      labels:
        serving.kserve.io/revision: churn-model-stable
    - name: canary
      labels:
        serving.kserve.io/revision: churn-model-canary

다음은 VirtualService 예시입니다.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: churn-model-vs
  namespace: ml
spec:
  hosts:
    - churn-model-predictor.ml.svc.cluster.local
  http:
    - route:
        - destination:
            host: churn-model-predictor.ml.svc.cluster.local
            subset: stable
          weight: 90
        - destination:
            host: churn-model-predictor.ml.svc.cluster.local
            subset: canary
          weight: 10
      timeout: 10s
      retries:
        attempts: 2
        perTryTimeout: 3s
        retryOn: connect-failure,refused-stream,unavailable,cancelled

여기서 timeoutretries는 503 체감에 직접적인 영향을 줍니다. “모델이 느려서 503이 난다”처럼 보이지만 실제로는 메시 레벨 타임아웃이 먼저 터지는 경우가 많습니다.

카나리 중 503이 터질 때: 원인별 체크리스트

503은 “백엔드가 준비되지 않았거나, 라우팅/연결이 실패했거나, 타임아웃/리트라이 정책이 부적절하거나” 같은 넓은 원인을 가집니다. 모델 카나리에서는 특히 아래 6가지가 자주 겹칩니다.

1) 준비되지 않은 리비전으로 트래픽이 들어감

  • 증상
    • 배포 직후 카나리 비율을 올리면 503이 급증
    • kubectl describe에서 readiness 불안정
  • 원인
    • 모델 다운로드(storageUri)가 느림
    • 워밍업이 길어 readiness가 늦음
    • 오토스케일이 0에서 깨어나는 cold start
  • 대응
    • 모델 아티팩트 크기/다운로드 경로 최적화
    • minReplicas를 두어 cold start 완화
    • readiness probe를 “진짜 준비 완료” 기준으로 설계

KServe/Knative 조합에서는 scale-to-zero가 기본인 경우가 많아, 카나리 첫 요청이 특히 취약합니다.

2) Istio mTLS 모드 불일치

  • 증상
    • 특정 경로에서만 503 UC 또는 upstream connect error
    • 카나리만 실패(라벨/서브셋이 다르게 적용)
  • 원인
    • PeerAuthentication이 STRICT인데 대상 워크로드가 사이드카 없이 뜸
    • DestinationRule TLS 모드가 맞지 않음
  • 대응
    • 워크로드에 사이드카 주입 여부 확인
    • PeerAuthentication 범위(네임스페이스/워크로드 셀렉터) 확인
    • DestinationRuleISTIO_MUTUAL 적용 확인

3) VirtualService/DestinationRule subset 라벨 매칭 실패

  • 증상
    • weight를 올려도 트래픽이 한쪽으로만 가거나
    • 특정 subset이 엔드포인트 0개로 잡혀 503 no healthy upstream
  • 원인
    • subset 라벨 키/값이 실제 파드 라벨과 다름
    • KServe 리비전 라벨이 기대와 다름(버전/설치 방식 차이)
  • 대응
    • kubectl get pod -n ml --show-labels로 실제 라벨 확인
    • istioctl proxy-config endpoints로 Envoy가 보는 엔드포인트 확인

4) 타임아웃이 모델 지연보다 짧음

  • 증상
    • 로그상 모델은 응답했는데 클라이언트는 503 또는 504
    • P95/P99 지연이 높을수록 에러가 비례 증가
  • 원인
    • VirtualService timeout이 너무 짧음
    • 업스트림(모델)에서 배치/토크나이즈/후처리로 지연 증가
  • 대응
    • 메시 타임아웃, 클라이언트 타임아웃, 서버 타임아웃을 일관되게 정렬
    • 지연이 큰 요청은 비동기화 또는 큐잉 고려

gRPC를 쓰는 경우 타임아웃은 더 자주 문제를 만듭니다. 원인별로 정리한 글을 같이 보면 트러블슈팅 속도가 빨라집니다.

5) 리트라이가 503을 증폭시키는 경우

  • 증상
    • 503이 순간적으로 폭발하고, CPU/메모리도 같이 튐
  • 원인
    • 실패한 요청을 메시가 재시도하면서 모델 서버에 부하를 더 줌
    • 특히 cold start 구간에서 리트라이가 “스파이크 증폭기” 역할
  • 대응
    • attempts를 낮추거나, retryOn을 제한
    • 카나리 구간에서는 리트라이를 보수적으로 운영
    • 백오프가 필요한 경우 애플리케이션 레벨에서 제어

6) HPA/KPA 스케일링과 카나리 비율 상승 타이밍 충돌

  • 증상
    • 카나리 비율을 10에서 30으로 올리는 순간 503
  • 원인
    • 트래픽 증가에 비해 스케일 아웃이 늦음
    • 스케일 기준이 RPS가 아니라 CPU라서 반응이 느림
  • 대응
    • 최소 레플리카를 두거나
    • 스케일링 지표를 모델 특성에 맞게 재설계
    • “비율 상승은 단계적으로, 단계마다 관측 후 진행”을 룰로 고정

503을 빠르게 분해하는 관측 포인트

카나리 상황에서는 “원인이 모델인가, 메시인가, 인프라인가”를 빨리 나눠야 합니다.

  • Envoy 액세스 로그에서 response_flags 확인
    • 예: UF, UC, UO 등은 upstream 연결/헬스 문제를 강하게 시사
  • Prometheus 지표
    • istio_requests_total의 응답 코드 분포
    • istio_request_duration_milliseconds 지연
  • KServe 컨트롤 플레인 이벤트
    • 리비전 준비, 스토리지 다운로드 실패, 이미지 풀 실패 등

운영 팁은 “카나리 비율을 올리기 전에, 카나리 엔드포인트가 이미 warm 상태인지”를 확인하는 것입니다. 가능하면 사전 워밍업 잡을 돌려 모델을 메모리에 올린 뒤 가중치를 올리세요.

드리프트 대응: 카나리는 단순 장애뿐 아니라 품질 저하를 잡는다

드리프트는 크게 두 종류로 나뉩니다.

  • 데이터 드리프트: 입력 분포가 바뀜(예: 신규 국가 유입, UI 변경으로 피처 분포 변화)
  • 컨셉 드리프트: P(y|x) 자체가 바뀜(예: 정책/가격 변경으로 고객 행동 변화)

카나리 배포에서 드리프트 대응의 핵심은 “온라인 지표(즉시) + 지연 라벨 지표(나중)”를 함께 보는 것입니다.

즉시 볼 수 있는 온라인 지표

  • 입력 피처 분포 변화
    • 평균/분산, 분위수, 범주형 분포
    • PSI(Population Stability Index) 같은 간단한 스코어
  • 모델 출력 분포 변화
    • 확률 예측의 평균/엔트로피
    • 상위 클래스 비율 변화
  • 시스템 지표
    • 지연, 타임아웃, 5xx, 리소스 사용량

지연 라벨이 필요한 품질 지표

  • 실제 전환/이탈 등 라벨이 늦게 도착하는 경우
    • AUC, LogLoss, calibration error 등은 “나중에” 계산
    • 그래서 카나리 초반에는 대체 지표(프록시)를 둬야 함

카나리 승격/롤백 기준을 룰로 고정하기

사람이 대시보드 보고 판단하면 늦습니다. 최소한 아래는 자동화 룰로 박아두는 편이 좋습니다.

  • 즉시 롤백(강제)
    • 5xx 비율이 stable 대비 X배 이상
    • P95 지연이 stable 대비 Y배 이상
    • no healthy upstream 같은 인프라성 플래그 급증
  • 승격 보류
    • 입력 분포 PSI가 임계치 초과
    • 출력 분포가 급격히 변함(예: 평균 확률이 급락)
  • 점진 승격
    • 1단계: 1%
    • 2단계: 5%
    • 3단계: 10%
    • 4단계: 25%
    • 5단계: 50%
    • 6단계: 100%

각 단계마다 최소 관측 시간(예: 15분 또는 1시간)을 두고, 트래픽이 충분히 쌓였는지(통계적 유의성)도 체크합니다.

예시: 헤더 기반 “테스터 우선” 카나리 라우팅

가중치 분할만 쓰면 “운 나쁘게 중요한 고객이 카나리를 맞는” 일이 생깁니다. 운영에서는 먼저 내부 사용자나 특정 테넌트에만 노출하는 방식을 많이 씁니다.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: churn-model-vs
  namespace: ml
spec:
  hosts:
    - churn-model-predictor.ml.svc.cluster.local
  http:
    - match:
        - headers:
            x-canary-user:
              exact: "true"
      route:
        - destination:
            host: churn-model-predictor.ml.svc.cluster.local
            subset: canary
          weight: 100
      timeout: 10s
    - route:
        - destination:
            host: churn-model-predictor.ml.svc.cluster.local
            subset: stable
          weight: 100
      timeout: 10s

이 방식은 “테스터 트래픽으로 워밍업 및 품질 검증”을 하고, 이후에 weight 카나리로 넘어가기 좋습니다.

모델 서버 레벨에서 503을 줄이는 실전 팁

  • readiness를 “프로세스 기동”이 아니라 “모델 로드 완료”로 설정
  • 모델 로드/토크나이즈 같은 무거운 초기화는 시작 시점에 수행
  • 워커 수, 스레드 수, 배치 전략을 리소스 한도에 맞춤
  • 요청 바디 제한, 입력 검증으로 비정상 요청이 리소스를 잠식하지 않게 함

로컬 LLM이나 대형 모델을 올릴 때는 OOM과 지연 최적화가 카나리 성공률을 크게 좌우합니다. 아래 글은 성능 최적화 관점에서 도움이 됩니다.

운영 플로우 제안: “503 대응”과 “드리프트 대응”을 하나로 묶기

카나리 운영을 절차로 정리하면 다음이 깔끔합니다.

  1. 배포 전
    • 카나리 리비전 minReplicas로 워밍업
    • smoke test(고정 입력)로 기능 확인
  2. 1차 노출
    • 헤더 기반으로 내부 사용자에게만 100% 카나리
    • 503, 지연, 기본 출력 분포 확인
  3. 가중치 카나리
    • 1%부터 단계 상승
    • 단계마다 SLO(5xx, P95)와 드리프트 지표(PSI, 출력 분포) 평가
  4. 자동 롤백
    • 임계치 초과 시 즉시 stable 100%로 전환
    • 원인 분석 후 재배포
  5. 승격
    • 100% 전환 후에도 일정 시간 관측

이때 “503은 인프라/라우팅 문제, 드리프트는 품질 문제”처럼 분리해서 보지 말고, 동일한 카나리 게이트에서 함께 차단하는 게 좋습니다. 새 모델이 느려져 타임아웃이 늘면(시스템 문제) 결국 품질 지표도 왜곡되기 때문입니다.

마무리

KServe와 Istio 조합은 모델 카나리 배포를 강력하게 만들어주지만, 그만큼 503의 원인 공간도 넓어집니다. 운영에서 중요한 건 “503을 단일 현상으로 보지 않고, 준비 상태, mTLS, 라벨 매칭, 타임아웃/리트라이, 스케일링”으로 빠르게 분해하는 관측 루틴입니다.

그리고 카나리의 진짜 목적은 단순 무중단 배포가 아니라 “드리프트를 빠르게 발견하고 안전하게 롤백하는 것”입니다. 온라인 지표(즉시)와 지연 라벨 지표(나중)를 함께 설계하고, 승격/롤백을 룰로 고정하면 모델 배포의 실패 비용을 크게 줄일 수 있습니다.