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,確保重新啟動後不會恢復原值。

因應措施步驟(逐步指南) #

步驟 1. 從容器的「主機端 PID」識別 cgroup 路徑 #

確認容器 ID:

docker ps

外掛程式守護程序的 ID 如 998eb0c50703 等。

使用 docker top <容器ID> 取得主機上的 PID:

docker top 998eb0c50703

例如:/app/main 的處理程序為 1012512(主機 PID)等。

參考 /proc/<主機PID>/cgroup:

cat /proc/1012512/cgroup

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

步驟 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

這樣「當場」可解除限制,但重新啟動或重新建立容器後很可能會恢復原值。

步驟 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 則正常

cat /sys/fs/cgroup/system.slice/pids.max
# => 若為 max 則正常

容器啟動後,再次使用「步驟 1~2」的方法確認 /sys/fs/cgroup/system.slice/docker-.scope/pids.max 是否為 max。

步驟 4.(必要時)檢查核心參數等 #

核心的執行緒/PID 上限:

sysctl kernel.threads-max
sysctl kernel.pid_max

若為較小的值(3 萬~6 萬左右),則需進一步提高。

sudo sysctl -w kernel.threads-max=200000

若要永久化,請在 /etc/sysctl.conf 中新增 kernel.threads-max=200000 等設定。

systemd 整體的預設 TasksMax:

cat /etc/systemd/system.conf

/etc/systemd/system.conf 中有 DefaultTasksMax=65535 等記述,可能會套用至所有服務。

將其變更為 infinity 並重新啟動,可更為確實。

總結 #

  • 問題原因:cgroup v2 的 pids.max 固定為約 30000,外掛程式守護程序產生大量執行緒時達到限制。
  • 因應措施:/sys/fs/cgroup/system.slice/docker-.scope/pids.max 變更為 max 以放寬限制。同時在 systemd slice 中設定 TasksMax=infinity,確保重新啟動後不會恢復原值。
  • 補充:若不修正執行緒數異常增加的根本原因(應用程式的執行緒洩漏),最終可能會發生記憶體不足等其他問題。建議盡可能透過軟體更新或批次分割處理等方式根治。

透過以上步驟,可避免「達到約 29958 上限而凍結的問題」。

 

Updated on 2026年6月9日

What are your feelings

  • Happy
  • Normal
  • Sad