DifyのPlugin Daemonはスケールアウトができるのか
概要
DifyのPlugin Daemonがスケールアウトできるのかを調査しました。
細部までの理解はできてないのですが以下のような結論です
- スケールアウトは可能
- pluginの数が多くなると、起動時の処理が遅くなる
- 負荷分散の仕組みは難しいので、特定のコンテナのプラグインに処理が集中する可能性がある
詳細
スケールアウトに関する記述
DifyのPlugin DaemonのREADMEに以下のような記述があります。
For now, Daemon community edition dose not support smoothly scale out with the number of replicas, If you are interested in this feature, please contact us. we have a more production-ready version for enterprise users.
smoothなスケールアウトとは何だろうということで、調べた結果を共有します
生成AIに聞いてみる
ChatGPT o4-mini-highに聞いてみました。
## 1. 共有ディレクトリ依存によるパフォーマンス問題
* daemon はプラグイン実行時にカレントディレクトリ(`cwd`)を共有ディレクトリとして使い、そこでプラグインのパッケージを解凍・キャッシュします。
* Kubernetes 等で複数レプリカを立てる際、これを NFS や他のネットワークボリュームで共有すると、ファイル I/O が遅くなり「プラグインがタイムリーに起動しない」などの性能劣化を招きます。
([github.com][1])
## 2. シンプルなシングルノード設計
* コミュニティ版はあくまで単一インスタンス向けに設計されており、レプリカ間で状態を同期する分散ロックやレプリケーション機構を持ちません。
* 各インスタンスが個別にプラグインをダウンロード・キャッシュするため、水平スケール時に冗長な I/O やキャッシュミスが発生します。
## 3. エンタープライズ版での拡張機能提供
* README にあるように「スムーズなスケールアウト対応」はエンタープライズ向けの機能として別途用意されており、コミュニティ版では実装されていません。
* 具体的には、中央ストレージや分散キャッシュ、レプリカ間のタスク分散制御などのプロダクション向け機能がエンタープライズ版でサポートされる想定です。
([github.com][1])
---
以上のように、プラグインデーモン自身がローカルファイルシステムと単一ノード前提の状態管理に依存しているため、複数レプリカでスムーズにスケールアウトすることが難しくなっています。もし大規模運用でのスケールアウトを検討されている場合は、README に案内のあるエンタープライズ版へのお問い合わせをおすすめします。
[1]: https://github.com/langgenius/dify-plugin-daemon "GitHub - langgenius/dify-plugin-daemon"
とはいえ、あっているかわからないので、実際に中身を見てみることにしました
Dify Plugin Daemonについて
DifyのLLMなどの処理は基本的にプラグインとして実行します
Plugin Daemonの主な用途としては以下の通りです
- プラグインのインストールと管理
- インストールは厳密には別のHTTPで受け付けている
- APIやWorkerからのリクエストの受付(HTTP)
- プラグインの実行管理
スケールアウトを実現するのであれば
Plugin DaemonはAPIやWorkerとHTTP通信でやりとりをしています
単一のエンドポインをを指定するとスケールアウトができないので、AWSであれば、ALBやService Discoveryなどのリクエストを分散するための仕組みが必要になります
リクエストベースやラウンドロビンによる負荷分散を行うため、処理によっては負荷が偏る可能性がありそうです。
スケールアウするときに検討が必要な内容
新しくコンテナが起動したときに、インストール済みのプラグインが実行できる状態になるのか
起動時は以下のような処理となります
- DBを参照して、インストールしているプラグインの一覧を取得して、指定したディレクトリ(AWSの場合はS3を指定)からファイルを取得します
- pluginはzipで圧縮されたファイルなので、展開して、指定のパスに配置します
↑ 厳密には展開のみで指定のパスへの配置は別のところでやっています
- 現状はすべてPythonで実装されているので、requirements.txtより依存するモジュールの一覧を取得します
- プラグインを実行します
上記のような仕組みを実行するため、新しく起動したコンテナでも、プラグインは同じものが入ります
ただし、Pluginごとに requirements.txt のインストールなどをするために、Pluginの数が多いと起動完了までに時間がかかることが予想されます
※ ローカルで実行するプラグインを前提としていますが、別のコンテナとして起動する場合やLambdaでの実行もできるようです
新しいプラグインのインストール
- HTTPのリクエストとして受け付けるため、初回のインストール自体は1つのコンテナで実行することになります
- Plugin DaemonはDBから定期的にインストールしているプラグインとローカルのプラグインの差分を検知して、インストールをするため、時間差は生じるものの、正常に動作します
apiやworkerからのリクエストの状況管理
- Dify apiやWorkerからHTTPでリクエストを受けた場合、Server-Sent Eventsを利用して実行状況の共有を行います。
- 受け付けたコンテナで処理状況の確認を行うため、コンテナが複数になっても問題はなさそうに思えます。
プラグインの実行管理
- Pluginの処理の実行管理はローカルでの実行の場合は、Stdoutを利用して管理されています
- ローカルのみでの実行管理となるため、複数コンテナがあったとしても状況の管理は問題なくできます
- なお、Pluginにつき1つのGo Routineになるため、同じプラグインに多くのリクエストが実行された場合、待ち時間が長くなることがあるようです
- ALBなどを利用して負荷分散したとしても、Pluginの実行状況まではわからないため、コンテナによって実行されているPluginが偏ってしまい処理が遅くなる可能性があります
結論
Plugin Daemonはある程度スケールアウトするための設定がすでに実現できているように見えました。
ただし、新しくコンテナを起動した場合にプラグインのインストールに時間がかかることがありそうであること、pluginの中の負荷による分散は難しいので、より正確なスケールアウトをする場合は、community editionでは難しいのかもしれません。
Discussion