이 글에서 다루는 것

CPU-only 환경에서 Triton의 dynamic_batching과 instance_group 설정으로 처리량을 높이고 latency를 안정화하는 성능 최적화 과정

선수지식


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

Triton을 올리고 모델이 READY 상태가 되면 끝일까? 기본 설정만 사용하면 요청이 하나씩 순서대로 처리됩니다. 동시 요청이 몰리면 큐가 쌓이고, latency가 급격히 올라갑니다.

이 글은 dynamic_batchinginstance_group 설정으로 CPU 환경에서 처리량을 높이고 latency를 안정화한 과정을 설명합니다.


🎯 핵심 요약

  • 기본 설정의 한계: single instance + no batching -> 요청 직렬 처리
  • instance_group: CPU 인스턴스 수 증가 -> 병렬 추론 처리
  • dynamic_batching: 요청 자동 묶음 -> 배치 단위 추론으로 처리량 향상
  • 적용 시점: explicit mode에서는 model load 시 config.pbtxt 반영
  • 실측 결과: 136 RPS, p95 553ms, 에러율 0% (100 VU, CPU-only 3노드)

1️⃣ 기본 설정만으로는 무엇이 부족한가

Triton을 처음 실행하면 기본 동작은 다음과 같습니다.

요청 A → 추론 처리 → 응답
요청 B → 대기 → 추론 처리 → 응답
요청 C → 대기 → 대기 → 추론 처리 → 응답

요청이 하나씩 직렬로 처리됩니다.

config.pbtxt에 아무 설정이 없으면 다음 상태입니다.

instance_group: 1개 (기본값)
batching: 없음 (기본값)

이 상태에서 동시 요청이 100개 들어오면 1개 처리 중, 99개 대기입니다.

결과: 처리량 낮음, latency 증가, 큐 대기 시간 급증.

이 문제를 해결하기 위해 두 가지 설정을 추가합니다.

instance_group: 병렬 처리 인스턴스 수 증가
dynamic_batching: 요청 자동 묶음 처리

2️⃣ instance_group 설정

instance_group은 Triton이 모델을 몇 개의 인스턴스로 동시에 실행할지를 정의합니다.

KIND_CPU vs KIND_GPU

설정의미
KIND_CPUCPU 기반 추론 인스턴스
KIND_GPUGPU 기반 추론 인스턴스 (GPU ID 지정 가능)
KIND_AUTO환경에 따라 자동 선택

CPU-only 환경에서는 KIND_CPU를 사용합니다.

instance_group [
  {
    kind: KIND_CPU
    count: 2
  }
]

count와 병렬 처리

count: 2는 모델 인스턴스 2개가 동시에 추론 가능하다는 의미입니다.

즉 동시 요청이 들어왔을 때:

요청 A → 인스턴스 1 처리 (병렬)
요청 B → 인스턴스 2 처리 (병렬)
요청 C → 대기

단일 인스턴스 대비 2배의 병렬 처리가 가능합니다.

count 설정 기준

CPU 코어 수와 메모리를 고려해서 설정합니다. 너무 높으면 메모리 부족과 스케줄링 경쟁이 발생하고, 너무 낮으면 병렬 처리 이점이 없습니다. 3노드 CPU-only 클러스터 기준으로 count: 2는 합리적인 시작값입니다.


3️⃣ dynamic_batching 설정

dynamic_batching은 Triton이 들어오는 요청을 자동으로 묶어서 배치 단위로 추론하는 기능입니다.

기본 동작 원리

요청 A 도착
요청 B 도착 (거의 동시)
요청 C 도착 (거의 동시)
         ↓
Triton이 A+B+C를 하나의 배치로 묶어서 추론
         ↓
배치 결과를 각 요청에 분리해서 응답

1건씩 추론 3회보다 3건 배치 추론 1회가 처리 오버헤드가 적어 처리량이 향상됩니다.

preferred_batch_size

dynamic_batching {
  preferred_batch_size: [ 8, 16 ]
}

preferred_batch_size: [8, 16]의 의미: 8건이 모이면 우선 처리하고, 8건이 안 되면 최대 16건까지 기다렸다가 처리합니다.

이 설정은 처리량 향상과 latency 증가 사이의 균형점입니다.

