- Published on
KServe+Istio 503와 콜드스타트 지연 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
KServe를 Istio 위에 올려 InferenceService를 운영하다 보면, 트래픽이 없던 모델에 첫 요청을 보낼 때 503 이 터지거나 첫 응답이 수십 초까지 지연되는 경우가 흔합니다. 겉으로는 “콜드스타트가 느리다”로 뭉뚱그려지지만, 실제로는 Knative 스케일링(0에서 1), Istio 라우팅/리트라이, 사이드카 프록시 준비 상태, 프로브/타임아웃, 이미지 풀/모델 로딩이 겹쳐서 발생합니다.
이 글은 KServe+Istio 조합에서 자주 만나는 503 과 콜드스타트 지연을 증상별로 분해하고, 운영에서 효과가 컸던 진단 순서와 설정 변경을 정리합니다.
참고: 이미지 풀 단계에서 지연이 길다면
ImagePullBackOff계열 이슈도 함께 점검하세요. 내부 글: K8s ImagePullBackOff 401 해결 - ECR·Secret
1) 먼저 503의 “출처”를 구분하자
503 은 한 가지 의미가 아닙니다. KServe+Istio에서는 보통 아래 3가지 중 하나입니다.
- Istio Envoy가 업스트림 엔드포인트를 못 찾음
- 예:
no healthy upstream,upstream connect error,cluster not found
- 예:
- Knative가 아직 Pod를 준비 못 했거나 라우팅이 전환 중
- 스케일 0에서 1로 올라오는 동안 라우팅이 빈 서비스로 향함
- 애플리케이션(모델 서버) 자체가 준비 전/오류
- readiness는 통과했는데 실제
/v1/models같은 엔드포인트가 늦게 준비되는 경우도 있음
- readiness는 통과했는데 실제
빠른 판별 체크리스트
- Istio 인그레스/게이트웨이 로그에서
503의response_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이미지 풀: 컨테이너 이미지 pullT5사이드카 준비: Envoy/istio-proxy 초기화T6모델 서버 준비: 모델 로딩, 워커 기동T7readiness 통과 후 라우팅 전환
운영에서 가장 중요한 건 “느린 구간이 어디냐”입니다. 이벤트 타임라인을 보면 답이 빨리 나옵니다.
# 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
주의할 점:
perTryTimeout과timeout의 관계를 반드시 확인하세요.- retries가 많으면 “첫 요청” 하나가 내부적으로 여러 번 재시도되어, 스케일업 직후 모델 서버에 스파이크를 줄 수 있습니다.
4) 가장 흔한 원인 2: readiness/liveness 프로브가 모델 로딩 특성과 불일치
모델 서버는 컨테이너가 뜬 직후 바로 요청을 받을 수 없는 경우가 많습니다. 그런데 readiness 프로브가 너무 공격적이면
- 준비되기 전에 트래픽이 들어와
503 - 혹은 반대로, 준비는 됐는데 프로브 실패로 계속 Unready 같은 현상이 납니다.
체크 포인트
- readiness가 “프로세스 살아있음”이 아니라 “실제 추론 준비 완료”를 의미하는지
- 초기 로딩이 긴 모델에서
initialDelaySeconds가 너무 짧지 않은지 failureThreshold와periodSeconds조합이 현실적인지
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) 실전 트러블슈팅 플로우(권장 순서)
운영에서 시간을 아끼는 순서로 정리하면 다음과 같습니다.
- Istio ingress 로그에서 503 플래그 확인
no healthy upstream이면 엔드포인트/ready 문제 가능성 큼
- InferenceService 상태와 Knative revision 상태 확인
- Ready 조건이 흔들리는지, 트래픽이 어디로 가는지
- Pod 이벤트로 병목 구간 확인
- 스케줄링 vs 이미지 풀 vs 프로브 vs 모델 로딩
- timeout/retry 조정으로 “기다릴 수 있게” 만들기
- minReplicas 또는 scale down 지연으로 콜드스타트 빈도 줄이기
- 이미지/모델 캐시로 콜드스타트 절대값 줄이기
10) 최소 변경으로 효과 보는 추천 조합
비용을 크게 늘리지 않으면서 체감 개선이 컸던 조합은 보통 아래입니다.
- Istio
timeout을 콜드스타트 최악값보다 크게(예: 60초) - retries는 2~3회 정도로 제한하고
perTryTimeout을 길게 - readiness 프로브를 “실제 모델 준비” 기준으로 조정
- 트래픽이 일정 주기로 들어오는 서비스는 scale down 지연을 늘리거나
minReplicas: 1
마무리
KServe+Istio에서 503 과 콜드스타트 지연은 단일 원인이 아니라, 스케일업과 네트워크 라우팅, 준비 상태 판정이 맞물려 나타나는 경우가 대부분입니다. 먼저 503의 출처를 분리하고, Pod 이벤트로 시간을 쪼개 측정한 뒤, Istio 타임아웃/리트라이와 프로브를 현실에 맞추면 “첫 요청 실패”의 상당수를 안정적으로 줄일 수 있습니다.
다음 단계로는 실제 트래픽 패턴에 맞춰 minReplicas 와 스케일다운 정책을 조합해, 비용과 지연의 균형점을 찾는 것이 운영 관점에서 가장 효과적입니다.