- Published on
KServe 추론 503? Istio·Knative 콜드스타트 진단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
KServe를 붙여놓고 첫 요청 또는 트래픽이 뜸한 구간에서 갑자기 503이 떨어지면, 체감상으로는 “모델 서버가 죽었나?”처럼 보이지만 실제로는 Istio 라우팅 계층과 Knative Serving의 스케일-투-제로 및 activator 경유 경로에서 발생하는 경우가 많습니다. 특히 KServe는 내부적으로 Knative Service를 만들고, Istio(또는 다른 Ingress)를 통해 들어온 요청이 여러 홉을 거치기 때문에 어느 레이어가 503을 만든 것인지부터 분리해야 합니다.
이 글은 다음 질문에 답하는 형태로 진행합니다.
- 503이 Envoy에서 나온 건지, activator에서 나온 건지, 모델 컨테이너에서 나온 건지 어떻게 구분할까
- 콜드스타트 중 503을 줄이기 위해 타임아웃/스케일링/프로브/리소스를 어디서 조정해야 할까
- 진단 시 어떤
kubectl명령과 어떤 로그를 우선 봐야 할까
관련해서 “모델 로딩이 길어지는 문제” 자체가 핵심일 때는 Ray Serve 배포 시 모델 로딩 지연·OOM 해결법도 함께 참고하면 좋습니다. KServe든 Ray Serve든, 결국 콜드스타트는 모델 로딩 시간과 메모리 압박으로 귀결되는 경우가 많습니다.
1) 503의 주체부터 식별하기: 어디서 503을 만들었나
같은 503이라도 “누가” 만들었는지에 따라 처방이 완전히 달라집니다.
- Istio IngressGateway(Envoy): upstream 연결 실패, 라우팅 실패, 타임아웃, mTLS 이슈 등
- Knative activator: 스케일-투-제로 상태에서 트래픽을 버퍼링/프록시하다가 준비가 늦어 타임아웃
- Queue-proxy / 사용자 컨테이너: 앱이 아직 준비 안 됨, readiness 실패, 내부 5xx
1-1) 클라이언트에서 503 응답 헤더로 단서 잡기
curl -v로 응답 헤더를 보면 Envoy가 찍는 헤더가 보이는 경우가 많습니다.
curl -v http://YOUR_INGRESS_HOST/v1/models/MODEL:predict \
-H 'Content-Type: application/json' \
-d '{"instances": [1,2,3]}'
다음과 같은 헤더/메시지가 보이면 Envoy 계층을 의심합니다.
server: istio-envoyx-envoy-upstream-service-time- 본문에
upstream connect error또는no healthy upstream
반면 Knative 경유 문제는 activator 로그에서 단서가 더 잘 나옵니다.
1-2) KServe가 만든 Knative Service와 Revision 상태 확인
KServe InferenceService는 내부적으로 Knative Service, Configuration, Revision을 만듭니다. 먼저 리소스가 정상인지 봅니다.
# InferenceService 상태
kubectl get inferenceservice -n YOUR_NS
kubectl describe inferenceservice YOUR_NAME -n YOUR_NS
# Knative Service / Revision 상태
kubectl get ksvc -n YOUR_NS
kubectl get revision -n YOUR_NS
kubectl describe ksvc YOUR_KSVC -n YOUR_NS
kubectl describe revision YOUR_REV -n YOUR_NS
여기서 자주 보이는 패턴은 다음과 같습니다.
Ready가Unknown또는False- Revision이 만들어졌는데 Pod가 안 뜸(스케줄링 실패)
- Pod는 떴는데
Ready가 안 됨(프로브 실패, 모델 로딩 지연)
Pod가 반복 재시작 중이면 503은 결과일 뿐이고, 원인은 CrashLoopBackOff일 수 있습니다. 이 경우는 K8s CrashLoopBackOff 원인 10가지·즉시 진단법 체크리스트로 빠르게 좁힐 수 있습니다.
2) 콜드스타트 경로 이해: 왜 activator가 끼면 503이 늘어날까
Knative의 스케일-투-제로는 비용 측면에서 유리하지만, 첫 요청은 다음 과정을 거칩니다.
- 요청이 Ingress(보통 Istio Gateway)로 들어옴
- 대상 Revision이
0replica이면 activator가 요청을 받아 대기/프록시 - autoscaler가 Pod를 띄움
- Pod가 이미지 pull, 컨테이너 시작, 모델 로딩
- readiness가 통과하면 트래픽이 Pod로 전달
문제는 2~4단계가 길어지면 activator 또는 Envoy에서 타임아웃이 발생해 503이 됩니다. 특히 모델이 크고 초기화가 길면(예: GPU 모델 로딩) “첫 요청이 항상 실패”하는 현상이 자주 생깁니다.
3) 가장 흔한 503 원인 6가지와 빠른 분기
3-1) 이미지 Pull 지연 또는 레지스트리 인증 문제
증상:
- 첫 요청 503
- Pod 이벤트에
Pulling image가 오래 걸리거나ImagePullBackOff
진단:
kubectl get pod -n YOUR_NS -l serving.knative.dev/service=YOUR_KSVC
kubectl describe pod POD_NAME -n YOUR_NS
대응:
- 노드에 이미지 캐시(프리풀), 레지스트리 네트워크 개선
imagePullSecrets확인- 이미지 크기 줄이기(멀티스테이지 빌드/캐시). 빌드 최적화는 GitHub Actions로 Docker 멀티스테이지·캐시 튜닝도 참고
3-2) 모델 로딩이 readiness 타임아웃을 초과
증상:
- Pod는 뜨지만
Ready가 늦게 뜨거나 안 뜸 - queue-proxy 또는 사용자 컨테이너 로그에 로딩이 오래 걸림
진단:
kubectl logs -n YOUR_NS POD_NAME -c kserve-container
kubectl logs -n YOUR_NS POD_NAME -c queue-proxy
kubectl describe pod POD_NAME -n YOUR_NS
대응 방향:
- readiness probe를 “모델 로딩 완료 후 OK”로 설계
- 초기 로딩을 줄이거나(가중치 최적화, lazy load), 워밍업 트래픽을 넣거나, 최소 replica를 1로 유지
3-3) Knative activator 타임아웃(콜드스타트가 너무 김)
증상:
- 스케일-투-제로에서 첫 요청이 503
- 두 번째 요청은 성공(이미 Pod가 떠서)
진단(activator 로그):
kubectl logs -n knative-serving -l app=activator
대응:
- 스케일-투-제로를 끄거나 최소 replica를 1로
- 요청 타임아웃을 늘리기(아래 설정 섹션 참고)
3-4) Istio 라우팅/엔드포인트 문제: no healthy upstream
증상:
- Envoy가 즉시 503 반환
- 메시지에
no healthy upstream
진단:
# Istio gateway 로그
kubectl logs -n istio-system -l app=istio-ingressgateway
# 서비스 엔드포인트 확인
kubectl get endpoints -n YOUR_NS
kubectl get svc -n YOUR_NS
원인 후보:
- readiness가 통과한 Pod가 없어 엔드포인트가 비어 있음
- DestinationRule, PeerAuthentication 등 mTLS 설정 충돌
3-5) 리소스 부족으로 스케줄링 지연 또는 OOM
증상:
- Pod가 Pending 오래 지속
- 또는 시작 후 OOMKilled
진단:
kubectl get pod -n YOUR_NS
kubectl describe pod POD_NAME -n YOUR_NS
kubectl get events -n YOUR_NS --sort-by=.lastTimestamp
대응:
- GPU 노드 수/쿼터 확인, 요청 리소스 조정
- 모델 메모리 최적화(양자화, 배치 제한 등)
3-6) 프로브 설계 오류: readiness가 트래픽을 막음
증상:
- 컨테이너는 정상 동작하는데 readiness만 실패
- 503이 지속
진단:
kubectl describe pod POD_NAME -n YOUR_NS | sed -n '1,200p'
대응:
- readiness는 “실제 추론 준비 완료”를 의미해야 함
- 단순히 포트 열림만 보는 probe는 콜드스타트에서 오히려 불안정할 수 있음
4) 설정으로 해결하기: 타임아웃, 최소 replica, 스케일링
아래 예시는 KServe InferenceService에 주석(annotations)을 통해 Knative 동작을 조정하는 패턴입니다. 클러스터 버전/설치 옵션에 따라 키가 다를 수 있으니, 적용 전 kubectl get ksvc -o yaml로 실제 반영 여부를 확인하세요.
4-1) 최소 replica를 1로 고정해 콜드스타트 자체를 제거
콜드스타트 503이 비즈니스적으로 치명적이면 가장 확실한 해결은 스케일-투-제로를 포기하는 것입니다.
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: my-model
namespace: ml
spec:
predictor:
minReplicas: 1
maxReplicas: 3
containers:
- name: kserve-container
image: your-registry/your-image:tag
ports:
- containerPort: 8080
KServe 버전에 따라 minReplicas 위치/지원이 다를 수 있습니다. 반영이 안 되면 Knative Service 레벨에서 autoscaling.knative.dev/minScale 주석을 쓰는 방식도 있습니다.
4-2) Knative 요청 타임아웃 늘리기
모델 로딩이 길어 첫 요청이 timeout으로 죽는다면, 서비스의 timeout을 늘려 “기다려줄 시간”을 확보합니다.
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: my-model
namespace: ml
annotations:
serving.knative.dev/timeoutSeconds: "300"
spec:
predictor:
containers:
- name: kserve-container
image: your-registry/your-image:tag
주의할 점:
- 타임아웃만 늘리면 동시 콜드스타트 요청이 몰릴 때 대기열이 길어져 전체 지연이 커질 수 있습니다.
- 근본적으로는 로딩 시간 단축 또는 최소 replica 유지가 더 안정적입니다.
4-3) 초기 burst 트래픽에서 스케일링 튜닝
콜드스타트 이후 갑자기 QPS가 올라가면, 스케일아웃이 늦어 503 또는 5xx가 날 수 있습니다. Knative autoscaling 관련 주석으로 목표 동시성 등을 조절합니다.
metadata:
annotations:
autoscaling.knative.dev/metric: "concurrency"
autoscaling.knative.dev/target: "2"
autoscaling.knative.dev/maxScale: "10"
여기서 target을 너무 낮추면 과도 스케일아웃, 너무 높이면 지연/타임아웃이 늘 수 있으니 실제 트래픽 패턴에 맞춰 조정합니다.
5) 로그/이벤트로 교차검증하는 “현장용” 진단 루틴
아래 순서대로 보면 503 원인을 빠르게 좁힐 수 있습니다.
5-1) 1분 안에 보는 스냅샷
kubectl get inferenceservice -n YOUR_NS
kubectl get ksvc,revision,pod -n YOUR_NS
kubectl get events -n YOUR_NS --sort-by=.lastTimestamp | tail -n 30
- Pod가 없으면 스케일-투-제로 또는 스케줄링 문제
- Pod가 Pending이면 리소스/노드 문제
- Pod가 Running인데 Ready가 아니면 프로브/로딩 문제
5-2) Ingress와 activator 로그 분리
# Istio ingressgateway
kubectl logs -n istio-system -l app=istio-ingressgateway --tail=200
# Knative activator
kubectl logs -n knative-serving -l app=activator --tail=200
- ingress에서
upstream reset류가 보이면 네트워크/엔드포인트 - activator에서 대기/timeout 흔적이 보이면 콜드스타트 타임아웃
5-3) 실제 모델 컨테이너 로그 확인
kubectl logs -n YOUR_NS POD_NAME -c kserve-container --tail=200
여기서 로딩 단계가 어디서 오래 걸리는지(가중치 다운로드, GPU 초기화, 토크나이저 로딩 등)를 구체적으로 확인해야 합니다. “모델 로딩이 늘어지는 패턴” 자체는 KServe에 국한되지 않으므로, 로딩을 줄이는 전술은 앞서 언급한 글과 유사한 접근이 가능합니다.
6) 503을 줄이는 운영 패턴: 워밍업, 프리풀, 관측 가능성
6-1) 워밍업 트래픽 넣기
스케일-투-제로를 유지해야 한다면, 일정 주기로 가벼운 요청을 넣어 0으로 내려가는 시간을 늦추는 방법이 있습니다. 단, 이건 비용 절감 효과를 일부 포기하는 것이므로 “최소 replica 1”과 경제성 비교를 해야 합니다.
# 예: 크론잡/외부 스케줄러에서 주기 호출
curl -sS http://YOUR_INGRESS_HOST/v1/models/MODEL:predict \
-H 'Content-Type: application/json' \
-d '{"instances": [0]}' >/dev/null
6-2) 노드에 이미지 프리풀
이미지 pull이 병목이면 DaemonSet으로 프리풀하거나, 노드 이미지 캐시를 활용합니다. 특히 GPU 노드는 이미지가 커지는 경향이 있어 효과가 큽니다.
6-3) SLO 기준으로 timeout과 minScale 결정을 문서화
- 첫 요청 성공률이 중요하면
minScale=1 - 비용이 중요하고 첫 요청 실패가 허용되면 스케일-투-제로 + 워밍업 + timeout 증가
이 결정을 팀 SLO로 합의해두면, 장애 때 “왜 503이 나냐”를 기술적 논쟁으로 끌고 가지 않고 운영 정책으로 정리할 수 있습니다.
7) 체크리스트 요약
- 503 응답 헤더에서
istio-envoy여부 확인 kubectl get inferenceservice,kubectl get ksvc,revision,pod로 리소스 상태 스냅샷- 이벤트에서
ImagePullBackOff,FailedScheduling,OOMKilled여부 확인 istio-ingressgateway로그와activator로그를 분리해서 원인 레이어 확정- 콜드스타트가 원인이면
minReplicas또는autoscaling.knative.dev/minScale, 그리고serving.knative.dev/timeoutSeconds조정 - 모델 로딩이 원인이면 로딩 시간 단축, 이미지 최적화, 워밍업 전략 적용
콜드스타트 503은 “한 군데 설정을 바꾸면 끝”인 문제가 아니라, 요청 경로의 어느 구간에서 시간이 소모되는지를 먼저 계측하고, 그 구간에 맞는 처방(스케일 정책, 타임아웃, 프로브, 리소스, 이미지/모델 최적화)을 적용해야 안정적으로 해결됩니다.