🐢

Dify on Azure Container Apps でプラグインが表示されない問題の解決

に公開

こんにちは、secondz digital の SRE 兼情シス kame3t です。

はじめに

Dify を Azure Container Apps で運用している際に、プラグインをインストール後、コンソール画面でプラグインリストが表示されなくなる問題に遭遇しました。本記事では、この問題の原因と解決方法について共有します。

問題発生の契機

この問題は、ローカルプラグインをインストールした直後から発生しました。

発生前の状況

  • Dify標準のプラグインは正常に表示・動作
  • プラグイン管理画面も問題なく利用可能

問題の引き金となったローカルプラグイン

  • カスタム開発したローカルプラグインをインストール
  • プラグインのメタデータやリソースを含めると、レスポンスサイズが大幅に増加
  • プラグインリストAPIのレスポンスが約147KBに達する

なぜローカルプラグインで問題が顕在化したか

  1. データサイズの増加

    • 標準プラグイン:比較的シンプルなメタデータ
    • ローカルプラグイン:カスタムリソース、詳細な設定、依存関係などを含む
  2. JSONレスポンスの肥大化

    // エラーメッセージが示すように、188KB超のJSONデータ
    Unterminated string in JSON at position 188337
    
  3. nginxのデフォルト設定での限界

    • デフォルトのプロキシバッファ(16KB)では処理不可能
    • 結果として「upstream prematurely closed connection」エラーが発生

このように、ローカルプラグインの導入がnginxの設定不足を露呈させる形となりました。標準的な使用では問題が表面化しなかったものの、カスタムプラグインを活用する際には、インフラ側の設定見直しが必要であることが分かりました。

環境

  • Dify: v1.4.1
  • インフラ: Azure Container Apps
  • IaC: Terraform
  • リバースプロキシ: nginx

問題の症状

1. ブラウザコンソールのエラー

// JSONパースエラー
Uncaught (in promise) SyntaxError: Unterminated string in JSON at position 188337

// プラグイン関連APIの401エラー
Failed to load resource: the server responded with a status of 401 ()
/console/api/workspaces/current/plugin/list:1 
/console/api/workspaces/current/plugin/tasks?page=1&page_size=100:1

2. nginx エラーログ

[error] 16#16: *70 upstream prematurely closed connection while reading upstream

原因

プラグインリストのレスポンスサイズが大きく(約147KB)、nginxのデフォルトバッファサイズでは処理しきれないことが原因でした。

解決方法

1. nginx 設定の最適化

nginx.conf.template の修正

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log /var/log/nginx/access.log main;
    
    sendfile on;
    tcp_nopush on;
    
    # クライアント設定
    client_max_body_size 100M;      # 15M → 100M
    client_body_buffer_size 10M;    # 1M → 10M
    client_body_timeout 300s;
    client_header_timeout 300s;
    client_header_buffer_size 4k;
    large_client_header_buffers 8 16k;
    
    # プロキシバッファ設定(最も重要)
    proxy_connect_timeout 300s;
    proxy_send_timeout 3600s;
    proxy_read_timeout 3600s;
    proxy_buffer_size 64k;          # 4k → 64k
    proxy_buffers 32 32k;           # 8 4k → 32 32k
    proxy_busy_buffers_size 128k;   # 8k → 128k
    proxy_temp_file_write_size 256k;
    proxy_max_temp_file_size 1024m;
    
    # その他の最適化
    send_timeout 3600s;
    keepalive_timeout 65s;
    keepalive_requests 1000;
    
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    # === ===
    
    gzip on;
    
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }
    
    include /etc/nginx/conf.d/*.conf;
}

proxy.conf の修正

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

default.conf.template の修正

server {
    listen 80;
    server_name _;

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;

    # タイムアウト設定
    proxy_connect_timeout 300s;
    proxy_send_timeout 3600s;
    proxy_read_timeout 3600s;

    # バッファサイズ設定
    client_body_buffer_size 10M;
    client_max_body_size 100M;
    
    location /console/api {
        proxy_pass http://api:5001;
        include /etc/nginx/proxy.conf;
        proxy_buffering on;  # APIレスポンス用にバッファリングを有効化
    }
    
    location ~ ^/console/api/workspaces/.*/plugin {
        proxy_pass http://api:5001;
        include /etc/nginx/proxy.conf;
        proxy_buffering off;  # WebSocket通信用に無効化
    }
    
    # 他のlocation設定...
}

2. プラグインパッケージサイズとの整合性

Dify のプラグインパッケージサイズ制限との整合性も重要です:

# terraform/shared/app-variables.tf
PLUGIN_MAX_PACKAGE_SIZE = "52428800"  # 50MB

nginx の client_max_body_size はこれより大きい値(100MB)に設定し、Base64エンコーディングなどのオーバーヘッドを考慮しています。

追加の改善: DNS設定の最適化

Container Apps のリビジョン依存を解消するため、DNS設定も改善しました:

# terraform/shared/cloudflare.tf
resource "cloudflare_dns_record" "nginx" {
  name    = "${var.public_sub_domain}.${var.public_domain}"
  proxied = false
  ttl     = 1
  type    = "CNAME"
  # latest_revision_fqdn から ingress[0].fqdn に変更
  content = azurerm_container_app.nginx.ingress[0].fqdn
  zone_id = data.cloudflare_zone.domain.zone_id
}

まとめ

Dify on Azure Container Apps でプラグインが表示されない問題は、nginx のプロキシバッファサイズが不足していることが原因でした。以下の設定変更により問題を解決できます:

  1. proxy_buffer_size を 64k に増加
  2. proxy_buffers を 32 32k に増加
  3. client_max_body_size を 100M に増加
  4. location 毎に proxy_buffering を適切に設定

これらの設定により、大きなプラグインデータでも正常に処理できるようになります。

参考

Discussion