Published on

KServe+Istio 503와 콜드스타트 지연 해결 가이드

Authors

KServe를 Istio 위에 올려 InferenceService를 운영하다 보면, 트래픽이 없던 모델에 첫 요청을 보낼 때 503 이 터지거나 첫 응답이 수십 초까지 지연되는 경우가 흔합니다. 겉으로는 “콜드스타트가 느리다”로 뭉뚱그려지지만, 실제로는 Knative 스케일링(0에서 1), Istio 라우팅/리트라이, 사이드카 프록시 준비 상태, 프로브/타임아웃, 이미지 풀/모델 로딩이 겹쳐서 발생합니다.

이 글은 KServe+Istio 조합에서 자주 만나는 503 과 콜드스타트 지연을 증상별로 분해하고, 운영에서 효과가 컸던 진단 순서와 설정 변경을 정리합니다.

참고: 이미지 풀 단계에서 지연이 길다면 ImagePullBackOff 계열 이슈도 함께 점검하세요. 내부 글: K8s ImagePullBackOff 401 해결 - ECR·Secret

1) 먼저 503의 “출처”를 구분하자

503 은 한 가지 의미가 아닙니다. KServe+Istio에서는 보통 아래 3가지 중 하나입니다.

  1. Istio Envoy가 업스트림 엔드포인트를 못 찾음
    • 예: no healthy upstream, upstream connect error, cluster not found
  2. Knative가 아직 Pod를 준비 못 했거나 라우팅이 전환 중
    • 스케일 0에서 1로 올라오는 동안 라우팅이 빈 서비스로 향함
  3. 애플리케이션(모델 서버) 자체가 준비 전/오류
    • readiness는 통과했는데 실제 /v1/models 같은 엔드포인트가 늦게 준비되는 경우도 있음

빠른 판별 체크리스트

  • Istio 인그레스/게이트웨이 로그에서 503response_flags 확인
  • KServe InferenceService 상태에서 Ready 조건/URL 확인
  • Knative Revision/Pod 이벤트에서 스케일업 타이밍 확인

아래 명령으로 “어디서 503이 났는지”를 먼저 고정하세요.

# InferenceService 상태
kubectl -n ml get inferenceservice <name> -o yaml

# Knative revision / pod 이벤트
kubectl -n ml get ksvc
kubectl -n ml get revision
kubectl -n ml describe pod -l serving.kserve.io/inferenceservice=<name>

# Istio IngressGateway 로그(환경에 맞게 네임스페이스/파드명 조정)
kubectl -n istio-system logs deploy/istio-ingressgateway -c istio-proxy --tail=200

<name> 처럼 부등호가 들어가는 표기는 MDX에서 JSX로 오인될 수 있으니, 문서/명령 예시에서는 반드시 인라인 코드로 감싸는 습관을 추천합니다.

2) 콜드스타트 지연의 “구성 요소”를 나눠 측정하기

콜드스타트 지연은 대개 아래 구간 합입니다.

  • T1 라우팅 진입: 게이트웨이까지 도달
  • T2 스케일업: Knative가 0에서 1로 올림(Deployment/Revision 생성 포함)
  • T3 스케줄링: 노드에 배치
  • T4 이미지 풀: 컨테이너 이미지 pull
  • T5 사이드카 준비: Envoy/istio-proxy 초기화
  • T6 모델 서버 준비: 모델 로딩, 워커 기동
  • T7 readiness 통과 후 라우팅 전환

운영에서 가장 중요한 건 “느린 구간이 어디냐”입니다. 이벤트 타임라인을 보면 답이 빨리 나옵니다.

# Pod 이벤트 타임라인으로 스케줄링/풀/프로브 지연 확인
kubectl -n ml describe pod <pod-name>

# Knative activator / autoscaler 로그로 스케일업 지연 확인(설치 구성에 따라 네임스페이스 다름)
kubectl -n knative-serving logs deploy/activator --tail=200
kubectl -n knative-serving logs deploy/autoscaler --tail=200

3) 가장 흔한 원인 1: Istio 타임아웃/리트라이가 콜드스타트를 못 버팀

