이 글에서 다루는 것

Triton Inference Server를 CPU-only GitOps 구조로 배포하고, ONNX 모델 1개의 load/infer 검증 및 Prometheus/Grafana 관측까지 서빙 플랫폼 뼈대를 구축한 과정

선수지식


이 단계에서 해결하려는 문제

실무에서 서빙 계층은 곧바로 트래픽과 SLA를 맞는 최전선이다. 모델이 아무리 좋아도 서빙이 불안정하면 운영 시 바로 무너진다. 이번에는 Triton 첫 구축으로 GPU/파이프라인 연동을 일부러 빼고, Triton 자체를 GitOps로 안정적으로 띄우고, 모델 load → infer → metrics 관측까지 서빙 플랫폼 뼈대 구축을 진행했다.


🎯 핵심 요약

  • dev/prod 네임스페이스 분리: triton-dev, triton-prod
  • Triton 모델 제어 모드: --model-control-mode=explicit (필요 시에만 load/unload)
  • Model Repository: NFS(RWX) 기반 PVC로 /models 마운트
  • 모델 검증: ONNX 1개 simple_modelload → infer 성공
  • 관측: Triton /metrics를 Prometheus 포맷으로 자체 노출, ServiceMonitor로 자동 스크랩, Grafana 패널로 증명
  • GPU 확장 포인트: values에 gpu.enabled 분기 설계 (현재 false → CPU 사용)

1️⃣ 전체 구조 요약

mermaid-triton-00.png


2️⃣ 코드 트리

charts/triton/
├── Chart.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── serviceMonitor.yaml
└── values/
    ├── base.yaml
    ├── dev.yaml
    └── prod.yaml

3️⃣ 설계 포인트

(1) 서빙 계층 분리

  • FastAPI는 API Gateway/비즈니스 라우팅 계층
  • Triton은 Inference 전용 서빙 계층
  • 운영/스케일링/리소스 성격이 다르기 때문에 분리했다.

(2) explicit 모드 선택

  • 자동 로딩이 아니라 필요할 때만 모델 load
  • 운영에서 모델 배포/교체는 항상 위험 구간이라 통제 가능한 방식(Explicit)으로 진행했다.

(3) GitOps에서 dev/prod 관측 라벨 정합성

  • Prometheus는 연결이 아니라 라벨로 발견한다 (= exporter 연결이 아닌 label 탐색)
  • dev Prometheus는 release=monitoring-dev 라벨의 ServiceMonitor만 스크랩
  • prod는 release=monitoring-prod만 스크랩
  • 서비스/서비스모니터에 release 라벨이 핵심이었다.

4️⃣ Helm values 핵심

values/base.yaml

  • 이미지: nvcr.io/nvidia/tritonserver:24.08-py3
  • 포트: HTTP 8000 / gRPC 8001 / Metrics 8002
  • 리소스: CPU-only
  • NFS PVC를 /models로 마운트
  • GPU 확장 포인트는 flag만 두고 이번에는 false로 CPU 사용

values/dev.yaml / values/prod.yaml

  • global.envdev/prod로 분리
  • monitoring.releaseLabel을 각 환경별로 지정 (dev → monitoring-dev, prod → monitoring-prod)

5️⃣ Kubernetes 리소스 핵심 (Service / ServiceMonitor)

5-1) Service.yaml 핵심

  • Service에 app, env, release 라벨을 부여
  • ServiceMonitor가 이 라벨을 기준으로 대상 발견
  • Service가 metrics 포트를 노출해야 ServiceMonitor가 붙는다.

5-2) ServiceMonitor.yaml 핵심

  • Prometheus의 serviceMonitorSelector.matchLabels.release와 맞춰야 함
  • namespaceSelector.matchNames: [ Release.Namespace ]로 dev/prometheus가 dev namespace 안의 서비스만 긁게 고정

6️⃣ Model Repository (NFS, RWX)

NFS 최종 구조

/mnt/nfs_share/mlops/triton/model-repo/
├── dev/
│   └── simple_model/
│       ├── config.pbtxt
│       └── 1/
│           └── model.onnx
└── prod/
    └── simple_model/
        ├── config.pbtxt
        └── 1/
            └── model.onnx
  • PV/PVC는 dev/prod 각각 다른 path로 분리
  • PVC는 namespace 안에서 동일 이름(triton-model-repo-pvc)을 사용해도 되게 설계 (namespace가 격리)

7️⃣ 모델 준비 (ONNX)

STEP 1) ONNX export (k8s-master)

  • 처음엔 onnxscript가 없어 export 실패 → 설치 후 해결
  • opset downgrade 경고/실패가 있었지만 결과물 생성은 됨
  • 최종적으로 opset 18로 고정하는 편이 안전했음

STEP 2) 파일 전달

  • 랩 편의상: k8s-master → Windows(MobaXterm) → NFS 서버 업로드
  • 대안: NFS mount / initContainer로 자동 동기화 가능

STEP 3) config.pbtxt에서 shape mismatch 이슈

  • 실제 ONNX input shape: [1,4]
  • config.pbtxt에 [4]로 적으면 load 실패 → Triton이 아주 엄격하게 검사

8️⃣ 모델 로드/상태/추론 검증

8-1) Load + Status + Ready 확인

  • POST /v2/repository/models/simple_model/load
  • GET /v2/models/simple_model
  • GET /v2/health/ready

성공 시 status에서 inputs/outputs shape 확인 가능, ready 200 OK

8-2) Infer 요청

  • POST /v2/models/simple_model/infer
  • JSON 입력 shape [1,4]로 요청 → 정상 응답

이 시점에서 서빙 자체는 완성이다.


9️⃣ /metrics 확인 (핵심 증명)

처음 /metrics에는 CPU 관련 최소 지표만 보일 수 있다.

infer를 1~2번이라도 수행하면 아래가 등장한다:

  • nv_inference_request_success
  • nv_inference_request_duration_us
  • nv_inference_queue_duration_us
  • nv_inference_count

실제로 infer를 쐈고, Triton이 그 사실을 메트릭으로 남긴다.


🔟 Grafana 대시보드 (JSON Import)

Grafana에서 Dashboard → Import → JSON 붙여넣기

  • 변수: datasource / namespace / model
  • 핵심 5패널: RPS / Avg Latency / Avg Queue / CPU / Total Count

🧩 팁

  • 초기엔 inference 메트릭이 안 보이는 게 정상이다 (infer를 한 번이라도 쏴야 생김).
  • ServiceMonitorSelector는 결국 release 라벨 매칭이다. Prometheus 쪽 selector와 ServiceMonitor labels가 1:1로 맞아야 한다.
  • Triton은 config.pbtxt의 shape/dims를 아주 엄격하게 본다. ONNX input shape와 config dims 불일치는 가장 흔한 함정이다.

설계 판단 (Why This Way?)

explicit 모드를 선택하여 불완전한 파일 업로드 중 자동 로딩 실패를 방지하고 배포 타이밍을 DAG가 완전히 통제하도록 했습니다. NFS(RWX)는 예측 가능한 로딩 시간과 다중 Pod 동시 읽기를 보장하며, CPU-only로 시작하여 GPU 호환성 문제와 서빙 플랫폼 구축을 분리해 디버깅 범위를 좁혔습니다.


다음에 읽을 글

Triton 서빙 플랫폼 - MLflow → Triton 자동 배포 파이프라인 — 모델 레지스트리 연동 자동화