1. クイックスタート:5分で始めるTriton Serverの起動とモデルテスト
GPU搭載のLinuxマシン上でテスト用にTriton Inference Serverインスタンスを迅速に立ち上げたい場合、以下のシンプルな3ステップでリクエストを受け付ける環境を即座に構築できます。
ステップ 1.1:モデルリポジトリディレクトリの準備
Tritonは非常に厳格なディレクトリ規約に基づいてモデルを管理します。ここでは、テキスト分類用のONNXモデルを想定したディレクトリ構造を素早く作成します:
# モデルリポジトリ用のディレクトリを作成
mkdir -p ~/triton_models/sample_model/1
cd ~/triton_models/sample_model
# 基本的なconfig.pbtxtファイルを作成
cat << 'EOF' > config.pbtxt
name: "sample_model"
platform: "onnxruntime_onnx"
max_batch_size: 8
input [
{
name: "input_ids"
data_type: TYPE_INT64
dims: [ 128 ]
}
]
output [
{
name: "output_logits"
data_type: TYPE_FP32
dims: [ 2 ]
}
]
EOF
次に、実際のONNXファイルを所定のパス(~/triton_models/sample_model/1/model.onnx)に配置します。ディレクトリ名の1はモデルのバージョンを表しています。
ステップ 1.2:Triton Serverコンテナの起動
ホストサーバーにNVIDIA Container Toolkitがインストールされている必要があります。NVIDIA NGCから公式イメージをプルしてコンテナを起動します:
docker run --gpus all --rm -d \
-p 8000:8000 -p 8001:8001 -p 8002:8002 \
-v ~/triton_models:/models \
--name triton-server \
nvcr.io/nvidia/tritonserver:24.08-py3 \
tritonserver --model-repository=/models
提供される主な3つのサービスポートは以下の通りです:
- 8000:汎用的なHTTP REST APIポート。
- 8001:バイナリ形式のgRPC APIポート。極めて低レイテンシであり、本番環境に最適です。
- 8002:リソースやキューの監視に利用するPrometheus Metricsポート。
ステップ 1.3:稼働状態の確認
以下の2つのcurlコマンドを実行し、サーバーおよびモデルがリクエスト受付可能な状態であるかを確認します:
# サーバーの生存確認(Livenessチェック)
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/v2/health/live
# モデルの準備完了確認(Readinessチェック)
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/v2/models/sample_model/ready
両コマンドとも200が返ってくれば、モデルがVRAM上に正常にロードされ、サーバーが推論処理を受け付ける準備が整ったことを意味します。
2. Tritonのアーキテクチャとコアメカニズム
多くの開発チームは、AIモデルを本番環境へ移行する初期段階において、FastAPIやFlaskを用いてモデルごとに独立したWebサービスをラッピングしがちです。音声認識のWhisper、物体検出のYOLO、テキスト分類のBERTなどを個別のサービスとして運用するケースがその典型です。
しかし、このアプローチはすぐに限界を迎えます。各コンテナがアイドル時でも2〜4GBのVRAMを占有し続け、PythonのGIL(グローバルインタプリタロック)がスループットの大きなボトルネックとなります。さらに、非同期に到着するリクエストを効率的にバッチ化することも極めて困難です。
Tritonは、高性能なC++コアによりこれらの課題を根本から解決します。集中化されたメモリ管理、自動バッチ化、そして的確なGPUリソースのスケジューリングを実現します。
標準的なモデルリポジトリ構造
Tritonはフォルダ名に基づいて適切なランタイムとバージョンをロードします:
model_repository/
├── yolo_detector/
│ ├── config.pbtxt
│ ├── 1/
│ │ └── model.onnx
│ └── 2/
│ └── model.onnx
└── text_classifier/
├── config.pbtxt
└── 1/
└── model.savedmodel/ (TensorFlow)
設定ファイル config.pbtxt の解読
config.pbtxtファイルは、データ型やテンソル形状(Shape)から推論スケジューリングアルゴリズムに至るまで、モデルのすべての動作を定義します。
Dynamic Batching:スループット向上の要
Tritonのスケジューラは、個別の単一リクエスト(batch = 1)を都度処理するのではなく、近いタイミングで到着したリクエストをGPUコアへ送る前に自動でより大きなバッチへとまとめます:
dynamic_batching {
max_queue_delay_microseconds: 5000
preferred_batch_size: [ 4, 8, 16 ]
}
max_batch_size: 16
この設定では、Tritonに対して最大5ミリ秒(5000マイクロ秒)待機するよう指示しています。4件、8件、または16件のリクエストが集まった時点で直ちにGPUへ投入されます。仮に5ミリ秒経過時点で3件しか集まっていなくても、レイテンシを維持するためにそのまま実行されます。実際の運用環境において、ResNet50やBERT-baseなどのモデルでは、この機構により単一処理時と比較してスループットが3〜5倍向上します。
3. 発展編:複数モデル運用、マルチインスタンス、モデルパイプライン(Ensemble)
同一GPU上での複数モデルインスタンスの並列実行
デフォルト設定では、Tritonは各モデルに対して1つの計算インスタンスのみを割り当てます。A10G(24GB)やA100(80GB)のような大容量GPUを使用している場合、VRAMに十分な空きがあるにもかかわらず、バッチ処理の合間にGPUコアがアイドル状態になることがあります。その場合、設定ファイルでワーカーを複製して並列度を高めることが可能です:
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [ 0 ]
}
]
これにより、TritonはGPU 0上にモデルの独立したインスタンスを2つ生成します。キューから取り出された2つのバッチが同時に並行処理されるため、後続のリクエストが先行リクエストの完了を待たされるブロッキングを排除できます。
モデルアンサンブル:前処理と推論のパイプライン化
実際のワークロードでは、生データがそのままモデルコアに入力されることは稀です。一般的なパイプラインは、画像の前処理やテキストのトークン化、ディープラーニングモデルへの入力、そして業務ロジックに応じた出力フォーマットのパースという一連の処理で構成されます。Triton Ensembleは、これらの全ステップを単一のパイプラインとして統合します:
name: "pipeline_nlp"
platform: "ensemble"
max_batch_size: 8
input [
{
name: "RAW_TEXT"
data_type: TYPE_STRING
dims: [ 1 ]
}
]
output [
{
name: "LABEL_SCORES"
data_type: TYPE_FP32
dims: [ 5 ]
}
]
ensemble_scheduling {
step [
{
model_name: "tokenizer_python_backend"
model_version: 1
input_map {
key: "TEXT"
value: "RAW_TEXT"
}
output_map {
key: "TOKEN_IDS"
value: "INTERMEDIATE_TOKENS"
}
},
{
model_name: "transformer_onnx"
model_version: 1
input_map {
key: "input_ids"
value: "INTERMEDIATE_TOKENS"
}
output_map {
key: "output_logits"
value: "LABEL_SCORES"
}
}
]
}
クライアントは単一のエンドポイントpipeline_nlpを呼び出すだけで完結します。トークナイザーとONNXモデル間の受け渡しデータはサーバーのRAMまたは共有メモリ(Shared Memory)内で直接処理されるため、マイクロサービス間で発生していたJSONのシリアライズやネットワーク通信のラウンドトリップによる数十ミリ秒のオーバーヘッドを完全に削減できます。
PythonクライアントからgRPC経由でTritonを呼び出す
tritonclientライブラリとgRPCを使用することで、Base64エンコードやJSONパースのオーバーヘッドを完全に回避し、バイナリデータを高速に転送できます:
import numpy as np
import tritonclient.grpc as grpcclient
# TritonへのgRPCクライアントを初期化
client = grpcclient.InferenceServerClient(url="localhost:8001")
# ダミーのinput_idsを準備: batch_size=1, sequence_length=128
input_data = np.ones((1, 128), dtype=np.int64)
# 入力テンソルのパッケージング
inputs = [grpcclient.InferInput("input_ids", input_data.shape, "TYPE_INT64")]
inputs[0].set_data_from_numpy(input_data)
# 取得する出力を定義
outputs = [grpcclient.InferRequestedOutput("output_logits")]
# 推論リクエストを送信
response = client.infer(model_name="sample_model", inputs=inputs, outputs=outputs)
# 結果をNumPy配列として取得
result = response.as_numpy("output_logits")
print("出力サイズ:", result.shape)
print("ロジット値:", result)
4. 本番環境でのTriton運用における実践的なTips
ノートブック上でのモデル検証と、ピーク時に毎秒数千リクエスト(RPS)を捌く本番運用とでは状況が大きく異なります。以下に、実際の運用現場から得られた重要なチューニングポイントを紹介します:
- Perf Analyzerによる正確な性能計測:
max_queue_delay_microsecondsやpreferred_batch_sizeといったパラメータを推測で設定してはいけません。公式ツールのperf_analyzerを使用して、実際のトラフィック負荷を測定・分析します:
perf_analyzer -m sample_model -u localhost:8001 -i grpc --concurrency-range 1:16
このコマンドは1〜16の同時クライアント接続をシミュレートします。測定結果から、スループットが最大化しつつp99レイテンシが許容SLA(例:30ms未満)に収まる最適なスイートスポット(Sweet Spot)を特定できます。
- Dynamic Model ControlによるVRAM使用量の制御: 数十種類のモデルを運用している環境でGPUのVRAMが16GB程度の場合、起動時に全モデルをロードするとメモリ不足(CUDA OOM)を引き起こします。
--model-control-mode=explicitフラグを付与してTritonを起動することで、API経由で要求があった場合にのみモデルをVRAMにロードし、不要になった段階で解放することが可能になります。 - TensorRTへの最適化・変換: Turing/Ampere/Ada世代のNVIDIA GPU(T4、A10、L4など)では、
trtexecを用いてONNXモデルをTensorRTエンジン(.plan)へコンパイルすることを強く推奨します。これにより、ONNX Runtimeと比較して推論レイテンシを40〜60%短縮し、VRAM使用量も大幅に削減できます。 - Prometheusアラートの設定: ポート8002の
/metricsエンドポイントからPrometheus標準メトリクスが取得可能です。特にnv_inference_request_duration_us(合計処理時間)、nv_inference_queue_duration_us(キュー滞留時間)、およびnv_gpu_memory_used_bytes(GPUメモリ使用量)の3つの重要指標を常時監視し、サーバーが高負荷に達する前に自動スケールアウトを発火させる仕組みを構築しましょう。