Istio는 기본적으로 요청을 업스트림으로 보내고, 업스트림 연결이 늦거나 준비가 안 되면 빠르게 실패시킬 수 있습니다. 특히 스케일 0에서 1로 올라오는 동안 첫 요청은 “기다려야” 정상인데, 게이트웨이/VirtualService의 타임아웃이 짧으면 503 으로 떨어집니다.

해결: VirtualService/Route에 적절한 timeout과 retry 정책

아래는 예시입니다. 핵심은

  • timeout 을 콜드스타트 최악값보다 크게
  • retries 는 과도하지 않게(모델 서버에 중복 부하를 줄 수 있음)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: kserve-<name>-vs
  namespace: ml
spec:
  hosts:
    - "<your-hostname>"
  gateways:
    - istio-system/<your-gateway>
  http:
    - route:
        - destination:
            host: <kservice-host>
            port:
              number: 80
      timeout: 60s
      retries:
        attempts: 3
        perTryTimeout: 20s
        retryOn: gateway-error,connect-failure,refused-stream,reset

주의할 점:

  • perTryTimeouttimeout 의 관계를 반드시 확인하세요.
  • retries가 많으면 “첫 요청” 하나가 내부적으로 여러 번 재시도되어, 스케일업 직후 모델 서버에 스파이크를 줄 수 있습니다.

4) 가장 흔한 원인 2: readiness/liveness 프로브가 모델 로딩 특성과 불일치

모델 서버는 컨테이너가 뜬 직후 바로 요청을 받을 수 없는 경우가 많습니다. 그런데 readiness 프로브가 너무 공격적이면

  • 준비되기 전에 트래픽이 들어와 503
  • 혹은 반대로, 준비는 됐는데 프로브 실패로 계속 Unready 같은 현상이 납니다.

체크 포인트

  • readiness가 “프로세스 살아있음”이 아니라 “실제 추론 준비 완료”를 의미하는지
  • 초기 로딩이 긴 모델에서 initialDelaySeconds 가 너무 짧지 않은지
  • failureThresholdperiodSeconds 조합이 현실적인지

KServe의 predictor 컨테이너에 프로브를 직접 넣는 패턴(커스텀 이미지)이라면 아래처럼 조정합니다.

readinessProbe:
  httpGet:
    path: /v1/models
    port: 8080
  initialDelaySeconds: 20
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 12

5) 가장 흔한 원인 3: Istio 사이드카 주입과 트래픽 전환 타이밍

사이드카(Envoy)가 준비되기 전에는 네트워크 경로가 불안정할 수 있고, holdApplicationUntilProxyStarts 설정 여부에 따라 애플리케이션이 먼저 떠버리기도 합니다. 이 경우 “앱은 준비됐다고 생각하지만 네트워크가 아직”인 미묘한 구간이 생깁니다.

해결 옵션

  • Istio의 프록시 준비를 애플리케이션 시작과 동기화
  • 또는 readiness를 네트워크까지 포함한 형태로 설계

환경에 따라 아래 애노테이션/설정을 사용합니다(버전별 차이가 있으니 클러스터 Istio 문서도 확인).

metadata:
  annotations:
    proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}'

6) 스케일 0이 문제라면: 최소 레플리카/스케일 다운 지연으로 완화

콜드스타트 자체를 없애려면 “0으로 내려가지 않게” 하는 게 가장 확실합니다. 비용과 지연 사이의 트레이드오프를 선택해야 합니다.

선택지 A: minReplicas를 1로

KServe InferenceService에서 최소 레플리카를 1로 유지하면 첫 요청 503 의 상당수를 제거할 수 있습니다.

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: <name>
  namespace: ml
spec:
  predictor:
    minReplicas: 1
    maxReplicas: 5
    model:
      modelFormat:
        name: sklearn
      storageUri: "s3://<bucket>/<path>"

선택지 B: scale down 지연(유휴 시간 늘리기)

완전 상시 1개가 부담되면, 유휴 시간이 짧은 서비스에 한해 스케일다운을 늦춰 “자주 깨는 콜드스타트”를 줄일 수 있습니다. 이는 Knative 설정(예: scale-to-zero-grace-period 등)과 연동되며, 설치 형태에 따라 ConfigMap을 조정합니다.

