- Published on
KServe+Istio에서 모델 503 해결 7단계
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
KServe를 Istio 위에 올려 운영하다 보면, 모델이 분명 배포됐는데도 요청이 503 Service Unavailable 로 떨어지는 순간이 자주 옵니다. 특히 트래픽이 들어오는 경로가 Gateway -> VirtualService -> (KServe가 만든) Knative Service/Revision -> Pod 로 길어지면서, 503이 “어디에서” 발생했는지부터 헷갈리기 쉽습니다.
이 글은 503을 7단계로 분해해서, 각 단계에서 무엇을 확인하고 어떤 커맨드로 증거를 모을지 정리한 실전 가이드입니다. 목표는 감으로 재배포하는 게 아니라, 증상-원인-조치를 빠르게 연결하는 것입니다.
참고: 네트워크 타임아웃/라우팅 문제를 체계적으로 좁혀가는 접근은 EKS Pod→S3 504 타임아웃 - VPC 엔드포인트·NAT·DNS 진단 글의 “경로 분해” 방식과 유사합니다.
503의 “발생 지점”부터 구분하기
Istio 환경에서 503은 대체로 아래 중 하나입니다.
- Envoy가 업스트림을 못 찾음: 라우팅 대상 엔드포인트가 0개, 혹은 subset/port mismatch
- 업스트림 연결 실패:
connection refused,TLS handshake error - 업스트림이 과부하/준비 안 됨: readiness 실패, scale-to-zero에서 콜드스타트, 큐 프록시 문제
- 권한/정책으로 차단: AuthorizationPolicy, PeerAuthentication, mTLS 모드 불일치
먼저 클라이언트에서 받은 응답 헤더/바디에 단서가 많습니다.
# 예: 외부에서 호출
curl -sv https://YOUR_HOST/v1/models/MODEL:predict \
-H 'Content-Type: application/json' \
-d '{"instances":[1,2,3]}'
확인 포인트:
- 응답 헤더에
server: istio-envoy가 보이면, 최소한 게이트웨이/사이드카 레벨에서 생성된 응답일 가능성이 큽니다. - 바디에
no healthy upstream같은 문자열이 있으면 “엔드포인트 0개” 계열로 거의 확정입니다.
이제부터는 경로를 따라가며 7단계로 좁힙니다.
1단계: InferenceService 상태부터 확정하기
KServe 리소스가 Ready가 아니면, 뒤에서 아무리 Istio를 만져도 503이 반복됩니다.
kubectl get inferenceservice -A
kubectl get inferenceservice YOUR_ISVC -n YOUR_NS -o yaml
체크리스트:
status.conditions에서Ready=True인지PredictorReady가 False면 predictor 배포가 안 된 상태status.url이 비어 있거나, 예상과 다른 호스트라면 라우팅 구성부터 재점검
자주 나오는 원인:
- 이미지 풀 실패, 모델 다운로드 실패, 스토리지 접근 실패
- 리소스 부족으로 스케줄링 실패
이 단계에서 이미 kubectl describe 로 이벤트가 쏟아지는 경우가 많습니다.
kubectl describe inferenceservice YOUR_ISVC -n YOUR_NS
2단계: 실제 트래픽 엔드포인트(Revision/Pod)가 존재하는지
KServe는 내부적으로 Knative Serving 리소스를 생성합니다(설치 형태에 따라 다르지만, 많은 배포에서 그렇습니다). 503의 흔한 원인은 “라우팅은 됐는데 실제 엔드포인트가 없다” 입니다.
kubectl get ksvc -n YOUR_NS
kubectl get revision -n YOUR_NS
kubectl get pod -n YOUR_NS -owide
체크리스트:
- predictor pod가 Running인지
- Ready가 0/1로 남아 있다면 readiness probe 실패
- scale-to-zero 상태에서 트래픽이 들어오는데도 스케일이 안 올라오면 오토스케일/큐 프록시 문제
Pod 로그도 바로 봅니다.
# predictor 컨테이너 로그
kubectl logs -n YOUR_NS deploy/YOUR_ISVC-predictor -c kserve-container --tail=200
# 사이드카/큐프록시가 있는 경우
kubectl logs -n YOUR_NS pod/YOUR_POD -c queue-proxy --tail=200
kubectl logs -n YOUR_NS pod/YOUR_POD -c istio-proxy --tail=200
여기서 모델 로딩이 오래 걸리면, 콜드스타트 동안 503이 발생할 수 있습니다. 이 경우는 뒤 단계(타임아웃/프로브)에서 더 구체적으로 잡습니다.
3단계: Service/Endpoints가 0인지 확인 (가장 흔한 503)
Envoy가 no healthy upstream 을 내는 전형적인 케이스가 “Kubernetes Endpoints가 비어 있음” 입니다.
kubectl get svc -n YOUR_NS
kubectl get endpoints -n YOUR_NS
# 특정 서비스의 엔드포인트 확인
kubectl get endpoints YOUR_SERVICE -n YOUR_NS -o yaml
원인 패턴:
- Service selector가 Pod label과 불일치
- Pod는 떠 있는데 readiness가 false라 endpoints에 안 붙음
- 포트 이름/포트 번호 불일치 (Istio 라우팅에서 특히 문제)
조치 방향:
kubectl describe svc로 selector 확인kubectl get pod --show-labels로 라벨 비교- readiness probe 실패면 5단계에서 해결
4단계: Istio Gateway/VirtualService 라우팅 정합성
외부에서 호출하는 경우, 대개 Gateway 와 VirtualService 를 거칩니다. 호스트/경로/포트가 조금만 어긋나도 503이 납니다.
kubectl get gateway,virtualservice -A
kubectl get virtualservice YOUR_VS -n YOUR_NS -o yaml
kubectl get gateway YOUR_GW -n istio-system -o yaml
체크리스트:
hosts가 실제 요청 Host 헤더와 일치하는지gateways참조가 맞는지http.match의 prefix/path가 실제 요청 경로와 맞는지route.destination.host가 올바른 서비스 DNS인지destination.port.number가 서비스 포트와 동일한지
실전 팁: 라우팅이 애매할 때는 게이트웨이 Envoy 로그나 접근 로그를 켜서 “어떤 라우트가 매칭되었는지”를 보는 게 빠릅니다.
# istio ingressgateway 프록시 로그
kubectl logs -n istio-system deploy/istio-ingressgateway -c istio-proxy --tail=200
5단계: readiness/liveness, 모델 로딩 시간, 프로브 설계
모델 서버는 시작 시 모델 로딩으로 수십 초에서 수 분까지 걸릴 수 있습니다. 이때 readiness가 너무 공격적이면 endpoints가 붙지 않아 503이 납니다.
확인:
kubectl describe pod YOUR_POD -n YOUR_NS
kubectl get pod YOUR_POD -n YOUR_NS -o jsonpath='{.status.containerStatuses[*].ready}'
자주 보이는 이벤트:
Readiness probe failedBack-off restarting failed container
조치 예시(개념):
- 초기 로딩이 긴 경우
startupProbe를 사용하거나 readiness의initialDelaySeconds를 늘립니다. - 모델 로딩 완료 후에만 readiness가 true가 되도록 엔드포인트(
/health,/ready)를 분리합니다.
KServe/Knative 조합에서는 큐 프록시가 앞단에 있어 “애플리케이션이 뜨는 시간”과 “트래픽을 받을 준비가 된 시간”이 엇갈릴 수 있습니다. 이때는 다음도 같이 봅니다.
queue-proxy로그에서 요청이 들어왔는지- 오토스케일이 실제로 0에서 1로 올라갔는지
6단계: Istio mTLS 및 정책(AuthorizationPolicy)로 인한 차단
503이 “권한 문제”처럼 보이지 않게 나타나는 경우가 있습니다. 특히 mTLS 모드가 STRICT 인데, 대상 워크로드가 기대한 방식으로 통신하지 못하면 연결 실패로 503이 터질 수 있습니다.
확인할 리소스:
kubectl get peerauthentication -A
kubectl get authorizationpolicy -A
kubectl get destinationrule -A
체크리스트:
- 네임스페이스에
PeerAuthentication이STRICT인지 - 특정 워크로드에
AuthorizationPolicy가 deny로 걸려 있지 않은지 DestinationRule의 TLS 모드가 서비스 호출 방식과 일치하는지
트러블슈팅 팁:
istio-proxy로그에rbac_access_denied가 보이면 AuthorizationPolicy 가능성이 큼- TLS 관련 에러(핸드셰이크 실패)가 보이면 PeerAuthentication/DestinationRule 조합을 의심
7단계: 타임아웃, 리트라이, 커넥션 풀, 그리고 “간헐적 503”
모든 설정이 맞는데도 503이 간헐적으로 뜬다면, 대개는 아래 범주입니다.
- 업스트림이 순간적으로 과부하로 응답 불가
- Envoy 타임아웃이 모델 추론 시간보다 짧음
- 리트라이가 오히려 폭주를 키움
- 커넥션 재사용/HTTP2 설정 문제로 특정 상황에서만 실패
VirtualService에서 타임아웃/리트라이를 명시해 “기본값”에 끌려다니지 않게 만드는 게 좋습니다.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: model-vs
namespace: YOUR_NS
spec:
hosts:
- your.model.host
gateways:
- istio-system/your-gateway
http:
- route:
- destination:
host: your-service.YOUR_NS.svc.cluster.local
port:
number: 80
timeout: 30s
retries:
attempts: 2
perTryTimeout: 10s
retryOn: gateway-error,connect-failure,refused-stream,reset
추론 시간이 길거나 배치 요청이 큰 경우, timeout 을 늘리는 것만으로 해결되는 케이스가 많습니다. 반대로 모델이 무거운데 retries를 과하게 두면, 실패 순간에 트래픽이 증폭되어 더 많은 503을 만들 수 있습니다.
간헐적 네트워크/경로 문제를 추적하는 관점에서는 “어느 홉에서 지연이 커지는지”를 나눠 보는 게 핵심인데, 이 접근은 EKS Pod→S3 504 타임아웃 - VPC 엔드포인트·NAT·DNS 진단 에서도 동일하게 적용됩니다.
빠른 결론: 503을 7개의 질문으로 바꾸기
503을 보면 바로 아래 7개 질문으로 변환해 보세요.
- InferenceService가 Ready인가
- 실제 predictor pod가 존재하고 Running인가
- Service의 endpoints가 0인가
- Gateway/VirtualService의 host, path, port, destination이 맞는가
- readiness/liveness가 모델 로딩 특성과 맞는가
- mTLS/AuthorizationPolicy/DestinationRule이 통신을 막고 있지 않은가
- timeout, retry, 부하 패턴 때문에 간헐적 503이 생기지 않는가
이 순서대로 보면 “재배포로 운 좋게 해결” 같은 흐름에서 벗어나, 503을 재현 가능하고 설명 가능한 문제로 바꿀 수 있습니다.
부록: 현장에서 바로 쓰는 커맨드 묶음
# 1) KServe 상태
kubectl get isvc -A
kubectl describe isvc YOUR_ISVC -n YOUR_NS
# 2) Pod/로그
kubectl get pod -n YOUR_NS -owide
kubectl describe pod YOUR_POD -n YOUR_NS
kubectl logs -n YOUR_NS pod/YOUR_POD -c kserve-container --tail=200
kubectl logs -n YOUR_NS pod/YOUR_POD -c istio-proxy --tail=200
# 3) Service/Endpoints
kubectl get svc,endpoints -n YOUR_NS
kubectl get endpoints YOUR_SERVICE -n YOUR_NS -o yaml
# 4) Istio 라우팅
kubectl get gw,vs -A
kubectl get vs YOUR_VS -n YOUR_NS -o yaml
kubectl logs -n istio-system deploy/istio-ingressgateway -c istio-proxy --tail=200
# 5) 정책/mTLS
kubectl get peerauthentication,authorizationpolicy,destinationrule -A
원하시면, 사용 중인 설치 조합(예: KServe RawDeployment 여부, Knative 사용 여부, Ingress가 istio-ingressgateway 인지, 네임스페이스 mTLS 모드)을 알려주시면 위 7단계를 당신 환경에 맞춘 “우선순위”로 재정렬해서 체크리스트를 더 공격적으로 최적화해 드릴 수 있습니다.