Dify プラグインデーモンのスレッドリークに対するワークアラウンド

Dify プラグインデーモンのスレッドリークに対するワークアラウンド

2 min read

 

問題の概要 #

発生現象:

  • 大量のナレッジドキュメントをインデックスすると、プラグインデーモン(plugin_daemon)が無数のスレッドを生成し続け、最終的に「約30,000個」で新規スレッドが作れなくなる。
  • コンテナログには can't start new thread エラーが出て、サービスがフリーズする。

推定原因:

  • コンテナ/cgroup v2 の pids.max(プロセス+スレッド上限)が 29958(約3万)に設定されており、それを超えた時点でスレッド生成に失敗する。
  • プラグインデーモン側のスレッドリーク、あるいは大量同時処理によって短時間でスレッド数が肥大化している。

試行錯誤:

  • Docker Compose やコンテナ内で pids_limit: 0 や ulimit を調整しても、ホストの cgroup が 29958 に固定されているため効果がなかった。
  • /sys/fs/cgroup/system.slice/docker-.scope/pids.max の実際の値を確認したところ、29958 だった。

根本解決策 #

  • プラグインデーモン側のスレッドリークを修正(アップデートやパッチ適用)。
  • 一度に処理する文書数を小分けにして、スレッドが行き過ぎないようにする。

当面のワークアラウンド #

  • ホストの cgroup 設定 を変更し、pids.max を max(無制限)または大きな値に引き上げる。
  • あわせて systemd の slice ユニット (system.slice など) で TasksMax=infinity を設定して再起動後も元に戻らないようにする。

ワークアラウンド手順(ステップバイステップ) #

STEP 1. コンテナの「ホスト側PID」から cgroup パスを特定 #

コンテナID を確認:

docker ps

プラグインデーモンは 998eb0c50703 などのID。

docker top <コンテナID> で、ホスト上のPIDを取得:

docker top 998eb0c50703

例: /app/main のプロセスが 1012512 (ホストPID) など。

/proc/<ホストPID>/cgroup を参照:

cat /proc/1012512/cgroup

出力例: 0::/system.slice/docker-998eb0c5070321...scope

STEP 2. 実際の上限値 (pids.max) を変更 #

該当ディレクトリへ移動し、pids.max を確認:

cd /sys/fs/cgroup/system.slice/docker-998eb0c5070321...scope
cat pids.max

値が 29958 など数値になっていれば制限あり、max なら無制限。

無制限に変更:

echo max | sudo tee pids.max

これで「その場」は制限が解除されますが、再起動やコンテナ再生成で元に戻る可能性が高い。

STEP 3. systemd の設定で TasksMax を恒久的に無制限化 #

cgroup の設定がリセットされないよう、systemd の slice ユニット (system.slice) で TasksMax を上書きします:

3-1. オーバーライドファイルを作成: #

sudo systemctl edit --force --full system.slice

エディタが開いたら、以下のように [Slice] セクションを追記して保存します:

[Slice]
TasksMax=infinity

3-2. デーモンリロード & 再起動: #

sudo systemctl daemon-reload
sudo systemctl restart docker
docker-compose を使う場合は、一旦 docker-compose down && docker-compose up -d などでコンテナを再起動してください。

新たにコンテナが生成される際、TasksMax=infinity が反映されるようになります。

3-3. 反映確認: #

systemctl show system.slice -p TasksMax
# => TasksMax=infinity ならOK

cat /sys/fs/cgroup/system.slice/pids.max
# => max ならOK

さらにコンテナが立ち上がったら再度「STEP 1~2」の方法で /sys/fs/cgroup/system.slice/docker-.scope/pids.max を確認し、max になっているかチェックします。

STEP 4. (必要に応じて) カーネルパラメータ等のチェック #

カーネルのスレッド/PID 上限:

sysctl kernel.threads-max
sysctl kernel.pid_max

もし小さな値(3万~6万程度)であれば、さらに引き上げる必要があります。

sudo sysctl -w kernel.threads-max=200000

永続化する場合は /etc/sysctl.confkernel.threads-max=200000などを追記。

systemd 全体のデフォルト TasksMax:

cat /etc/systemd/system.conf

もし /etc/systemd/system.confDefaultTasksMax=65535などの記述があれば、全サービスに適用される場合があります。

これを infinity に変更して再起動すると、より確実です。

まとめ #

  • 問題の原因:cgroup v2 の pids.max が約 30000 に固定されており、プラグインデーモンが大量スレッドを生成するためリミットに達する。
  • ワークアラウンド:/sys/fs/cgroup/system.slice/docker-.scope/pids.max を max に変更して制限を緩和。さらに systemd slice に TasksMax=infinity を設定して再起動時も元に戻らないようにする。
  • 補足:スレッド数が異常に増える根本原因(アプリのスレッドリーク)を修正しないと、いずれメモリ不足など別の問題が起きる可能性がある。可能であればソフトウェアのアップデートやバッチ分割処理などで根治を図る。

以上のステップで「約29958の上限に達してフリーズする問題」を回避できます。

 

Updated on 2025年3月7日

What are your feelings

  • Happy
  • Normal
  • Sad