- Dify
- エージェントプラットフォーム
Dify Enterpriseのオンプレミス導入:Kubernetesアーキテクチャとキャパシティプランニングの実践
執筆:Apollaデリバリーチーム読了目安:約4分
まず結論
Kubernetes上のDify Enterpriseで同時接続ユーザー3,000人を支える典型的な構成は、3つのノードプールに分ける形です。業務アクセスプール16C/32G×4、非同期タスクプール32C/64G×4、インフラプール8C/16G×2に加えて、PostgreSQL、Redis、オブジェクトストレージ、ベクトルデータベースが必要です。負荷試験で最初にボトルネックになったのはモデル呼び出しではなく、ドキュメント解析の非同期タスクキューとベクトルデータベースの書き込みスループットでした。
ノードプールを分ける理由
Difyのコンポーネントは、それぞれ負荷特性が大きく異なります。APIとWebは典型的なオンラインリクエストを処理し、レイテンシに敏感です。Workerはドキュメント解析、ベクトル化、バッチ処理を担い、CPUとメモリを大量に消費しますが、キューで待たせることは許容されます。データベースとキャッシュには安定したI/Oが必要です。これらを同じノード群に同居させると、ナレッジベースへの大規模な一括インポートが1回走っただけで、オンラインの対話機能がダウンしかねません。
3つのノードプールの構成
- 業務アクセスプール16C/32G×4:api、web、plugin-daemon、sandbox。CPU使用率60%で自動スケールアウト
- 非同期タスクプール32C/64G×4:worker(解析、ベクトル化、バッチタスク)。キュー長に応じてスケールアウト
- インフラプール8C/16G×2:Ingress、監視、ログ収集。taintを付与し、業務コンポーネントがここにスケジュールされないようにする
- 3000人
- 目標とする同時接続ユーザー数
- 10ノード
- 3つのノードプールの合計
- <2秒
- 負荷試験におけるP95の初回トークン応答時間
Apolla導入時の実測値(ある電力グループの国産ITスタック〈中国の信創〉環境)
ミドルウェアの選定
PostgreSQLは独立して配置し、読み書き分離を有効にすることを推奨します。最大接続数は「apiレプリカ数×コネクションプール上限」の1.5倍を目安に余裕を持たせます。RedisはCeleryのブローカーとキャッシュの両方に使いますが、タスクの滞留がセッションキャッシュに影響しないよう、インスタンスを分けることを推奨します。オブジェクトストレージはS3互換インターフェースを利用します。ベクトルデータベースは、10万ドキュメント以下ならpgvectorで十分で、それを超えた段階で独立したベクトルデータベースへ移行します。
負荷試験で顕在化した3つのボトルネック
1つ目は、2,000件のPDFを一括インポートした際に、Workerキューの滞留が40分を超えた問題です。原因は解析タスクのデフォルトの並列数が低すぎたことで、Workerの並列数を4から16に引き上げ、解析キューを専用に分離したところ、6分まで短縮できました。2つ目は、並列インポート時にベクトルデータベースの書き込みスループットがボトルネックになった問題で、バッチ書き込みに変更することで約5倍に向上しました。3つ目は、モデルゲートウェイでコネクションの再利用が無効になっていたために、高並列時にTCPコネクションが枯渇した問題です。keep-aliveを有効にしたところ解消しました。
見落とされがちなポイント
sandboxコンポーネントはデフォルトで外部ネットワークへの通信を許可しています。本番環境では必ずネットワークポリシーを厳格化し、必要な内部アドレスのみを許可してください。そうしなければ、コード実行ツールがデータ流出の経路になりかねません。
国産ITスタック環境での追加作業
Kylin V10 ARM64 + Dameng + containerdという国産ITスタック(中国の信創。国産ハードウェア・OS・DBで構成された環境)では、ARM64イメージをすべてビルドし直す必要があり、依存関係のうち3つのネイティブモジュールはソースからコンパイルしなければなりませんでした。DamengのJSONBと配列型のサポートはPostgreSQLと差異があり、ORM層での対応が必要です。この部分の作業量は導入全体の約30%を占めるため、計画上は独立した項目として計上することを推奨します。