アーキテクチャ
全体像
Kthena はコントロールプレーンとデータプレーンを分離し、片方だけでもデプロイして使える (README.md:55)。コントロールプレーンはカスタムリソースを推論ワークロードへ reconcile し、配置を Volcano スケジューラに任せる。データプレーンは推論トラフィックを終端し、各リクエストをバックエンド pod へルーティングする。バイナリは 2 つで、データプレーンが cmd/kthena-router/main.go:40、コントロールプレーンが cmd/kthena-controller-manager/main.go:54。
コンポーネント
kthena-controller-manager (コントロールプレーン)
Kthena の CRD を reconcile し、推論レプリカの deploy / scale / upgrade を回す。有効化できる controller は modelserving / modelbooster / autoscaler (cmd/kthena-controller-manager/main.go:81-82)。gang scheduling は自前実装せず Volcano に委譲する。また secure port 8443 で webhook サーバを動かし、証明書を自動生成して ValidatingWebhook / MutatingWebhook の CABundle を更新する (cmd/kthena-controller-manager/main.go:74)。所有する workload CRD は pkg/apis/workload/v1alpha1 にある。
kthena-router (データプレーン)
推論トラフィックの入口。各リクエストを model 名・ヘッダ・URI で分類し、ロードバランスとトラフィック制御を当て、適切な推論インスタンスへ振る。prefill-decode 分離ルーティングをネイティブ対応する。README は、router が参照実装であり、Gateway Inference Extension が prefill-decode 分散をネイティブにサポートしないため使われていること、標準 API gateway の後段にも置けることを明記している (README.md:64)。router は pkg/apis/networking/v1alpha1 の networking CRD、すなわち ModelRoute (マッチ規則 + レート制限) と ModelServer (PDGroup / KVConnector / TrafficPolicy を持つバックエンド pod 群) を参照する。
リクエストの流れ
router を通る 1 推論リクエスト (pkg/kthena-router/router/router.go):
- gin ハンドラ
Router.HandlerFunc()(router.go:210) がGET /v1/modelsをListModelsで即返し (router.go:216-220)、それ以外は本処理へ進む。 ParseModelRequest(router.go:491) がボディを読み、OpenAI 互換 structOpenAIRequestBody(pkg/kthena-router/handlers/request.go:29) からmodelフィールドを取り出す。- プロンプトを tokenize して入力トークン数を算出し、失敗時は
len(promptStr)/4にフォールバックする (router.go:269-273)。 r.loadRateLimiter.RateLimit(modelName, promptStr)(router.go:285) でレート制限。入力トークン・出力トークン・リクエスト数の 3 種を判定し、超過は HTTP 429。- fairness scheduling が無効なら
doLoadbalance(router.go:322→:335)、有効ならhandleFairnessScheduling(router.go:327→:1037) へ。 doLoadbalance(router.go:335) はr.store.MatchModelServer(router.go:359) でModelRouteを先にマッチし、getPodsAndServer(router.go:369→:513) で pod 群とModelServerを取得し、framework.Contextを組み (router.go:451-457)、r.scheduler.Schedule(ctx, pods)(router.go:459) で pod を採点する。- 選抜結果を
proxyModelEndpoint(router.go:484→:682) でプロキシする。PD 非分離ならproxy(router.go:614)、KVConnector ありならproxyToPDDisaggregated(router.go:943)。 - プロキシ後に
r.scheduler.RunPostHooks(ctx, i)(router.go:675) が on-flight カウンタを更新する。
主要な設計判断
最も特徴的なのは、一部の同種プロジェクトが使う LeaderWorkerSet (LWS) / dual-LWS の多層パターンを避けたこと。代わりに単一の ServingGroup が Prefill / Decode の role を持ち、gang scheduling は Volcano の scheduling.volcano.sh/v1beta1 PodGroup に委譲する。メンテナの理由は、Volcano gang scheduling と統合するには別アーキが必要で、dual-LWS の層構造は彼らのユースケースでは明確な利点なく複雑さだけ増えた、というもの (pacoxu の比較)。ModelServingSpec.SchedulerName のデフォルトは volcano (pkg/apis/workload/v1alpha1/model_serving_types.go:47)。
2 つ目は、KV キャッシュ・prefix キャッシュの局所性をエンジン内ではなく router の L7 採点層で推定し、共通プレフィックスを持つリクエストを同じ pod に寄せること (pkg/kthena-router/scheduler/plugins/prefix_cache.go)。
拡張ポイント
- スケジューラ plugin: router のスケジューラは差し替え可能な score / filter plugin を回す (
pkg/kthena-router/scheduler/plugins)。デフォルトはleast-request/least-latency/prefix-cache(pkg/kthena-router/scheduler/scheduler_impl.go:68-72)。 - KV 転送 connector: prefill-decode の KV 転送は NIXL / MoonCake / SGLang 向け connector で抽象化 (
pkg/kthena-router/connectors)。 - CRD: workload CRD (
ModelServing/ServingGroup/ModelBooster/AutoscalingPolicy) と networking CRD (ModelRoute/ModelServer)。いずれもpkg/apis配下。 - Webhook: controller-manager が提供する ValidatingWebhook / MutatingWebhook (
cmd/kthena-controller-manager/main.go:74)。