- Published on
KServe vLLM 배포 후 502? InferenceService 8단계
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서빙은 떴는데 호출만 하면 502 Bad Gateway가 떨어지는 상황은 KServe에서 꽤 흔합니다. 특히 vLLM은 모델 로딩 시간이 길고(다운로드, 샤딩, KV 캐시 초기화), GPU/메모리 요구량이 크며, OpenAI 호환 API 경로가 KServe 프록시 경로와 섞이기 쉬워서 502가 “원인 불명”처럼 보이기 쉽습니다.
이 글은 KServe의 InferenceService로 vLLM을 배포한 뒤 502가 발생했을 때, 원인을 8단계로 쪼개서 빠르게 좁히는 실무 체크리스트입니다. 단계별로 “무엇을 확인”하고 “어떤 명령/로그를 보며” “어떻게 고치는지”까지 정리합니다.
참고로 readiness/health 문제로 겉보기엔 정상 로그인데 트래픽만 실패하는 패턴은 아래 글에서도 다룬 적이 있습니다.
1단계: 502가 어디서 나는지(게이트웨이 vs 파드) 먼저 분리
502는 대개 “프록시가 업스트림에 연결 못 함”입니다. KServe 경로에서는 보통 다음 중 하나가 프록시 역할을 합니다.
- Istio IngressGateway(또는 Kourier/Knative)
- Knative activator(스케일-투-제로/콜드스타트 구간)
- KServe 내부 networking 레이어
먼저 요청이 어디까지 들어가는지 확인합니다.
# InferenceService 상태
kubectl get inferenceservice -A
kubectl describe inferenceservice -n <namespace> <name>
# KServe가 만든 Knative Service(사용 중이라면)
kubectl get ksvc -n <namespace>
# IngressGateway 로그(istio 사용 시)
kubectl -n istio-system logs deploy/istio-ingressgateway -c istio-proxy --tail=200
판별 기준:
- IngressGateway 로그에
upstream connect error또는no healthy upstream가 보이면 “파드 readiness/엔드포인트 문제” 가능성이 큽니다. - Ingress까지는 정상인데 응답이 늦다가
502라면 “타임아웃(라우팅/프로브/콜드스타트)” 가능성이 큽니다.
2단계: InferenceService Ready가 진짜 True인지, 조건을 읽기
KServe 리소스는 “겉으로 Ready처럼 보이는데 세부 조건이 깨져있는” 경우가 있습니다.
kubectl get isvc -n <namespace> <name> -o yaml
아래를 집중해서 봅니다.
status.conditions[]에서Ready,PredictorReady,RoutesReady상태status.url이 비어 있지 않은지- 이벤트에
RevisionMissing,ConfigurationFailed,ProgressDeadlineExceeded같은 메시지가 있는지
특히 Knative 기반이면 RoutesReady=False가 흔한데, 이 경우는 파드가 살아도 라우팅이 붙지 않아 502가 나기 쉽습니다.
3단계: 엔드포인트가 실제로 생겼는지(Service/EndpointSlice)
프록시가 업스트림으로 붙으려면 K8s Service 뒤에 엔드포인트가 있어야 합니다.
# 서비스 확인
kubectl get svc -n <namespace>
# 엔드포인트/슬라이스 확인
kubectl get endpoints -n <namespace>
kubectl get endpointslice -n <namespace>
체크 포인트:
- 엔드포인트가 비어 있으면 readiness가 실패했거나, selector가 맞지 않거나, 파드가 준비 상태가 아닙니다.
- 엔드포인트는 있는데도 502면, 포트/프로토콜 불일치(예: 서비스는
80인데 컨테이너는8000만 리슨) 가능성이 큽니다.
4단계: vLLM 컨테이너가 “리스닝”을 시작했는지 로그로 확인
vLLM은 모델 로딩이 끝나기 전까지 HTTP 서버가 뜨지 않거나, 뜨더라도 내부적으로 준비되지 않은 상태일 수 있습니다. 이때 KServe/Knative가 트래픽을 붙이면 502가 발생합니다.
kubectl logs -n <namespace> deploy/<predictor-deploy-name> -c <container> --tail=200
자주 보는 원인:
- 모델 다운로드 실패(HF 토큰, 네트워크, 프라이빗 리포)
- GPU 메모리 부족(OOM)으로 프로세스 재시작
--max-model-len과 KV 캐시 설정으로 초기화 실패--dtype/텐서 병렬 설정 불일치
특히 이미지 풀/레지스트리 문제면 파드가 뜨지도 못합니다. 이 경우는 ImagePullBackOff부터 해결해야 합니다.
5단계: 프로브(readiness/liveness/startup)가 vLLM 특성과 맞는지
502의 가장 흔한 실무 원인은 “모델 로딩이 긴데 readiness가 너무 공격적”인 설정입니다.
- readiness가 실패하면 엔드포인트에서 제외되어 프록시가
no healthy upstream으로502 - liveness가 실패하면 컨테이너가 재시작 루프에 빠져 영원히 준비되지 않음
권장 패턴은 startupProbe를 적극적으로 쓰는 것입니다.
# Deployment 템플릿에 들어갈 수 있는 예시(개념 예시)
startupProbe:
httpGet:
path: /health
port: 8000
failureThreshold: 120
periodSeconds: 5
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
주의:
- vLLM OpenAI 서버는 보통
GET /health를 제공합니다(이미지/버전에 따라 다를 수 있으니 실제 컨테이너 문서/로그 기준으로 확인). - KServe/Knative 조합에서는 프로브 경로가 별도 리라이트되는 경우도 있어, “파드 내부에서 직접
curl로 되는지”를 먼저 확인하는 게 안전합니다.
kubectl exec -n <namespace> -it <pod> -- sh -lc 'curl -sS localhost:8000/health'
프로브가 정상인데도 readiness가 실패한다면, 아래 글의 “로그는 정상인데 readiness만 실패” 유형을 같이 참고하면 좋습니다.
6단계: 라우팅 경로가 섞였는지(특히 OpenAI 호환 API)
KServe는 외부 URL에 라우팅 프리픽스가 붙을 수 있습니다. 반면 vLLM OpenAI 호환 서버는 보통 다음 경로를 기대합니다.
POST /v1/completionsPOST /v1/chat/completionsGET /v1/models
여기서 자주 생기는 문제:
- 사용자는
.../v1/chat/completions로 쐈는데, 실제로는.../v1/models만 열려 있거나 - Ingress에서
/로 들어오는 요청이 KServe 내부에서 특정 프리픽스로 리라이트되어 vLLM이 404를 내고, 그게 상위에서502로 보이는 경우
진단 방법은 “동일 클러스터 내부에서 서비스로 직접 호출”과 “외부 인그레스 호출”을 비교하는 것입니다.
# 클러스터 내부에서 서비스로 직접 호출(예시)
kubectl run -n <namespace> tmp-curl --rm -it --image=curlimages/curl -- sh
# 파드 안에서
curl -sS http://<service-name>.<namespace>.svc.cluster.local:8000/v1/models
내부 호출이 되는데 외부만 502면 라우팅/VirtualService/Ingress 설정 문제일 확률이 큽니다.
7단계: 타임아웃(콜드스타트, 대형 모델 로딩, 스트리밍) 조정
대형 LLM은 첫 요청이 특히 느립니다.
- Knative activator가 개입하는 구간에서 기본 타임아웃에 걸림
- Istio 라우트 타임아웃이 짧음
- 클라이언트(예: ALB, API Gateway, NGINX)가 먼저 끊음
증상:
- 일정 시간(예: 30초, 60초) 후 규칙적으로
502또는504 - vLLM 로그에는 요청이 들어왔는데 응답을 쓰기 전에 커넥션이 끊김
대응:
- Knative 사용 시 revision timeout을 늘리거나, scale-to-zero를 끄고 최소 레플리카를
1로 유지 - Istio VirtualService/DestinationRule의 타임아웃을 워크로드 특성에 맞게 증가
- 스트리밍 응답을 쓰는 경우, 중간 프록시가
chunked를 제대로 통과시키는지 확인
이 단계는 “로그상 요청 처리 시간”과 “프록시 타임아웃”을 함께 봐야 합니다.
8단계: 리소스/GPU 스케줄링과 OOM 재시작 루프 확인
vLLM은 GPU가 없거나, GPU는 있는데 메모리가 부족하거나, 노드에 스케줄이 안 되면 준비 상태가 되지 않습니다. 이 경우 프록시는 계속 502를 냅니다.
kubectl get pod -n <namespace> -o wide
kubectl describe pod -n <namespace> <pod>
# 재시작/종료 코드 확인
kubectl get pod -n <namespace> <pod> -o jsonpath='{.status.containerStatuses[*].restartCount}'
kubectl logs -n <namespace> <pod> --previous --tail=200
자주 나오는 패턴:
OOMKilled로 계속 재시작nvidia.com/gpu요청은 했는데 노드에 여유가 없어Pending- GPU는 잡았지만
CUDA out of memory로 초기 로딩 실패
실무 팁:
모델 크기에 맞춰
--gpu-memory-utilization을 보수적으로 시작--max-model-len을 줄여 KV 캐시 상한을 낮춤필요하면 양자화(4bit 등)로 VRAM 요구량을 줄이되, 실패 케이스가 많으니 체크리스트를 갖고 접근
재현 가능한 최소 예시: vLLM을 KServe InferenceService로 띄우는 형태
클러스터/버전마다 세부 필드는 달라질 수 있지만, “어디를 건드려야 하는지” 감을 잡기 위한 골격 예시입니다. 부등호 문자는 MDX 빌드 이슈가 있어 인라인 코드로 표기합니다.
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: vllm-llama
namespace: default
spec:
predictor:
containers:
- name: vllm
image: vllm/vllm-openai:latest
args:
- --model
- meta-llama/Llama-2-7b-hf
- --host
- 0.0.0.0
- --port
- "8000"
ports:
- containerPort: 8000
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token
key: token
resources:
limits:
nvidia.com/gpu: "1"
memory: "24Gi"
cpu: "4"
requests:
nvidia.com/gpu: "1"
memory: "16Gi"
cpu: "2"
이후 내부 점검은 다음 순서로 진행하면, 502의 원인을 빠르게 좁힐 수 있습니다.
kubectl get isvc -n default vllm-llama
kubectl describe isvc -n default vllm-llama
kubectl get pod -n default -l serving.kserve.io/inferenceservice=vllm-llama
kubectl logs -n default <pod> -c vllm --tail=200
kubectl exec -n default -it <pod> -- sh -lc 'curl -sS localhost:8000/health'
결론: 502는 “네트워크 에러”가 아니라 “준비 안 된 업스트림”인 경우가 대부분
KServe에서 vLLM 배포 후 502가 나면, 무작정 Ingress부터 의심하기보다 아래 8단계를 위에서 아래로 밟는 게 가장 빠릅니다.
502가 발생하는 레이어를 분리(게이트웨이 로그)InferenceService조건(Ready/RoutesReady)을 YAML로 확인- Service 뒤 엔드포인트 존재 여부 확인
- vLLM이 실제로 리스닝/모델 로딩 완료했는지 로그 확인
- 프로브를 vLLM 로딩 시간에 맞게 조정(
startupProbe중심) - OpenAI 호환 API 경로와 KServe 라우팅 프리픽스 충돌 점검
- 콜드스타트/프록시 타임아웃 조정
- GPU 스케줄링, OOM, 재시작 루프 확인
이 순서를 지키면 “왜 502인지”가 로그와 오브젝트 상태로 설명 가능해지고, 재발 방지도 쉬워집니다.