
こんにちは。SB C&S の村上です。
この記事では、NVIDIA Dynamoを実際に動かしながら、推論処理を実行するまでの流れを紹介します。
なお、本記事はNVIDIA Dynamo連載の第3回になります。
第1回と第2回にて概要や機能についての紹介をしていますので、ぜひ先にそちらをご覧ください。
【連載①】NVIDIA Dynamo 紹介:概要編
【連載②】NVIDIA Dynamo 紹介:3つの最適化機能
【連載③】NVIDIA Dynamo 紹介:Dynamoを用いた推論の実行←本記事
【連載④】NVIDIA Dynamo 紹介:KV Cache-Aware Routingを用いたルーティング最適化
【連載⑤】NVIDIA Dynamo 紹介:Disaggregated Servingを用いた分散推論
【連載⑥】NVIDIA Dynamo 紹介:KVBM / KV Cache Offloadingを用いたKVキャッシュの効率化
実施する内容
今回は、Dynamoを利用した推論環境として以下の2つの構成をそれぞれ実施します。
- 最小構成での推論の実行
- 複数Workerでの推論の実行
Dynamoでは、主に以下のコンポーネントを利用します。
- Frontend
- クライアントから推論リクエストを受け付ける窓口となるコンポーネント
- OpenAI互換APIを公開し、受け取った推論リクエストをWorkerへ振り分ける
- Worker
- Frontendから受け取ったリクエストに対して実際のモデル推論を行うコンポーネント
- vLLMやSGLangなどの推論エンジンに対応しており、用途や構成に応じて利用する推論エンジンを選択可能
- 本記事では、NVIDIA H200を搭載したサーバー上でvLLMを利用
- etcd
- 分散システムで広く利用されているKey-Value Store
- DynamoではFrontendとWorkerのサービス情報を共有し、各コンポーネントを検出するためのサービスディスカバリとして動作
- ※推論リクエスト自体はetcdを経由せず、FrontendからWorkerへ送信されます
※本記事では、複数サーバー間でのサービスディスカバリにetcdを利用します。
ただし、最新のDynamoでは、Kubernetes環境におけるetcdベースのDiscovery Planeは非推奨(deprecated)となっています。本記事はDynamo v1.4.0を用いた検証構成を紹介するものであり、最新のKubernetes環境で構築する場合には公式ドキュメントを参照してください。
まず最小構成では、1台のGPUサーバー上でFrontendとWorkerを動作させ、DynamoからLLMの推論を実行できることを確認します。
続いて複数Worker構成では、Frontendとetcdをそれぞれ独立したサーバーに配置し、2台のGPUサーバー上でWorkerを動作させます。Frontendから複数のWorkerへリクエストを振り分ける様子を確認していきます。
環境準備
本記事では以下4台のサーバーを利用します。
1つ目の「最小構成での推論の実行」では、Worker_1のみを使用し、1台のサーバー上でFrontendとWorkerの両方を動作させます。
2つ目の「複数Workerでの推論の実行」では、4台すべてを使用します。etcdとFrontendをそれぞれ独立したサーバーに配置し、Worker_1とWorker_2それぞれをWorker用として動作させます。
各サーバーのOSはUbuntu 24.04で、コンテナを実行できる状態まで準備済みとしています。
Workerとして動作させるWorker_1およびWorker_2にはNVIDIA H200を搭載し、NVIDIA GPU DriverとNVIDIA Container Toolkitをインストール済みとしています。
コンテナイメージとしては、以下の2種類を利用します。
基本となるDynamoのコンテナイメージに加えて、複数Worker構成では、サービスディスカバリのためにetcdも使用します。なお、今回は推論エンジンに vLLM を用いるため、vLLM 用のコンテナイメージを使用します。
- Dynamo vLLM Runtime
- vLLMの推論エンジンを搭載したDynamoのコンテナイメージ
- nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0
- etcd
- 複数Worker構成におけるサービスディスカバリに使用
- quay.io/coreos/etcd:v3.5.14
利用するLLMに関しては事前にHugging FaceからQwen3-32Bをダウンロードして、マシン上の/opt/models配下に格納済みとなります。
https://huggingface.co/Qwen/Qwen3-32B
なお、以降の手順では各サーバーのIPアドレスを${ETCD_IP}、${FRONTEND_IP}、${WORKER1_IP}、${WORKER2_IP}のように表記します。
実行時には、それぞれ利用する環境のIPアドレスに読み替えてください。
①最小構成での推論の実行
まずは、NVIDIA Dynamo を最小構成で起動し、推論 API が利用できるところまで確認します。 この構成では、FrontendとWorkerを1台のGPUサーバー上で動作させます。
まずはWorker_1として利用するGPUサーバー上で、Dynamo vLLM Runtimeコンテナを起動します。
docker run --rm -d -it \
--name dynamo-single \
--gpus all \
--ipc=host \
--network host \
-v /opt/models:/models:ro \
nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0 bash
主なオプションは以下となります。
- --gpus all
- コンテナでGPUを利用
- --ipc=host
- vLLMなどで利用する共有メモリ領域を確保するため、ホスト側IPC名前空間を共有
- --network host
- ホスト側のネットワークをコンテナで利用
- -v /opt/models:/models:ro
- モデル格納ディレクトリをコンテナ内の/modelsへマウント
続いて、コンテナ内でFrontendとWorkerを起動するコマンドを実行するため、コンテナのシェルに入ります。
docker exec -it dynamo-single bash
コンテナ内に入ったら、まずFrontendを起動します。
最小構成での実施のため、サービスディスカバリにはfileバックエンドを利用します。
このfileバックエンドでは、FrontendやWorkerのサービス情報をローカルのファイルを介して共有するため、etcdなどの外部サービスを用意せずにシンプルな構成で利用できます。
※複数サーバーにまたがる構成では、各コンポーネント間でサービス情報を共有する必要があるため、後続の複数Worker構成ではetcdを利用します
python3 -m dynamo.frontend \ --discovery-backend file \ --http-port 8000 &
次に、推論エンジンとして動作するWorkerを起動します。このプロセスが、Frontend 経由で受け取ったリクエストに対して実際のモデル推論を実行します。
ここではdynamo.vllmを用い、使用するモデルやサービスディスカバリ方式、推論時の動作設定をあわせて指定しています。
python3 -m dynamo.vllm \ --model /models/Qwen3-32B \ --served-model-name qwen3-32b \ --discovery-backend file \ --dyn-reasoning-parser qwen3
- --model
- 使用するモデルのパスを指定
- --served-model-name
- APIからモデルを利用するときのモデル名
- --discovery-backend file
- Frontendと同じfileバックエンドを利用
- --dyn-reasoning-parser qwen3
- Qwen3のReasoning出力を処理するParserを指定
Workerの起動後、モデルの読み込みが完了すると、Frontend側に以下のようなログが出力されます。
2026-04-07T09:32:11.657935Z INFO dynamo_llm::discovery::watcher: Chat completions is ready
また、前後には以下のようなログも出力されます。
dynamo_llm::http::service::service_v2: completion endpoints enabled
これら一連のログが出力されていることを、推論APIの準備が完了した目安として確認します。
それではまずはモデルが正しく公開されているかを確認するため、モデル一覧を取得します。
出力結果として「qwen3-32b」が表示されれば、FrontendからWorker上のモデルが認識され、推論APIの起動が完了していることを確認できます。
curl -s http://${WORKER1_IP}:8000/v1/models | jq
{
"object": "list",
"data": [
{
"id": "qwen3-32b",
"object": "model",
"created": 1788325524,
"owned_by": "nvidia",
"context_window": 40960
}
]
}
続いて、実際にチャット形式のリクエストを送り、モデルから応答が返ることを確認します。
無事に応答が返ってくれば、Dynamoの推論 API が正しく動作しています。
curl -s http://${WORKER1_IP}:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-32b",
"messages": [{"role": "user", "content": "こんにちは!"}],
"max_tokens": 1024
}' | jq -r '.choices[0].message.content'
## 出力
こんにちは!😊 お手伝いできることがありますか?
ここまででFrontendとWorkerを1台のGPUサーバー上で動作させる最小構成で、Dynamoを利用したLLM推論の実行ができました。
この状態でも簡易的な推論APIとして利用することができますが、実際には負荷増加や可用性の観点で複数のWorker上で推論エンジンを動かすことが多くなります。次の章では実際に複数ノードを使う場合の構成を作成していきます。
②複数Workerでの推論の実行
続いて、複数Workerを用いた構成を作成していきます。
前章ではFrontendとWorkerを単一サーバー内で動作させましたが、この章ではFrontendとWorkerをそれぞれ独立したサーバー上のコンテナとして起動し、Frontend から複数のWorkerへリクエストを振り分ける構成を試します。
この構成では、FrontendやWorkerが互いの存在を検出するためのサービスディスカバリとしてetcdを用います。
Workerが起動すると自身のエンドポイント情報がetcdを通じて共有され、Frontendはetcdを参照することで利用可能なWorkerを検出し、随時推論リクエストを振り分けます。
まずは、前章でWorker_1上に起動したコンテナを停止しておきます。
docker stop dynamo-single
etcdの起動
最初に、etcdサーバー上でetcdコンテナを起動します。
Frontendと各Workerは、このetcdを利用してサービス情報を共有します。
etcd用のサーバー上で以下コマンドを実行します。
docker run --rm -d \
--name dynamo-etcd \
--network host \
quay.io/coreos/etcd:v3.5.14 \
/usr/local/bin/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://${ETCD_IP}:2379
- --listen-client-urls
- etcdがクライアントからの接続を待ち受けるアドレス
- 0.0.0.0を指定し、すべてのネットワークインターフェースで待ち受け
- --advertise-client-urls
- FrontendやWorkerへ通知するetcdの接続先
- 今回はetcdサーバーのIPアドレスを指定
起動後、以下コマンドでetcdの状態を確認し、trueが返答されれば準備OKです。
curl -s http://${ETCD_IP}:2379/health
## 出力
{"health":"true","reason":""}
Frontendの起動
続いて、Frontendサーバー上でFrontendの処理を起動します。
以下コマンドでFrontendサーバー上でDynamoのコンテナとFrontendのプロセスを起動します。
(docker runの実行時にFrontendの起動もあわせて実施しています)
なお、FrontendではGPUを利用した推論処理を行わないため、--gpusは指定しません。
docker run --rm -d \
--name dynamo-frontend \
--network host \
-v /opt/models:/models:ro \
-e ETCD_ENDPOINTS=http://${ETCD_IP}:2379 \
-e DYN_REQUEST_PLANE=tcp \
nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0 \
python3 -m dynamo.frontend \
--discovery-backend etcd \
--model-name qwen3-32b \
--model-path /models/Qwen3-32B \
--http-port 8000
主な設定は以下となります。
- ETCD_ENDPOINTS
- サービスディスカバリに利用するetcdの接続先
- DYN_REQUEST_PLANE=tcp
- FrontendとWorker間のリクエスト通信にTCPを利用
- ※tcpはデフォルト値
- --discovery-backend etcd
- Workerの検出にetcdを利用
- --model-name
- Frontendで扱うモデル名を指定
- --model-path
- Frontendが利用するモデル設定やTokenizerの参照先
- --http-port 8000
- OpenAI互換APIを8000番ポートで公開
Frontendではモデル推論そのものは行いませんが、リクエストの前処理でモデルの設定ファイルやTokenizerを利用するため、モデルディレクトリをマウントして参照できるようにしています。
起動後、以下コマンドでstatusがhealthyなのを確認しておきます。
※この段階ではまだWorkerが起動していないため、endpointsやinstancesは空です
curl -s http://${FRONTEND_IP}:8000/health
{"status":"healthy","endpoints":[],"instances":[]}
これでFrontend側の準備は完了です。
各Workerを起動
最後に、各Worker上でコンテナを起動していきます。
まずはWorker_1上で以下コマンドを実行し、DynamoのコンテナとWorkerのプロセスを起動します。
(前述のFrontendと同様にdocker runの実行時にWorker処理の起動もしています)
docker run --rm -d \
--name dynamo-worker \
--gpus all \
--ipc=host \
--network host \
-v /opt/models:/models:ro \
-e ETCD_ENDPOINTS=http://${ETCD_IP}:2379 \
-e DYN_SYSTEM_PORT=8081\
-e DYN_REQUEST_PLANE=tcp \
nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0 \
python3 -m dynamo.vllm \
--model /models/Qwen3-32B \
--served-model-name qwen3-32b \
--discovery-backend etcd \
--dyn-reasoning-parser qwen3
基本的なオプションは前章の最小構成と同じですが、複数Worker構成では以下の設定が変更されています。
- ETCD_ENDPOINTS
- Frontendと共通のetcdサーバーを指定
- DYN_SYSTEM_PORT=8081
- Worker側のシステム状態・メトリクス収集用ポートを有効化
- Prometheusなどからリクエストの状態などを監視可能
- DYN_REQUEST_PLANE=tcp
- Frontendとのリクエスト通信にTCPを利用
- --discovery-backend etcd
- 前章のfileからetcdへ変更し、Workerのサービス情報をetcd経由で共有
Workerが起動すると、自身のエンドポイント情報がetcdを通じて共有され、Frontendから利用できるようになります。
以下コマンドでFrontendの状態を確認し、先ほどは空だったendpointsやinstancesにWorkerの情報が表示されれば、Worker_1がFrontendから正常に検出されています。
curl -s http://${FRONTEND_IP}:8000/health | jq
## 出力抜粋
"endpoints": [
"dyn://dynamo.backend.clear_kv_blocks",
"dyn://dynamo.backend.generate",
"dyn://dynamo.backend.get_perf_metrics"
],
"instances": [
{
"component": "backend",
"endpoint": "clear_kv_blocks",
"namespace": "dynamo",
"instance_id": 7587897283809533460,
"transport": {
"tcp": "${WORKER1_IP}:44577/694da060919e5e14/clear_kv_blocks"
},
"device_type": "cuda",
"request_plane_codec": "msgpack"
},
同様に、Worker_2側でも同じコマンドでコンテナとWorkerのプロセスを起動します。
docker run --rm -d \
--name dynamo-worker \
--gpus all \
--ipc=host \
--network host \
-v /opt/models:/models:ro \
-e ETCD_ENDPOINTS=http://${ETCD_IP}:2379 \
-e DYN_SYSTEM_PORT=8081\
-e DYN_REQUEST_PLANE=tcp \
nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0 \
python3 -m dynamo.vllm \
--model /models/Qwen3-32B \
--served-model-name qwen3-32b \
--discovery-backend etcd \
--dyn-reasoning-parser qwen3
こちらもモデルの読み込みが完了すると、2台目のWorkerとしてetcdへ登録されます。
再度Frontendの状態を確認しinstancesにWorker_1とWorker_2の2つのWorkerが表示されれば、Frontendから両方のWorkerを検出できています。
※transportのところで、Worker_1のIPとWorker_2のIPがそれぞれ表示されます
複数Workerへのリクエスト振り分けの確認
実行中、それぞれのWorkerのログを確認すると、どちらもリクエストを受け取り推論処理を実施していることが確認できます。
また、DynamoのWorkerではPrometheus形式のメトリクスを取得できます。
これを利用して、先ほど送信したリクエスト数を可視化した結果が以下になります。
今回の実行結果では、10回のリクエストに対してWorker_1とWorker_2がそれぞれ5回ずつ処理していることが確認できました。
このように、Dynamoでは複数のWorkerを利用した推論環境を構成できます。今回は2台のWorkerを利用しましたが、さらにWorkerを追加して構成を拡張することも可能です。
また、本記事ではコンテナを利用して構築しましたが、DynamoはKubernetes環境にも対応しており、より大規模な構成へ展開することもできます。
まとめ
本記事では、NVIDIA Dynamoを実際に動かし、LLMの推論を実行するまでの流れを紹介しました。
最小構成では1台のGPUサーバー上でFrontendとWorkerを動作させ、さらに複数Worker構成ではetcdを利用して複数のWorkerを検出し、推論リクエストを振り分けられることを確認しました。
このようにDynamoでは、シンプルな構成から始め、Workerを追加することで推論環境を拡張していくことができます。
今回は基本的な推論の実行までを確認しましたが、次回からはDynamoの特徴となる推論最適化機能を実際に試していきます。 第4回ではKV Cache-Aware Routingを用いたリクエストルーティングの最適化について紹介します。
【連載④】NVIDIA Dynamo 紹介:KV Cache-Aware Routingを用いたルーティング最適化
NVIDIAに関する情報発信はこちら
著者紹介
SB C&S株式会社
ICT事業本部 技術本部 第1技術統括部
第2技術部 1課
村上 正弥 - Seiya.Murakami -
VMware vExpert
