Skip to content

Amazon Linux 2023上にHTTPSで保護されたDifyを新規構築する

Amazon Linux 2023上にDockerでDifyを起動する手順をメモしておきます。Difyを構成するコンテナ群にはcert-botコンテナが含まれており、サーバ証明書を新規取得・更新する能力を持っています。今回はcert-botコンテナも併用し、サーバ証明書を取得してDifyをHTTPSでサービスします。

検証環境

対象 バージョン
Amazon Linux 2023.9.20251110
Dify 1.10.0
Docker 25.0.13, build 0bab007
Docker Compose 2.40.3

EC2仮想マシンの作成

EC2上にDifyを動作させる仮想マシンを作成します。

サイジング

Docker Compose デプロイには以下の記載があります。

Dify インストール前に, マシンが最小インストール要件を満たしていることを確認してください:

  • CPU >= 2 Core
  • RAM >= 4 GiB

これを踏まえ、今回はt4g.mediumな仮想マシンを作成しました。OSにはAmazon Linux 2023を選択しました。

インスタンスサイズ vCPU メモリ (GiB) ベースラインパフォーマンス/vCPU CPU クレジット取得/時間 ネットワークバースト幅 (Gbps) EBS バースト幅 (Mbps)
t4g.nano 2 0.5 5% 6 最大 5 最大 2,085
t4g.micro 2 1 10% 12 最大 5 最大 2,085
t4g.small 2 2 20% 24 最大 5 最大 2,085
t4g.medium 2 4 20% 24 最大 5 最大 2,085
t4g.large 2 8 30% 36 最大 5 最大 2,780
t4g.xlarge 4 16 40% 96 最大 5 最大 2,780
t4g.2xlarge 8 32 40% 192 最大 5 最大 2,780

セキュリティ要件

セキュリティグループではHTTP(TCP/80)とHTTPS(TCP/443)を許可しておきます。

プロトコル ポート 用途
TCP 80 Let's Encryptでサーバ証明書を取得する際に利用する
TCP 443 Dify自体のサービスに利用する

DockerとDocker Composeのインストール

DifyはDockerコンテナとして動作させる為、DockerとDocker Composeをインストールします。

Dockerのインストール

Dockerはdnfでインストールします。

dnf install -y docker

インストールが完了しました。インストール直後はサービスが停止していました。

# systemctl status docker.service

○ docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; preset: disabled)
     Active: inactive (dead)
TriggeredBy: ○ docker.socket
       Docs: https://docs.docker.com

サービスを有効化しておきます。

systemctl enable --now docker

Docker Composeのインストール

Docker CompsoeのGitHubリポジトリからバイナリを取得してインストールします。

mkdir -p /usr/local/lib/docker/cli-plugins/ && \
curl -SL https://github.com/docker/compose/releases/download/v2.40.3/docker-compose-linux-armv7 -o /usr/local/lib/docker/cli-plugins/docker-compose && \
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

Difyのインストール

gitのインストール

GitHubからDifyを取得する為、gitをインストールします。

dnf install -y git

Difyのインストール

GitHubからDifyをクローンします。

cd /opt && \
git clone https://github.com/langgenius/dify.git

Difyの設定ファイルはdify/docker/.envですが、このファイルは初期状態だと存在しません。サンプルファイルであるdify/docker/.env.exampleをコピーして作成します。

cd dify/docker && \
cp .env.example .env

基本的な初期設定(コンソールURL、タイムゾーン、メール送信)

docker composeで起動したDifyからSMTPでメール送信するの内容に従ってコンソールWeb URLやタイムゾーン、電子メールの送信設定を.envへ設定します。尚、詳細はDify公式の環境変数の説明ページで説明されています。

