Published on

NVIDIA Triton 배포서 503? 헬스체크·워커 튜닝

Authors

서빙 중인 Triton Inference Server에서 갑자기 503(Service Unavailable)가 튀면, 많은 경우 “모델이 아직 준비되지 않았는데 트래픽이 들어오는 상황” 혹은 “요청을 처리할 워커/인스턴스가 부족하거나 막혀서 큐가 터지는 상황”입니다. 문제는 둘 다 겉으로는 동일하게 503으로 보일 수 있다는 점입니다.

이 글에서는 Kubernetes 배포를 기준으로, Triton의 헬스체크(ready/live) 설계와 워커(동시성) 튜닝을 통해 503을 줄이는 실전 체크리스트를 정리합니다. 특히 롤링 업데이트, 콜드 스타트, 모델 로드 지연, GPU 메모리 압박, 동적 배칭 설정이 얽힐 때 503이 가장 자주 발생합니다.

관련해서 파드가 재시작 루프에 빠지는 경우도 함께 점검해야 합니다. CrashLoopBackOff가 동반된다면 아래 글도 같이 보면 원인 분리가 빨라집니다.

Triton에서 503이 나오는 대표 시나리오

1) Readiness가 준비되기 전에 트래픽 유입

Triton은 내부적으로 모델을 로드하고, 인스턴스를 띄우고, 백엔드 초기화를 끝내야 “정상 서빙 가능” 상태가 됩니다. 그런데 Kubernetes의 readinessProbe가 부정확하거나, Service/Ingress가 readiness를 무시하면 준비 전 트래픽이 들어와 503이 발생합니다.

2) 모델이 UNAVAILABLE 상태

모델 리포지토리 변경, 모델 로드 실패(버전 폴더 구조/권한/파일 누락), config.pbtxt 오류, GPU 메모리 부족(OOM) 등으로 모델이 UNAVAILABLE이면 해당 모델 요청은 503으로 떨어질 수 있습니다.

3) 큐가 포화되거나 스케줄링이 막힘

Triton은 모델별로 인스턴스 수, 동적 배칭, 스케줄링을 통해 처리량을 만든 뒤 요청을 처리합니다. 인스턴스 수가 너무 적거나, 배칭이 비효율적이거나, CPU 스레드/네트워크가 병목이면 요청이 쌓이고 타임아웃/503으로 이어집니다.

4) 롤링 업데이트 중 일시적 공백

파드 종료(terminationGracePeriodSeconds)와 새 파드 준비 사이에 공백이 생기면, 로드밸런서가 살아있는 엔드포인트를 못 찾아 503을 반환할 수 있습니다.

503 원인 분리: 가장 먼저 볼 것 3가지

1) Triton 로그에서 모델 로드/실패 확인

파드 로그에서 failed to load 류 메시지, 백엔드 초기화 실패, OOM, 파일 접근 권한 등을 확인합니다.

kubectl logs -n inference deploy/triton --tail=200

모델 상태는 HTTP로도 확인할 수 있습니다.

curl -s http://TRITON_HOST:8000/v2/health/ready
curl -s http://TRITON_HOST:8000/v2/models
curl -s http://TRITON_HOST:8000/v2/models/MODEL_NAME

주의: 본문에 TRITON_HOST 같은 값은 환경에 맞게 바꿔야 합니다.

2) readiness와 liveness가 무엇을 보고 있는지 점검

많은 배포가 livenessProbe와 readinessProbe를 같은 엔드포인트로 두거나, readiness를 너무 공격적으로 설정합니다. 그 결과 모델 로딩 중에도 “준비됨”으로 판단되거나, 반대로 로딩이 오래 걸리면 liveness가 죽여서 영원히 준비되지 못하는 루프가 생깁니다.

3) Ingress/Nginx/ALB의 업스트림 타임아웃

Triton 자체는 처리 중인데 앞단 프록시가 타임아웃을 먼저 내고 503/504로 변환하는 경우가 있습니다. 특히 대형 배치나 긴 전처리/후처리가 있으면 프록시 타임아웃과 충돌합니다.

프록시 계층 문제(502/503/리다이렉트)는 Nginx 설정에서도 자주 발생합니다.

