インフォコム株式会社R&D所属の大津です。
現在、実験的にDifyをセルフホスティングで運用しています。
用途ですが、主にAIエージェントのワークフローの評価に利用しています。
あまりないとは思いますが、Macで設置する方の参考になればと思い、MacStudioに設置する際の情報を共有します。
MacStudioに設置した理由
元々ローカルAIのそこそこ大きなモデルの動作確認用途にMacStudioを用意しており、Difyの評価にちょうど良さそうだよね。ということでMacStudioに設置しました。
Difyの設置について
設置する際の注意点ですが、URLはルートパスに設定しておく必要があります。
案の上、別のURLを新たに発行する必要がありました。
環境について
- Apple M2 Ultra 24コアCPU メモリ192GB ストレージ4TB
- Dify VMの設定: 12コアCPU メモリ 16GB
何もしない状態で大体メモリを5GB程使います。
Podmanとは
Dockerと互換性のあるオープンソースのコンテナエンジンです。
開発元はRedHat社です。
Podmanを使った理由としましては、MacでDockerDesktopを利用する場合は、ライセンスの手続きが必要となりますので、手続き不要でお手軽に使う手段としてPodmanを利用しました。
Podman Machineの自動起動
Debian系でDockerを使う場合、aptで入れるだけで自動起動してくれますが、Macだと自動起動の設定から書いていく必要があります。
Macでの自動起動はめんどくさいひと手間かかります。
今回はユーザーがログインしていない状態で自動起動したいので、/Library/LaunchDaemons/に設定ファイルを設置しました。
ポイントは、Podmanは起動が少し特殊らしくAbandonProcessGroupを設定しておかないと親プロセス終了時にプロセスがキルされるので設定が必要です。
下記の設定は、ホームディレクトリの/Users/hogeを調整すれば他の環境でも使えると思います。
$ vi /Library/LaunchDaemons/launched.hoge.podman.start.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>launched.hoge.podman.start</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/env</string>
<string>-i</string>
<string>HOME=/Users/hoge</string>
<string>PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
<string>TMPDIR=/tmp</string>
<string>/opt/homebrew/bin/podman</string>
<string>machine</string>
<string>start</string>
<string>podman-machine-default</string>
</array>
<key>UserName</key><string>hoge</string>
<key>WorkingDirectory</key><string>/Users/hoge</string>
<key>RunAtLoad</key><true/>
<key>AbandonProcessGroup</key><true/>
<key>StandardOutPath</key><string>/Users/hoge/workspace/podman/log/launch.stdout</string>
<key>StandardErrorPath</key><string>/Users/hoge/workspace/podman/log/launch.stderr</string>
</dict>
</plist>
// 権限確認
$ ls -la launched.hoge.podman.start.plist
-rw-r--r-- 1 root wheel 1004 8 25 17:46 launched.hoge.podman.start.plist
// 登録
$ sudo launchctl load -w /Library/LaunchDaemons/launched.hoge.podman.start.plist
// 解除
$ sudo launchctl unload -w /Library/LaunchDaemons/launched.hoge.podman.start.plist
Podmanでのコンテナ自動起動はsystemdで管理が必要
Dockerだとプロセスが起動するとコンテナは自動起動してくれますが、Podmanはsystemdに設定を用意していく必要があります。
主に三つの方法があります。
本来推奨されている方法はQuadletを用いた設定になりますが、Difyでは複雑な要素があるためか、変換できなかったため、現在は推奨ではない、systemdからcomposeを実行する方法で運用しています。
簡単に三つの方法をまとめます。
- systemd generate
- 環境変数の変更を反映するにはワンステップ処理を噛ませる必要がある
- Quadletによる生成
- Difyは複雑な要素があるので変換できない
- systemdからcomposeの実行
- 推奨とされていないが、.env、docker-compose.yamlの適用も再起動で反映可能
作業はpodman machineに接続して行います。
PodmanにマウントしているWorkingDirectoryのパスを調整すれば他の環境でも利用が可能です。
# podman machine に接続
$ podman machine ssh
# pipからpodman-composeの追加
$ python3 -m ensurepip --default-pip --user
$ python3 -m pip install --upgrade pip --user
$ python3 -m pip install podman-compose --user
# dify.service の追加
$ mkdir ~/.config/systemd/user
$ vi ~/.config/systemd/user/dify.service
[Unit]
Description=Dify Auto Start with Podman Compose
# ネットワークとPodmanソケットの起動を待つ
After=network-online.target podman.socket
Requires=podman.socket
[Service]
# 重要: Difyのdocker-compose.yamlがある場所を指定
WorkingDirectory=/Users/hoge/src/dify/docker
# 実行コマンド
ExecStart=/home/core/.local/bin/podman-compose up
# 停止時のコマンド
ExecStop=/home/core/.local/bin/podman-compose down
# 落ちたら再起動する設定
Restart=always
RestartSec=10
[Install]
WantedBy=default.target
# Systemd設定の再読み込み
$ systemctl --user daemon-reload
# サービスの起動
$ systemctl --user enable --now dify.service
# ログの確認
$ journalctl --user -u dify.service -f
# docker-compose.yaml or .env 変更反映手順
$ systemctl --user restart dify.service
Dify環境設定
メール設定はしておくと無難
Difyを利用するにはメールサーバーが必須です。
設定しないと、手作業で招待URLを送ることになります。
また、パスワードを忘れた際の対応が管理画面からは行えませんのでメールサーバーの設定は最初に行うのがおすすめです。
- 招待実行時のメール送信
- パスワード忘れのメール送信
以降の設定は Dify 1.10.1 時の内容となります。
docker-compose.yaml の設定
一部のコンテナは動かないようにprofilesが設定してあります。
DBはpostgres、ナレッジDBにweaviateを動かしましたので下記の設定を行いました。
postgresと、weaviateの profilesを削除します。
profiles:
- postgresql
profiles:
- weaviate
また、1.10.1ではDBの種類が増えましたので下記の設定をコメントアウトにしました。
# db_mysql:
# condition: service_healthy
# required: false
# oceanbase:
# condition: service_healthy
# required: false
# seekdb:
# condition: service_healthy
# required: false
networksのデフォルトにブリッジを追加しないと動きませんでした。
networks:
# create a network between sandbox, api and ssrf_proxy, and can not access outside.
ssrf_proxy_network:
driver: bridge
internal: true
milvus:
driver: bridge
opensearch-net:
driver: bridge
internal: true
default: # ← 追加
driver: bridge # ← 追加
Proxy設定
社内での運用にはProxy設定が必要でしたのでdocker-compose.yamlに下記を設定しました。
NO_PROXYは小文字にしないと動かないケースがありましたので大文字、小文字両方を設定しました。
また、SSRF_PROXY_HTTPS_URLにHTTP_PROXYの値を設定しておかないと、プラグインをインストールする際に失敗します。
| コンテナ | HTTP_PROXY HTTPS_PROXY |
NO_PROXY no_proxy |
SSRF_PROXY_HTTP_URL SSRF_PROXY_HTTPS_URL |
|---|---|---|---|
| api | ✅ | ✅ | ✅ |
| plugin_daemon | ✅ | ✅ | |
| worker | ✅ | ||
| sandbox | ✅ | ||
| weaviate | ✅ |
.envの設定
.envは .env.exampleをコピーして用意します。
NO_PROXYにはコンテナの名前を足しておきます。
NO_PROXY="127.0.0.0/8,localhost,plugin_daemon,api,sandbox,ssrf_proxy,weaviate,db_postgres"
以下は.envの差分になります。
※ 公開しても問題がないようにexampleにしてあります。(参考まで)
-CONSOLE_API_URL=
+CONSOLE_API_URL=https://example.com
-CONSOLE_WEB_URL=
+CONSOLE_WEB_URL=https://example.com
-SERVICE_API_URL=
+SERVICE_API_URL=https://example.com
-APP_API_URL=
+APP_API_URL=https://example.com
-APP_WEB_URL=
+APP_WEB_URL=https://example.com
-FILES_URL=
+FILES_URL=https://example.com
-APP_MAX_EXECUTION_TIME=1200
+APP_MAX_EXECUTION_TIME=600000
-SERVER_WORKER_AMOUNT=1
+SERVER_WORKER_AMOUNT=8
-SERVER_WORKER_CONNECTIONS=10
+SERVER_WORKER_CONNECTIONS=150
-GUNICORN_TIMEOUT=360
+GUNICORN_TIMEOUT=720
-CELERY_WORKER_AMOUNT=
+CELERY_WORKER_AMOUNT=6
-CELERY_AUTO_SCALE=false
+CELERY_AUTO_SCALE=true
-API_TOOL_DEFAULT_CONNECT_TIMEOUT=10
-API_TOOL_DEFAULT_READ_TIMEOUT=60
+API_TOOL_DEFAULT_CONNECT_TIMEOUT=600
+API_TOOL_DEFAULT_READ_TIMEOUT=600
-SQLALCHEMY_POOL_SIZE=30
+SQLALCHEMY_POOL_SIZE=100
-POSTGRES_MAX_CONNECTIONS=100
+POSTGRES_MAX_CONNECTIONS=200
-POSTGRES_SHARED_BUFFERS=128MB
+POSTGRES_SHARED_BUFFERS=1024MB
-POSTGRES_WORK_MEM=4MB
+POSTGRES_WORK_MEM=32MB
-POSTGRES_MAINTENANCE_WORK_MEM=64MB
+POSTGRES_MAINTENANCE_WORK_MEM=512MB
-POSTGRES_EFFECTIVE_CACHE_SIZE=4096MB
+POSTGRES_EFFECTIVE_CACHE_SIZE=32768MB
-UPLOAD_FILE_SIZE_LIMIT=15
+UPLOAD_FILE_SIZE_LIMIT=500
-UPLOAD_FILE_BATCH_LIMIT=5
+UPLOAD_FILE_BATCH_LIMIT=500
-MAIL_TYPE=resend
+MAIL_TYPE=smtp
-MAIL_DEFAULT_SEND_FROM=
+MAIL_DEFAULT_SEND_FROM=hoge@example.com
-SMTP_SERVER=
-SMTP_PORT=465
+SMTP_SERVER=example.com
+SMTP_PORT=25
-SMTP_USE_TLS=true
+SMTP_USE_TLS=false
-CODE_EXECUTION_CONNECT_TIMEOUT=10
-CODE_EXECUTION_READ_TIMEOUT=60
-CODE_EXECUTION_WRITE_TIMEOUT=10
+CODE_EXECUTION_CONNECT_TIMEOUT=160
+CODE_EXECUTION_READ_TIMEOUT=160
+CODE_EXECUTION_WRITE_TIMEOUT=160
-WORKFLOW_MAX_EXECUTION_TIME=1200
+WORKFLOW_MAX_EXECUTION_TIME=600000
-HTTP_REQUEST_NODE_MAX_BINARY_SIZE=10485760
-HTTP_REQUEST_NODE_MAX_TEXT_SIZE=1048576
+HTTP_REQUEST_NODE_MAX_BINARY_SIZE=104857600
+HTTP_REQUEST_NODE_MAX_TEXT_SIZE=104857600
-TEXT_GENERATION_TIMEOUT_MS=60000
+TEXT_GENERATION_TIMEOUT_MS=300000
-SANDBOX_WORKER_TIMEOUT=15
+SANDBOX_WORKER_TIMEOUT=160
-NGINX_CLIENT_MAX_BODY_SIZE=100M
-NGINX_KEEPALIVE_TIMEOUT=65
+NGINX_CLIENT_MAX_BODY_SIZE=500M
+NGINX_KEEPALIVE_TIMEOUT=3600s
-PLUGIN_STDIO_BUFFER_SIZE=1024
+PLUGIN_STDIO_BUFFER_SIZE=65536
-PLUGIN_PYTHON_ENV_INIT_TIMEOUT=120
-PLUGIN_MAX_EXECUTION_TIMEOUT=600
+PLUGIN_PYTHON_ENV_INIT_TIMEOUT=300
+PLUGIN_MAX_EXECUTION_TIMEOUT=1200
+PLUGIN_DAEMON_TIMEOUT=1200
-TENANT_ISOLATED_TASK_CONCURRENCY=1
+TENANT_ISOLATED_TASK_CONCURRENCY=1
+HTTP_PROXY=http://example.com
+NO_PROXY="127.0.0.0/8,localhost,plugin_daemon,api,sandbox,ssrf_proxy,weaviate,db_postgres"
sandboxの設定は docker-compose.yaml とは別にある
sandboxの設定はdocker-compose.yamlとは別の場所にファイルが置いてあります。
MacStudioのリソースに余裕がありますので、少しパフォーマンスを上げておきます。
VMのCPUを12に設定していますので、max_workers: 4 -> 24としておきました。
worker_timeoutも5 -> 600 と増やします。
proxyも忘れずに設定が必要です。
$ vi volumes/sandbox/conf/config.yaml
app:
port: 8194
debug: True
key: dify-sandbox
max_workers: 24
max_requests: 50
worker_timeout: 600
python_path: /usr/local/bin/python3
enable_network: True # please make sure there is no network risk in your environment
allowed_syscalls: # please leave it empty if you have no idea how seccomp works
proxy:
socks5: 'http://example.com'
http: 'http://example.com'
https: '127.0.0.0/8,localhost,plugin_daemon,api,sandbox,ssrf_proxy,weaviate,db_postgres'
以上で設定は完了です。結構大変ですね。
終わりに
- URLはルートパスを利用する
- メールサーバーを用意する
- Proxy設定
- MacStudioでのアプリケーション(Podman)自動起動のひと手間
- Dockerと異なりPodmanはコンテナの自動起動設定が必要
- Difyの環境設定はdocker-compose.yaml, .env , sandbox用のconfigに設定が必要
以上の含蓄が得られたでしょうか?
セルフホスティングでPodmanで運用する方の参考になればと思います。