sed -i -e "s/^CONSOLE_API_URL=/CONSOLE_API_URL=https:\/\/dify.example.com/g" .env
sed -i -e "s/^CONSOLE_WEB_URL=/CONSOLE_WEB_URL=https:\/\/dify.example.com/g" .env
sed -i -e "s/^SERVICE_API_URL=/SERVICE_API_URL=https:\/\/dify.example.com/g" .env
sed -i -e "s/^APP_API_URL=/APP_API_URL=https:\/\/dify.example.com/g" .env
sed -i -e "s/^APP_WEB_URL=/APP_WEB_URL=https:\/\/dify.example.com/g" .env
sed -i -e "s/^LOG_TZ=UTC/LOG_TZ=Asia\/Tokyo/g" .env
sed -i -e "s/^MAIL_TYPE=resend/MAIL_TYPE=smtp/g" .env
sed -i -e "s/^MAIL_DEFAULT_SEND_FROM=/MAIL_DEFAULT_SEND_FROM=no-reply@example.com/g" .env
sed -i -e "s/^SMTP_SERVER=/SMTP_SERVER=email-smtp.us-east-1.amazonaws.com/g" .env
sed -i -e "s/^SMTP_PORT=465/SMTP_PORT=587/g" .env
sed -i -e "s/^SMTP_USERNAME=/SMTP_USERNAME=12345678901234567890/g" .env
sed -i -e "s/^SMTP_PASSWORD=/SMTP_PASSWORD=12345678901234567890123456789012345678901234/g" .env
sed -i -e "s/^SMTP_OPPORTUNISTIC_TLS=false/SMTP_OPPORTUNISTIC_TLS=true/g" .env

設定が完了したら以下を実行し、意図した値に設定されていることを確認します。今回、TRIGGER_URLは設定していないのですが、念の為確認しています。

grep \
  -e ^CONSOLE_API_URL= \
  -e ^CONSOLE_WEB_URL \
  -e ^SERVICE_API_URL= \
  -e ^TRIGGER_URL= \
  -e ^APP_API_URL= \
  -e ^APP_WEB_URL= \
  -e ^LOG_TZ \
  -e ^MAIL_TYPE \
  -e ^MAIL_DEFAULT_SEND_FROM \
  -e ^SMTP_SERVER= \
  -e ^SMTP_PORT \
  -e ^SMTP_USERNAME \
  -e ^SMTP_PASSWORD \
  -e ^SMTP_USE_TLS \
  -e ^SMTP_OPPORTUNISTIC_TLS \
  .env

サーバ証明書の取得

Difyでサーバ証明書を取得する方法はLaunching new servers with SSL certificatesに記載されています。まず、サーバ証明書の取得に必要なパラメータを.envへ設定します。NGINX_SSL_CERT_FILENAMENGINX_SSL_CERT_KEY_FILENAMEはデフォルトのままだと証明書の取得までは成功するのですが、最終的にDifyを起動した際、フロント側を処理するNginxの起動に失敗するようです。

sed -i -e "s/^NGINX_SSL_CERT_FILENAME=dify.crt/NGINX_SSL_CERT_FILENAME=fullchain.pem/g" .env
sed -i -e "s/^NGINX_SSL_CERT_KEY_FILENAME=dify.key/NGINX_SSL_CERT_KEY_FILENAME=privkey.pem/g" .env
sed -i -e "s/^NGINX_ENABLE_CERTBOT_CHALLENGE=false/NGINX_ENABLE_CERTBOT_CHALLENGE=true/g" .env
sed -i -e "s/^CERTBOT_DOMAIN=your_domain.com/CERTBOT_DOMAIN=dify.example.com/g" .env
sed -i -e "s/^CERTBOT_EMAIL=your_email@example.com/CERTBOT_EMAIL=dify@example.com/g" .env

設定が完了したら以下を実行し、意図した値に設定されていることを確認します。

grep \
  -e NGINX_HTTPS_ENABLED \
  -e NGINX_SSL_CERT_FILENAME \
  -e NGINX_SSL_CERT_KEY_FILENAME \
  -e NGINX_ENABLE_CERTBOT_CHALLENGE \
  -e CERTBOT_EMAIL \
  -e CERTBOT_DOMAIN \
  .env

docker network pruneで未使用のネットワークを削除したらcertbotプロファイルを指定して起動します。

docker network prune --force && \
docker compose --profile certbot up --force-recreate -d