헬스체크 설계: ready/live를 분리하라

Triton은 기본적으로 다음 헬스 엔드포인트를 제공합니다.

  • readiness: http://...:8000/v2/health/ready
  • liveness: http://...:8000/v2/health/live

핵심은 다음과 같습니다.

  • live는 “프로세스가 살아있나”를 보기에 적합합니다.
  • ready는 “요청을 받아도 되는가”를 보기에 적합합니다.
  • 모델 로딩이 길면 readiness 초기 지연(initialDelaySeconds)과 실패 허용(failureThreshold)을 넉넉히 줘야 합니다.
  • liveness를 readiness처럼 엄격하게 두면, 로딩 중에 죽여서 503이 아니라 CrashLoopBackOff로 악화됩니다.

Kubernetes 프로브 예시

apiVersion: apps/v1
kind: Deployment
metadata:
  name: triton
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: triton
          image: nvcr.io/nvidia/tritonserver:24.01-py3
          args:
            - tritonserver
            - --model-repository=/models
            - --strict-readiness=true
          ports:
            - containerPort: 8000
            - containerPort: 8001
            - containerPort: 8002
          readinessProbe:
            httpGet:
              path: /v2/health/ready
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 12
          livenessProbe:
            httpGet:
              path: /v2/health/live
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 6

--strict-readiness=true를 권장하는 이유

이 옵션은 “정말로 모델과 서버가 준비된 상태”만 readiness로 통과시키는 쪽에 가깝습니다. 준비 전 트래픽 유입으로 인한 503을 줄이는 데 도움이 됩니다.

단, 모델 로딩이 매우 길면 readiness가 오래 실패하므로, 배포 파이프라인과 HPA, 롤링 업데이트 설정(특히 maxUnavailable: 0)을 함께 맞춰야 합니다.

503을 줄이는 워커(동시성) 튜닝 포인트

Triton에서 “워커”라는 표현은 상황에 따라 다르게 쓰이지만, 실무에서 503에 직접 영향을 주는 축은 보통 아래 3가지입니다.

  1. 모델 인스턴스 수(instance_group)
  2. 동적 배칭(dynamic_batching)
  3. 클라이언트/프록시 동시성 및 타임아웃

1) 모델 인스턴스 수: instance_group부터 확인

가장 흔한 병목은 “GPU는 남는데 모델 인스턴스가 1개라서 직렬 처리”하는 경우입니다. 이때 큐가 길어지고 프록시 타임아웃으로 503이 증가합니다.

config.pbtxt 예시:

name: "my_model"
platform: "onnxruntime_onnx"
max_batch_size: 16

instance_group [
  {
    kind: KIND_GPU
    count: 2
    gpus: [0]
  }
]
  • count를 올리면 동시 처리량이 늘 수 있습니다.
  • 단, 모델이 GPU 메모리를 많이 쓰면 인스턴스를 늘리는 순간 OOM으로 모델이 UNAVAILABLE이 될 수 있습니다. 그 경우 503이 더 심해집니다.

GPU 메모리 압박이 핵심이라면 아래 글의 OOM 완화 전략(메모리 최적화, 배치/해상도 조절, 체크포인트 관리 등)도 참고할 만합니다.

2) 동적 배칭: 처리량은 올리되 지연을 관리

동적 배칭은 여러 요청을 묶어 GPU 효율을 올리지만, 배칭을 기다리느라 지연이 늘 수도 있습니다. 지연이 늘면 앞단 타임아웃에 걸려 503으로 “보이는” 실패가 증가합니다.

config.pbtxt 예시:

dynamic_batching {
  preferred_batch_size: [ 4, 8, 16 ]
  max_queue_delay_microseconds: 2000
}
  • max_queue_delay_microseconds가 너무 크면 p95 지연이 튀고, 프록시 타임아웃과 충돌합니다.
  • 너무 작으면 배칭이 잘 안 되어 처리량이 떨어지고, 결과적으로 큐가 다시 길어질 수 있습니다.

실무 팁:

  • 먼저 “프록시 타임아웃”과 “클라이언트 타임아웃”을 확인한 뒤 그보다 충분히 작은 범위에서 배칭 지연을 설계합니다.
  • 배칭으로 처리량을 올리는 목적이라면, 인스턴스 수 증가와 함께 A/B로 비교하는 편이 안전합니다.