운영 팁: 트래픽 패턴이 “몇 분 간격”이면 스케일다운 지연이 비용 대비 효과가 큽니다.

7) 이미지 풀/모델 다운로드가 병목이면: 캐시와 프리풀 전략

콜드스타트가 긴데 이벤트를 보면 Pulling image 또는 모델 다운로드가 대부분인 경우가 있습니다.

이미지 풀 최적화

  • 노드에 이미지 캐시가 남도록 imagePullPolicy: IfNotPresent 고려
  • 프리풀 DaemonSet로 자주 쓰는 이미지 미리 pull
  • 레지스트리 네트워크/인증 문제 점검

ECR 인증/Secret 문제로 풀 지연 또는 실패가 나면 콜드스타트가 아니라 그냥 장애처럼 보일 수 있습니다. 이 케이스는 내부 글이 더 직접적입니다.

모델 아티팩트 다운로드 최적화

  • 모델을 원격 스토리지에서 받아오는 구조라면, 가능하면 노드 로컬 캐시/퍼시스턴트 볼륨 사용
  • 큰 모델은 압축/샤딩/메모리 매핑 등 로딩 방식 변경 검토

8) 관측(Observability): 503 재현이 어려울수록 로그/메트릭을 먼저 깔자

간헐적 503 은 “그 순간”의 증거가 없으면 끝까지 못 잡는 경우가 많습니다. 아래 3가지를 최소로 권합니다.

  • Istio access log에 response_code, response_flags, upstream_cluster, duration 포함
  • Knative autoscaler/activator 로그 보관
  • KServe controller 로그(리비전/트래픽 전환 이벤트)

또한 노드/시스템 로그가 폭주해 디스크가 꽉 차면, 그 자체로 스케줄링/이미지 풀/컨테이너 시작이 느려져 콜드스타트가 악화됩니다. 이런 “환경적 병목”도 함께 점검하세요.

9) 실전 트러블슈팅 플로우(권장 순서)

운영에서 시간을 아끼는 순서로 정리하면 다음과 같습니다.

  1. Istio ingress 로그에서 503 플래그 확인
    • no healthy upstream 이면 엔드포인트/ready 문제 가능성 큼
  2. InferenceService 상태와 Knative revision 상태 확인
    • Ready 조건이 흔들리는지, 트래픽이 어디로 가는지
  3. Pod 이벤트로 병목 구간 확인
    • 스케줄링 vs 이미지 풀 vs 프로브 vs 모델 로딩
  4. timeout/retry 조정으로 “기다릴 수 있게” 만들기
  5. minReplicas 또는 scale down 지연으로 콜드스타트 빈도 줄이기
  6. 이미지/모델 캐시로 콜드스타트 절대값 줄이기

10) 최소 변경으로 효과 보는 추천 조합

비용을 크게 늘리지 않으면서 체감 개선이 컸던 조합은 보통 아래입니다.

  • Istio timeout 을 콜드스타트 최악값보다 크게(예: 60초)
  • retries는 2~3회 정도로 제한하고 perTryTimeout 을 길게
  • readiness 프로브를 “실제 모델 준비” 기준으로 조정
  • 트래픽이 일정 주기로 들어오는 서비스는 scale down 지연을 늘리거나 minReplicas: 1

마무리

KServe+Istio에서 503 과 콜드스타트 지연은 단일 원인이 아니라, 스케일업과 네트워크 라우팅, 준비 상태 판정이 맞물려 나타나는 경우가 대부분입니다. 먼저 503의 출처를 분리하고, Pod 이벤트로 시간을 쪼개 측정한 뒤, Istio 타임아웃/리트라이와 프로브를 현실에 맞추면 “첫 요청 실패”의 상당수를 안정적으로 줄일 수 있습니다.

다음 단계로는 실제 트래픽 패턴에 맞춰 minReplicas 와 스케일다운 정책을 조합해, 비용과 지연의 균형점을 찾는 것이 운영 관점에서 가장 효과적입니다.