しばらく待機してコンテナが起動したら以下を実行し、サーバ証明書を取得します。この際、TCP/80ポートが閉塞しているとサーバ証明書の取得に失敗します。

docker compose exec -it certbot /bin/sh /update-cert.sh

サーバ証明書が取得できたらDifyのフロント画面を提供しているNginxがサーバ証明書を利用するように設定します。

sed -i -e "s/^NGINX_HTTPS_ENABLED=false/NGINX_HTTPS_ENABLED=true/g" .env

設定が完了したら再度以下を実行し、意図した値に設定されていることを確認します。

grep \
  -e NGINX_HTTPS_ENABLED \
  -e NGINX_SSL_CERT_FILENAME \
  -e NGINX_SSL_CERT_KEY_FILENAME \
  -e NGINX_ENABLE_CERTBOT_CHALLENGE \
  -e CERTBOT_EMAIL \
  -e CERTBOT_DOMAIN \
  .env

これでサーバ証明書の設定は完了です。設定を変更したのでNginxだけ、再起動します。但し後述しますが、この状態ではベクトルデータベースは起動しておらず、Dify自体は利用できるものの、ナレッジベースの作成ができません。

docker compose --profile certbot up -d --no-deps --force-recreate nginx

Webブラウザでhttps://dify.example.com(環境にあわせて読み替えます)にアクセスし、Difyのセットアップ画面が表示されれば成功です。

image

サーバ証明書の更新

自動的にサーバ証明書が更新されるように設定します。Amazon Linux 2023はsystemdベースであり、且つ、デフォルトではcronがインストールされていません。その為、サーバ証明書を更新するタスクはsystemdで設定します。cert-renew.servicecert-renew.timerを以下の内容で新規作成します。

/etc/systemd/system/cert-renew.service
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[Unit]
Description=Cert Renewal

[Service]
Type=oneshot
WorkingDirectory=/opt/dify/docker/
User=root
ExecStart=docker compose exec -it certbot /bin/sh /update-cert.sh && docker compose exec nginx nginx -s reload

[Install]
WantedBy=multi-user.target
/etc/systemd/system/cert-renew.timer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Unit]
Description=Cert Renewal Timer

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

タイマーを開始すれば設定完了です。

systemctl daemon-reload && \
systemctl enable --now cert-renew.timer

ベクトルデータベースを起動する

ここまでの手順を実行するとコンテナは以下の状態になっていました。DifyはデフォルトだとベクトルデータベースとしてWeaviateを利用しますが、以下のコンテナ一覧にはWeaviateが存在しません。

# docker compose ps -a
NAME                     IMAGE                                       COMMAND                  SERVICE         CREATED          STATUS                   PORTS
docker-api-1             langgenius/dify-api:1.10.0                  "/bin/bash /entrypoi…"   api             3 minutes ago    Up 2 minutes             5001/tcp
docker-certbot-1         certbot/certbot                             "/docker-entrypoint.…"   certbot         3 minutes ago    Up 2 minutes             80/tcp, 443/tcp
docker-db-1              postgres:15-alpine                          "docker-entrypoint.s…"   db              3 minutes ago    Up 2 minutes (healthy)   5432/tcp
docker-nginx-1           nginx:latest                                "sh -c 'cp /docker-e…"   nginx           36 seconds ago   Up 24 seconds            0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp
docker-plugin_daemon-1   langgenius/dify-plugin-daemon:0.4.1-local   "/bin/bash -c /app/e…"   plugin_daemon   3 minutes ago    Up 2 minutes             0.0.0.0:5003->5003/tcp, [::]:5003->5003/tcp
docker-redis-1           redis:6-alpine                              "docker-entrypoint.s…"   redis           3 minutes ago    Up 2 minutes (healthy)   6379/tcp
docker-sandbox-1         langgenius/dify-sandbox:0.2.12              "/main"                  sandbox         3 minutes ago    Up 2 minutes (healthy)
docker-ssrf_proxy-1      ubuntu/squid:latest                         "sh -c 'cp /docker-e…"   ssrf_proxy      3 minutes ago    Up 2 minutes             3128/tcp
docker-web-1             langgenius/dify-web:1.10.0                  "/bin/sh ./entrypoin…"   web             3 minutes ago    Up 2 minutes             3000/tcp
docker-worker-1          langgenius/dify-api:1.10.0                  "/bin/bash /entrypoi…"   worker          3 minutes ago    Up 2 minutes             5001/tcp
docker-worker_beat-1     langgenius/dify-api:1.10.0                  "/bin/bash /entrypoi…"   worker_beat     3 minutes ago    Up 2 minutes             5001/tcp

