ステータス: ドラフト(2026-11-14)、実測値。 Dynamo v1.6.2(チャート
dynamo-platform-1.6.2、オペレーターイメージkubernetes-operator:1.6.2)をKubernetes v1.34、Helm v3.18に2026-11-13に直接インストールした際の記録です。リソース数はhelm templateの出力から数えたものであり、エラーメッセージは実際の出力のままです。グローバル方式は確認のみでインストールはしていません。グローバル方式のPod Webhookが他の名前空間のPod作成に与える遅延は未計測です。 作成ルール: 実測データを基本とし、測定できなかったものは「未計測」、確認できなかったものは「未確認」と表記します。
目次
- 1. vLLMのみを使用する際にはなかったもの
- 2. グローバル方式: すべてのPod作成にWebhookが介入する
- 3. Webhookは値で絞り込むことができない
- 4. 制限方式を単体で使用した場合に欠落する3つの要素
- 5. 名前空間を分けるとオペレーターが161回停止した
- 6. 閉域網への持ち込み
- 7. まとめ: インストール前に決定すべきこと
- 参考
1. vLLMのみを使用する際にはなかったもの
vLLMはDeploymentとServiceがあれば起動します。クラスター範囲のリソースは1つも作成しません。
Dynamoは異なります。ワーカーとフロントエンドを DynamoGraphDeployment という1つのカスタムリソースで記述し、オペレーターがそれを読み込んでDeploymentとServiceを作成します。そのためには、CRD、オペレーター、承認Webhook、およびそれらの権限をあらかじめクラスターに配置しておく必要があります。複数のチームで共同利用するクラスターである場合、この部分はエンジンの性能よりも優先して検討すべき対象となります。
チャートのデフォルト値から記載します。1.6.2ではetcd、NATS、Grove、KAIスケジューラー、snapshotはいずれも無効化されています。ディスカバリーはKubernetes APIで、リクエストプレーンはTCPで、KVイベントはZMQで行うのがデフォルトであるためです。リポジトリの複数のドキュメントでは依然としてNATSとetcdを必須のように記載していますが、4つすべてなしでインストールして第3部の測定を最後まで実行できました。
2. グローバル方式: すべてのPod作成にWebhookが介入する
デフォルトのインストールは、1つのオペレーターがクラスター全体を監視するグローバル方式です。インストール前に helm template でレンダリングして作成されるリソース数をカウントしました(チェックポイント機能を無効化した値)。
| 種類 | グローバル方式 | 名前空間制限方式 |
|---|---|---|
| ClusterRole | 12 | 7 |
| ClusterRoleBinding | 10 | 7 |
| Role | 1 | 6 |
| RoleBinding | 1 | 6 |
| ServiceAccount | 2 | 5 |
| Webhook(変形 4 + 検証 4) | 8、セレクターなし | 8、対象名前空間セレクター |
| PodMonitor | 5 | 5 |
グローバル方式の8つのWebhookには namespaceSelector も objectSelector もありません。7つはDynamoのカスタムリソースのみを対象とするため、他のチームへの影響はありません。残りの1つが問題です。
mpodcheckpointrestore.kb.io resources: pods operations: CREATE
failurePolicy: Ignore timeoutSeconds: 10
クラスター内のすべてのPod作成がこのWebhookを経由します。 チェックポイント機能を無効化しても(checkpoint.enabled: false)登録されます。 failurePolicy が Ignore であるため、オペレーターが停止していてもPodの作成がブロックされることはありません。ただしその場合、APIサーバーがWebhookからの応答を待つ時間がPod作成ごとに発生する可能性があります。その遅延が実際にはどの程度なのかは、グローバル方式をインストールしなかったため測定できませんでした(未計測)。
検証目的で導入するオペレーターを、他のチームのPod作成パスに組み込みたくありませんでした。そのため、Webhookをテスト用の名前空間に限定しようと試みました。
3. Webhookは値で絞り込むことができない
チャートには webhook.namespaceSelector という値が存在します。ここにセレクターを指定してインストールしようとすると、Helmによって拒否されます。
VALIDATION ERROR: webhook.namespaceSelector must be empty. Helm manages admission
scope: cluster-wide webhooks cover every namespace and namespace-restricted
webhooks cover only their target namespace.
つまり、Webhookのスコープはインストール方式によって決定され、ユーザーが個別に選択することはできないということです。残された手段は名前空間制限方式(namespaceRestriction.enabled: true)のみです。この方式でレンダリングすると、8つのWebhookすべてに次のセレクターが付与されます。
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: <대상 네임스페이스>
制限方式はCRDの自動更新と併用できません。両方を同時に有効にすると、これも拒否されます。
VALIDATION ERROR: namespaceRestriction.enabled=true is incompatible with
upgradeCRD=true. Set upgradeCRD=false so the namespaced release does not install
or update cluster-wide CRDs.
また、オペレーターを制限方式で起動すると、ログに大きな警告が出力されます。「DEVELOPMENT AND TESTING ONLY: Namespace-restricted mode is not supported for production.」チャートの検証メッセージでも、制限方式は「グローバルオペレーターの傍らに開発・検証目的で並行して配置するもの」と説明されています。 本番環境でサポートされている唯一の手段はグローバル方式であり、その方式を選択する場合は第2節のPod Webhookを受け入れる必要があります。
4. 制限方式を単体で使用した場合に欠落する3つの要素
グローバルオペレーターなしで制限方式のみをインストールしたところ、3つの要素が不足していました。これらはグローバルオペレーターが処理していたためです。
CRDが存在しない。 upgradeCRD: false が必須であるため、CRDを手動で追加する必要があります。リポジトリの deploy/operator/config/crd/bases/ から6つを取得しました。最も大きいファイルは3,038,832バイトです。クライアント側の kubectl apply は適用した内容の全体をアノテーション(annotation)に再格納するため、このサイズを超えることはできません。 kubectl apply --server-side を使用して追加しました。
変換Webhookが参照するサービス名が異なる。 CRDオリジナルの変換Webhookは dynamo-system 名前空間の dynamo-operator-webhook-service を指しています。チャートが作成するサービスの名称は <릴리스 전체 이름>-webhook-service であり、リリース名を dynamo-platform とすると dynamo-platform-dynamo-operator-webhook-service になります。CRDを追加する前に、この名前と名前空間を実際の値に修正する必要があります。
変換WebhookのCAが含まれていない。 Webhook証明書はオペレーター自身が生成してSecretに格納しますが、そのCAをCRDの caBundle に挿入する権限はグローバルオペレーターにしかありません。インストール後、Secretから ca.crt を取り出して4つのCRD(dynamocomponentdeployments, dynamographdeploymentrequests, dynamographdeployments, dynamographdeploymentscalingadapters)に直接書き込みました。
これら3つをすべて補って初めて、 v1beta1 リソースを作成することができました。
5. 名前空間を分けるとオペレーターが161回停止した
オペレーターを dynamo-system に、ワークロードを別の名前空間に配置しました。権限を持つプロセスとモデルサーバーを分離するという一般的な構成です。制限方式でこの構成を採用すると、オペレーターを長く稼働させることができませんでした。
failed to list *v1.Secret: secrets is forbidden: User "system:serviceaccount:
dynamo-system:dynamo-platform-dynamo-operator-controller-manager" cannot list
resource "secrets" in API group "" in the namespace "dynamo-system"
...
problem running manager {"error": "failed to wait for cert-rotator caches to sync
kind source: *v1.Secret: timed out waiting for cache to be synced ..."}
オペレーター内の証明書ローテーターは、自身の名前空間のSecretを監視します。監視するにはリスト取得(list)およびウォッチ(watch)権限が必要です。チャートがリリース名前空間に作成するRoleは次のようになっています。
rules:
- apiGroups: [""]
resources: [secrets]
resourceNames: [webhook-server-cert]
verbs: [get, list, watch, update, patch]
resourceNames で名前を制限したルールでは、 名前を指定しないリスト取得を許可することができません。 これはKubernetes RBACの既知の特性です。そのためキャッシュの同期が完了せず、タイムアウトを超過するとプロセスが自ら終了します。
グローバル方式ではClusterRoleが、リリース名前空間と対象名前空間が同一である制限方式では対象名前空間のRoleがSecretのリスト取得を許可しているため、この問題は表面化しません。2つの名前空間が異なる場合にのみ顕在化します。
実際に確認された状況は次のとおりです。
- コンテナの起動から約35秒後にコントローラーが処理を開始します。約2分30秒でプロセスが終了します(観測された1サイクル: 15:17:27開始、15:19:58終了)。
- Kubernetesが再起動するまで5分間待機します(CrashLoopBackOffの上限)。
- 20時間にわたり161回再起動を繰り返しました。
- 稼働している約2分間は、Webhookとコントローラーが正常に動作しました。その間に投入した
DynamoGraphDeploymentは25秒以内に4つのDeploymentと4つのServiceになりました。 - 停止している5分間は、Dynamoリソースの作成と更新が拒否されると考えられます。検証Webhookの
failurePolicyがFailであるためです。本記事では稼働しているタイミングを待って適用しており、停止している間に意図的に投入するテストは行っていません(未計測)。 - すでに起動しているワーカーとフロントエンドは影響を受けませんでした。 第3部の測定の一部はオペレーターが停止している間に実行されましたが、エラーは発生しませんでした。ワーカーの登録はワーカーが直接使用する
DynamoWorkerMetadataリソースによって行われ、オペレーターを経由しないためです。
原因が権限にあるため、修正方法としてはリリース名前空間にSecretの get·list·watch を許可するRoleを追加することです。あるいは、オペレーターとワークロードを同じ名前空間に配置します。本記事の測定ではRoleを追加せず、オペレーターが稼働しているタイミングを待って適用する方法で完了させたため、両方の方法を適用しての確認は行っていません(未計測)。
RunningとCrashLoopBackOffを行き来するPodは、一見すると正常に見えることがあります。オペレーターをインストールした後は、再起動回数を数分おきに2回確認する必要があります。
6. 閉域網への持ち込み
外部から取得したものは4つです。
| 対象 | 名称 | サイズ | 必須か |
|---|---|---|---|
| ランタイムイメージ | nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.6.2 |
10,203,035,478バイト | はい。ワーカーとフロントエンドで共用 |
| オペレーターイメージ | nvcr.io/nvidia/ai-dynamo/kubernetes-operator:1.6.2 |
94,673,132バイト | はい |
| チャート | dynamo-platform-1.6.2.tgz |
516,410バイト | はい |
| モデル | Qwen/Qwen3-0.6B、リビジョン固定 | 1,519,209,243バイト | はい。ワーカーノードごとに必要 |
| フロントエンド専用イメージ | dynamo-frontend:1.6.2 |
約3.76GiB | いいえ。ランタイムイメージで代替可能 |
| NATS、etcd、Grove、KAI | いいえ。デフォルトで無効 |
ランタイムイメージにはvLLM 0.31.0、 ai-dynamo 1.6.2、PyTorch 2.13.0+cu130、NIXL 1.4.1が含まれています。負荷ツールAIPerfはランタイムイメージには含まれておらず、フロントエンド専用イメージにのみ含まれています。
導入時に直面した課題は3つあります。
10GBのイメージが、同じノード上の他のデプロイの起動をブロックする。 ランタイムイメージのプルにノードあたり約11分かかりました。コンテナランタイムがイメージを1つずつ順番にプルするように設定されているノードでは、その間他のイメージが待機状態になります。実際、同じノードで起動するはずだった別のデプロイが12分11秒待機した末に進捗タイムアウトを超過して失敗しました。サイズが大きいイメージは、デプロイのない時間帯に事前にプルしておく必要があります。
リビジョンをハッシュ値で取得すると、オフライン環境でモデルが見つからない。 snapshot_download(..., revision="<커밋 해시>") で取得した場合、キャッシュに refs/main が作成されません。その状態で HF_HUB_OFFLINE=1 に --model Qwen/Qwen3-0.6B を指定すると、リポジトリ名をスナップショットとして展開できません。取得後、 refs/main ファイルにそのハッシュ値を記述しておきました。リビジョンを固定しない方法は推奨しません。名前だけを指定すると、来月には別のファイルを指す可能性があるためです。
モデルディレクトリは読み取り専用でマウントしても問題ない。 ワーカーがキャッシュインデックスファイルの書き込みを試みて Permission denied の警告を残しますが、読み込みは正常に完了します。
7. まとめ: インストール前に決定すべきこと
| 項目 | グローバル方式 | 名前空間制限方式 |
|---|---|---|
| 本番環境でサポートされているか | はい | いいえ(開発・検証専用である旨がログに出力される) |
| 他のチームのPod作成に影響するか | はい。Pod CREATE変異ウェブフック、 Ignore、10秒 |
いいえ |
| CRDを誰が入れるのか | オペレーター | 人が直接 |
| 変換ウェブフックのアドレスとCA | オペレーターが設定 | 人が直接 |
| オペレーターとワークロードのネームスペース分離 | 可能 | Roleをもう一つ追加する必要がある |
| クラスター範囲の権限 | ClusterRole 12個 | ClusterRole 7個 + Role 6個 |
お試しで導入する際は制限方式が他のチームに影響しないため安全ですが、手間がかかります。本番に導入する際はグローバル方式であり、その前にPodウェブフックの範囲がクラスター全体という事実をクラスター管理者と合意しなければなりません。どちらにせよ、オペレーターが停止してもすでに起動しているサービングは継続されるという点は確認できました。
参考
- Dynamoリポジトリ v1.6.2:
deploy/helm/charts/platform/components/operator/templates/の_validation.tpl,webhook-rbac.yaml,manager-rbac.yaml,deploy/operator/cmd/main.go. - Kubernetesドキュメント、Using RBAC Authorization の
resourceNames説明。 - インストールに使用した値ファイルとコマンドはリポジトリの
docs/experiments/2026-11-13-dynamo-adoption/にあります。