max_queue_delay_microseconds

배치가 채워지지 않을 때 최대 대기 시간을 설정할 수 있습니다.

dynamic_batching {
  preferred_batch_size: [ 8, 16 ]
  max_queue_delay_microseconds: 5000  # 5ms
}

5000 microseconds = 5ms 안에 배치가 채워지지 않으면 기다리지 않고 현재 쌓인 요청만으로 추론합니다. 이 값을 높이면 처리량이 오르지만 latency도 올라갑니다.


4️⃣ latency vs throughput 트레이드오프

dynamic_batching은 처리량과 latency 사이의 트레이드오프가 존재합니다.

상황권장 설정
실시간 응답 중요 (챗봇 등)작은 batch_size, 짧은 queue_delay
처리량 중요 (배치 API 등)큰 batch_size, 긴 queue_delay
균형 (ML serving 일반)preferred_batch_size: [8, 16]

이 프로젝트에서는 ML 서빙 일반 케이스로 [8, 16]을 선택했습니다.


5️⃣ explicit mode에서 배치 설정 적용 시점

이 프로젝트에서는 Triton을 explicit 모드로 실행합니다.

--model-control-mode=explicit

이 모드에서 모델은 자동으로 로드되지 않습니다. DAG에서 명시적으로 load API를 호출해야 합니다.

config.pbtxt 반영 시점:

load API 호출
      ↓
Triton이 model repository에서 config.pbtxt 읽음
      ↓
dynamic_batching + instance_group 설정 적용
      ↓
READY 상태

config.pbtxt를 수정한 뒤에는 반드시 unload -> load 순서로 재적용해야 합니다.

POST /v2/repository/models/best_model/unload
POST /v2/repository/models/best_model/load

6️⃣ 실제 config.pbtxt 코드

이 프로젝트의 Triton 모델 설정은 dags/mlops_lib/core/triton_config.pybuild_config_pbtxt() 함수가 생성합니다.

name: "best_model"
platform: "onnxruntime_onnx"
max_batch_size: 32

input [
  {
    name: "input"
    data_type: TYPE_FP32
    dims: [ -1, 4 ]
  }
]

output [
  {
    name: "output_probability"
    data_type: TYPE_FP32
    dims: [ -1, 3 ]
  },
  # ... (output_label 생략)
]

dynamic_batching {
  preferred_batch_size: [ 8, 16 ]
  max_queue_delay_microseconds: 5000
}

instance_group [
  {
    kind: KIND_CPU
    count: 2
  }
]

주요 설계 포인트:

  • platform: onnxruntime_onnx – ONNX Runtime 기반 추론
  • dims: [ -1, 4 ] – 배치 차원(-1) + feature 차원(4)
  • max_batch_size: 32 – dynamic_batching 활성화를 위해 반드시 0이 아닌 값 필요

dynamic_batchinginstance_group은 Airflow Variable로 런타임 제어가 가능합니다.

triton_dynamic_batching_enabled: true/false
triton_instance_group_enabled:   true/false

Variable이 미설정이면 두 블록 모두 config.pbtxt에서 생략됩니다.


7️⃣ 부하 테스트 결과와 연결

이 설정을 적용한 후 k6 부하 테스트를 수행했습니다.

테스트 환경: 3노드 Kubernetes (CPU-only), FastAPI 2 replica, Triton 1 replica, 100 VU

결과:

처리량:      136 RPS
p50 latency: 126ms
p95 latency: 553ms
에러율:      0%

instance_group count: 2의 효과: 인스턴스 1개 예상 ~70 RPS에서 2개 실측 136 RPS로, 병렬 처리 효과가 처리량에 직접 반영되었습니다.


설계 판단 (Why This Way?)

preferred_batch_size [8, 16]으로 배치 오버헤드 감소와 대기 latency 증가 사이의 균형을 맞추고, instance_group count 2로 CPU 경합 없는 안정적 병렬 추론 시작값을 설정했습니다. Airflow Variable로 배치/인스턴스 설정을 런타임 제어하여 config.pbtxt 재생성 없이 A/B 성능 비교와 장애 대응 유연성을 확보했습니다.


다음에 읽을 글

Feature Store & Feast - Feature Store-lite — 경량 Feature Store 설계