【Dify】AWSのEC2にDify OSS版構築してみた
はじめに
こんにちは!
今回は「Dify」というサービスを紹介します。Dify は、簡単に LLM(大規模言語モデル)を活用したワークフローを構築できるプラットフォームで、直感的な UI で 生成AIを使用したアプリケーションを作成・運用できるサービスです。一般的には SaaS 版を利用する方が多いですが、実は OSS 版 も公開されており、自前の環境にホスティングして独自にカスタマイズしたり、プライベート環境で運用することも可能です。
OSS 版を自前でホスティングするメリットには、独自の LLM や機能をカスタマイズできること、作成したAPIを保護することができます。
そこで今回は、CloudFormation を活用して AWS の EC2 上に Dify(OSS 版)を構築 し、そのままでは招待のためのメール送信ができなかったため SMTP 設定 を適用する方法もあわせて解説します。
想定する読者
- AWS を使ってDIfyを自分の環境でホスティングしたい人
- EC2でホスティングする際にIP制限をかけたい人
今回の目的
- EC2 上に Dify(OSS 版)をデプロイする。
- パブリック IP で誰でもアクセスできないように、ALB 経由で IP 制限かける
- Route 53 と ACM を使って独自ドメインを割り当て、HTTPS 通信を実現する
全体の流れ
- CloudFormation(CF)でインフラ構築
- CFのテンプレートを使用して、VPC、サブネット、インターネットゲートウェイ、EC2インスタンスなどを一括で作成する。
- EC2にGit、Docker、Docker Composeをインストールし、Dify(OSS版)をデプロイする。
- ALB(ロードバランサ)でIP制限
- ALBを構築し、HTTP/HTTPSリスナーを設定する。
- ALBのセキュリティグループで、許可する接続元IP(CIDR)のみアクセスを許可する。
- EC2のセキュリティグループでは、ALBからのアクセスのみ許可し、直接のアクセスを遮断する。
- SMTP設定でメール送信を有効化
- /opt/dify/docker ディレクトリ内の .env に、SMTPサーバーの情報を追記する。
- docker-compose を再起動して環境変数を反映する。
- 招待メールやパスワードリセットメールが正常に送信されるか確認する。
- Route 53とACMで独自ドメイン & HTTPS設定
- AWS Certificate Managerでドメイン証明書を取得。
- ALBのHTTPSリスナーに証明書を割り当て、暗号化通信を設定する。
- Route 53でAレコードをALBに向けて設定し、独自ドメインでアクセスできるようにする。
構築
今回は、EC2 のホスティングまでは AWS のワークショップを参考に進めました。興味がある方はぜひご覧ください。 AWSワークショップ
1. CloudFormation でのリソース作成
AWS のワークショップにある yaml
ファイルをダウンロードし、セットアップを行いました。
ダウンロードはこちら
最新版のダウンロードはこちらから
AWSTemplateFormatVersion: '2010-09-09'
Description: Dify CloudFormation Template (uksb-igk3zskei3)
Parameters:
VpcCIDR:
Type: String
Default: 192.168.0.0/16
Description: CIDR block for the VPC
Subnet1CIDR:
Type: String
Default: 192.168.0.0/20
Description: CIDR block for Subnet 1
Subnet2CIDR:
Type: String
Default: 192.168.16.0/20
Description: CIDR block for Subnet 2
AllowedCIDR:
Type: String
Default: 0.0.0.0/0
Description: CIDR block to allow HTTP traffic from
AmazonLinuxAMI:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64
Resources:
DifyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: dify-vpc
Subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref DifyVPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: !Ref Subnet1CIDR
Tags:
- Key: Name
Value: dify-subnet-1
Subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref DifyVPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: !Ref Subnet2CIDR
Tags:
- Key: Name
Value: dify-subnet-2
InternetGateway:
Type: AWS::EC2::InternetGateway
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref DifyVPC
InternetGatewayId: !Ref InternetGateway
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref DifyVPC
Route:
Type: AWS::EC2::Route
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
Subnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet1
RouteTableId: !Ref RouteTable
Subnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet2
RouteTableId: !Ref RouteTable
DifySecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP traffic
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref AllowedCIDR
Description: Allow HTTP traffic from allowed CIDR
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic
VpcId: !Ref DifyVPC
Tags:
- Key: Name
Value: dify-sg
DifyWsInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AmazonBedrockFullAccess'
- 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref DifyWsInstanceRole
DifyWsInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref AmazonLinuxAMI
InstanceType: t3.medium
NetworkInterfaces:
- AssociatePublicIpAddress: 'true'
DeviceIndex: '0'
GroupSet:
- !Ref DifySecurityGroup
SubnetId: !Ref Subnet1
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 20
VolumeType: gp2
Encrypted: 'true'
Tags:
- Key: Name
Value: dify-ws
IamInstanceProfile: !Ref InstanceProfile
UserData:
Fn::Base64: |
#!/bin/bash
max_attempts=5
attempt_num=1
success=false
while [ $success = false ] && [ $attempt_num -le $max_attempts ]; do
sudo dnf install -y git docker
if [ $? -eq 0 ]; then
echo "dnf install succeeded"
success=true
else
echo "dnf install $attempt_num failed. trying again..."
sleep 3
((attempt_num++))
fi
done
sudo systemctl start docker
sudo gpasswd -a ec2-user docker
sudo gpasswd -a ssm-user docker
sudo chgrp docker /var/run/docker.sock
sudo service docker restart
sudo systemctl enable docker
sudo curl -L "https://github.com/docker/compose/releases/download/v2.28.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
cd /opt
sudo git clone https://github.com/langgenius/dify.git
cd /opt/dify
sudo git checkout v0.15.3
sudo git pull origin v0.15.3
cd /opt/dify/docker
sudo cp .env.example .env
docker-compose up -d
Outputs:
InstancePublicIP:
Description: Public IP of the EC2 instance
Value: !GetAtt DifyWsInstance.PublicIp
Export:
Name: DifyInstancePublicIP-new
InstanceId:
Description: InstanceId of the EC2 instance
Value: !Ref DifyWsInstance
Export:
Name: DifyInstanceId-new
次に AWS コンソールの検索バーに「CloudFormation」と入力して開きます。
CloudFormation の画面で「スタックの作成」を選び、新しいリソースを作成します。
その後、スタック作成画面で「テンプレートを使用する」を選択し、先ほどダウンロードした yaml
ファイルをアップロードします。
今回は、その他の設定はデフォルトのまま進めました。
リソースの作成には少し時間がかかりますが、ステータスが CREATE_COMPLETE になったら作成完了です。
実際に EC2 のページに移動し、リソースが作成されていることと、該当インスタンスのパブリック IP をブラウザで開いて Dify のページが表示されることを確認してください。
2. ALB 経由で IP 制限をかける
このままでは、パブリック IP を知っていれば誰でもアクセスできる状態です。
そこで、Dify に直接 EC2 インスタンスのパブリック IP でアクセスさせるのではなく、ALB (Application Load Balancer) を挟んで接続元の IP アドレスや CIDR ブロックに応じてアクセスを制限させたいと思います。
2.1 ALB の作成
まず、EC2 のページで左メニューから Load Balancers を選択し、ALB (Application Load Balancer) を作成します。
-
リスナーを作成
- HTTP (ポート 80) や HTTPS (ポート 443) のリスナーを作成
- 今回は両方とも作成し、HTTP にアクセスがあった場合は HTTPS にリダイレクトするように設定しました
詳しい HTTP→HTTPS のリダイレクト方法
HTTP のリスナー設定で
-
URL にリダイレクト
- URL には HTTPS (443) を指定
とすることで、HTTP にアクセスが来た場合に自動的に HTTPS に転送できます。
-
ターゲットグループを作成
- ターゲットタイプを「インスタンス」または「IP」に設定
- Dify を動かしている EC2 インスタンスを登録
-
ALB のセキュリティグループを作成
- インバウンドルールで、アクセスを許可する IP アドレスや CIDR ブロックのみに制限
- 例:
xxx.x.xxx.x/32
など
- 例:
- AllowedCIDR といった特定範囲からのアクセスのみを許可
- インバウンドルールで、アクセスを許可する IP アドレスや CIDR ブロックのみに制限
-
EC2 のセキュリティグループを変更
- EC2 側でのインバウンドルールは「ALB のセキュリティグループからの通信のみ許可」する
- これにより、直接インターネットから EC2 インスタンスへアクセスできなくなります
2.2 セキュリティグループの設定
-
ALB セキュリティグループ (ALBSecurityGroup)
- インバウンドで AllowedCIDR など特定の範囲のみ許可
- アウトバウンドで EC2 インスタンスのセキュリティグループを許可
-
EC2 インスタンス側セキュリティグループ
- インバウンドルールを「ALB のセキュリティグループからの通信のみ許可」に変更
- これで、ALB を経由しない直接アクセスが拒否されます
2.3 動作確認
- ブラウザでALB の DNS 名にアクセスする
- AllowedCIDR 外の IP からのアクセスは拒否(タイムアウトや 403 など)
- AllowedCIDR 内の IP からのアクセスで Dify のページが表示される
上記が確認できればしっかりとIP制限がかかっています!!
3. メール送信の有効化
.env
に追記
3.1 SMTP 設定を Dify から招待メールやパスワードリセットメールを送るには、SMTP サーバーの設定が必要です。
/opt/dify/docker
ディレクトリにある .env
ファイルを編集し、以下のように書き換えます(今回は Gmail を利用する場合)。
まずは.envファイルの中身を確認します。
cd /opt/dify/docker
cat .env | grep SMTP
MAIL_TYPE=smtp
MAIL_DEFAULT_SEND_FROM=yourname@example.com
SMTP_PORT=587
SMTP_SERVER=smtp.gmail.com
SMTP_USERNAME=yourname@example.com
SMTP_PASSWORD=xxxxxxxxxxxxxxxx
SMTP_USE_TLS=true
SMTP_OPPORTUNISTIC_TLS=true
独自ドメインのメールサーバーをお持ちの場合は、その情報を入力してください。
GoogleMailを使用する方法
- Googleのアカウント設定から、アプリパスワードを作成する
- アプリパスワードの作成にアクセス
- アプリパスワード作成後、16桁のSMTP_PASSをセットする
3.2 コンテナの再起動
.env を変更したら、コンテナを再起動して設定を反映します。
cd /opt/dify/docker
docker-compose down
docker-compose up -d --force-recreate
4. 動作確認
設定が完了したら、Dify の管理画面またはコンソールからユーザー招待メールを送信してみて、正常にメールが届くかを確認します。
もしメールが届かない場合、以下をチェックしてみてください。
- SPF, DKIM, DMARC 設定
- Gmail や独自ドメインで認証が必要なケースがあります
- 迷惑メールフォルダ
- テストとして自分宛に送信し、迷惑メールに分類されていないか確認してみましょう
5. ドメイン設定 (Route 53 + ACM の例)
ここまでで、ALB を経由して IP 制限を行い、EC2 で Dify を動かす環境は整いましたが、実際には独自ドメインを利用してアクセスするケースが多いと思われます。そこで、AWS の Route 53 と AWS Certificate Manager (ACM) を使用して独自ドメインを割り当て、HTTPS 化する手順を簡単に紹介します。
5.1 ACM で SSL/TLS 証明書を取得
- AWS コンソールから「Certificate Manager」を開く
- 「証明書のリクエスト」から新しい証明書をリクエストする
- Route 53 を利用している場合、DNS レコードを自動で作成できますので今回はこちらの機能を使用いたしました!
5.2 ALB に証明書を割り当て
- ALB のリスナー設定(HTTPS:443 のリスナー)を開く
- 「リスナーの編集」で、証明書の設定画面があります
- 先ほどリクエストした証明書(ACM の ARN)を選択し、HTTPS 通信を暗号化します
5.3 Route 53 でドメインを ALB に向ける
- Route 53 のホストゾーンで、自分のドメインを選択
- 「レコードを作成」から A レコード(ALIAS)を ALB の DNS 名へ向ける
例:example.com → ALB の DNS 名 (xxxx.elb.amazonaws.com) - www.example.com 用にも同様に A レコードを作成し、ALB に向ける
- 数分~数十分待つと、https://example.com でアクセスできるようになります
最後に
今回行った
- CloudFormation (CF) を利用し、VPC、EC2、Docker、Dify の構築をまとめて自動化
- 独自ドメインや HTTPS 化が必要な場合は、Route 53 と ACM を利用して設定を行う
これらの構築をすることでよりセキュリティ性の高い状態でDifyを使用することができます!!
Dify はOpenAI API などのLLMのAPI をすぐに使える管理画面を備えているためLLM アプリケーションをスピーディに構築したい方に特におすすめです!!ぜひご活用ください!!
Discussion