3) 모델 로딩 전략: 시작 시 전부 로드할지, 온디맨드로 갈지

모델이 많고 로딩이 길면, 배포 직후 readiness가 오래 실패하거나(트래픽 공백), 반대로 온디맨드 로딩 중 첫 요청이 503/타임아웃으로 실패할 수 있습니다.

선택지:

  • 시작 시 로드: 배포 안정성은 좋지만 콜드 스타트가 길어짐
  • 온디맨드 로드: 콜드 스타트는 짧아질 수 있으나 첫 요청 실패 가능

운영에서는 보통 “핵심 모델만 시작 시 로드 + 나머지는 온디맨드” 같은 절충을 씁니다. 이때 readiness 기준을 어떻게 둘지(서버 ready인지, 특정 모델 ready인지)도 함께 정의해야 합니다.

Kubernetes 배포에서 503을 줄이는 운영 설정

1) 롤링 업데이트에서 공백 제거

  • maxUnavailable: 0으로 동시에 내려가는 파드를 0으로 제한
  • readiness 통과 전까지 Service 엔드포인트에 포함되지 않게 보장

이미 위 예시 YAML에 반영했습니다.

2) preStop과 종료 유예로 드레인 보장

파드 종료 직전에 트래픽을 끊고, 진행 중 요청을 마무리할 시간을 줘야 합니다.

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 15"]
  • 단순 sleep도 효과가 있지만, 더 나은 방식은 앞단 로드밸런서의 드레인과 함께 설계하는 것입니다.

3) 리소스 제한으로 “조용한 병목” 제거

CPU limit이 너무 낮으면 전처리/후처리, gRPC/HTTP 처리, 모델 로딩이 느려져 readiness가 지연되고 503이 늘 수 있습니다.

resources:
  requests:
    cpu: "2"
    memory: "8Gi"
  limits:
    cpu: "4"
    memory: "16Gi"

GPU는 별도로 nvidia.com/gpu를 설정합니다.

디버깅 체크리스트: 503이 보이면 이렇게 좁혀라

A. “준비 전 트래픽”인지 확인

  • curlv2/health/ready가 실패하는데 Ingress는 이미 라우팅하고 있는가
  • readinessProbe가 너무 느슨하거나, --strict-readiness가 꺼져 있는가

B. “모델 UNAVAILABLE”인지 확인

  • v2/models/MODEL_NAME에서 상태 확인
  • 로그에서 OOM, 파일 누락, 권한, config.pbtxt 파싱 오류 확인

C. “처리량 부족으로 큐 포화”인지 확인

  • 평균 지연이 아니라 p95/p99가 급격히 증가하는지
  • 인스턴스 수(count)가 1로 고정되어 있지 않은지
  • 동적 배칭 지연이 프록시 타임아웃보다 큰지

D. “앞단 프록시가 503으로 바꿔치기”인지 확인

  • Ingress/Nginx/ALB 로그에서 업스트림 타임아웃 여부 확인
  • 클라이언트 타임아웃과 재시도 정책 점검(과한 재시도는 서버를 더 죽임)

마무리: 503은 증상이고, 해법은 준비·동시성·타임아웃 정렬

Triton 배포에서 503을 잡는 가장 높은 확률의 접근은 다음 순서입니다.

  1. readiness와 liveness를 분리하고, --strict-readiness=true로 “준비 전 트래픽”을 차단
  2. 롤링 업데이트에서 공백이 생기지 않게 maxUnavailable: 0와 충분한 종료 유예를 설정
  3. 모델별 instance_groupdynamic_batching을 조합해 처리량을 만들고, 프록시/클라이언트 타임아웃과 정렬

이 3가지만 제대로 맞춰도 “이유 없이 가끔 503” 같은 운영 이슈는 대부분 사라집니다. 이후에는 모델별 특성(메모리 사용량, 배칭 효율, 전처리 비용)에 맞춰 인스턴스 수와 배칭 지연을 미세 조정하면서 안정적인 p95 지연과 에러율을 목표로 튜닝하면 됩니다.