ベクトルデータベースも起動したい場合は特にプロファイルを指定せず、コンテナを再起動します。

docker compose down
docker compose up -d

これでWeaviateも起動しました。

# docker compose ps -a
NAME                     IMAGE                                       COMMAND                  SERVICE         CREATED              STATUS                   PORTS
docker-api-1             langgenius/dify-api:1.10.0                  "/bin/bash /entrypoi…"   api             About a minute ago   Up About a minute        5001/tcp
docker-certbot-1         certbot/certbot                             "/docker-entrypoint.…"   certbot         7 minutes ago        Up 6 minutes             80/tcp, 443/tcp
docker-db-1              postgres:15-alpine                          "docker-entrypoint.s…"   db              7 minutes ago        Up 6 minutes (healthy)   5432/tcp
docker-nginx-1           nginx:latest                                "sh -c 'cp /docker-e…"   nginx           4 minutes ago        Up 4 minutes             0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp
docker-plugin_daemon-1   langgenius/dify-plugin-daemon:0.4.1-local   "/bin/bash -c /app/e…"   plugin_daemon   About a minute ago   Up About a minute        0.0.0.0:5003->5003/tcp, [::]:5003->5003/tcp
docker-redis-1           redis:6-alpine                              "docker-entrypoint.s…"   redis           7 minutes ago        Up 6 minutes (healthy)   6379/tcp
docker-sandbox-1         langgenius/dify-sandbox:0.2.12              "/main"                  sandbox         7 minutes ago        Up 6 minutes (healthy)
docker-ssrf_proxy-1      ubuntu/squid:latest                         "sh -c 'cp /docker-e…"   ssrf_proxy      7 minutes ago        Up 6 minutes             3128/tcp
docker-weaviate-1        semitechnologies/weaviate:1.27.0            "/bin/weaviate --hos…"   weaviate        About a minute ago   Up About a minute
docker-web-1             langgenius/dify-web:1.10.0                  "/bin/sh ./entrypoin…"   web             7 minutes ago        Up 6 minutes             3000/tcp
docker-worker-1          langgenius/dify-api:1.10.0                  "/bin/bash /entrypoi…"   worker          About a minute ago   Up About a minute        5001/tcp
docker-worker_beat-1     langgenius/dify-api:1.10.0                  "/bin/bash /entrypoi…"   worker_beat     About a minute ago   Up About a minute        5001/tcp

参考

cert-botコンテナに含まれている「サーバ証明書を取得・更新するスクリプト」(/update-cert.sh)は以下の内容でした。

/update-cert.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
set -e

DOMAIN="your_domain.com"
EMAIL="your_email@example.com"
OPTIONS=""
CERT_NAME="${DOMAIN}" # 証明書名をドメイン名と同じにする

# Check if the certificate already exists
if [ -f "/etc/letsencrypt/renewal/${CERT_NAME}.conf" ]; then
  echo "Certificate exists. Attempting to renew..."
  certbot renew --noninteractive --cert-name ${CERT_NAME} --webroot --webroot-path=/var/www/html --email ${EMAIL} --agree-tos --no-eff-email ${OPTIONS}
else
  echo "Certificate does not exist. Obtaining a new certificate..."
  certbot certonly --noninteractive --webroot --webroot-path=/var/www/html --email ${EMAIL} --agree-tos --no-eff-email -d ${DOMAIN} ${OPTIONS}
fi
echo "Certificate operation successful"
# Note: Nginx reload should be handled outside this container
echo "Please ensure to reload Nginx to apply any certificate changes."