はじめに
Difyをエンタープライズな企業環境で運用する場合、「AIモデルとの通信がインターネットを経由しないこと」がセキュリティ要件として求められるケースが多いです。
本記事では、Azure VM上にDify(OSS版)を構築し、Azure OpenAI Service(AOAI)とPrivate Linkで閉域接続する構成を解説します。これにより、ユーザーの入力データやAIの応答がパブリックインターネットを経由しない構成になります。
対象読者
- Difyをセキュアな環境で運用したいインフラエンジニア
- Azure上でのAI基盤構築を検討している方
前提(この記事で扱う範囲)
- Azure が利用できること
- VNet / NSG / Private Endpoint / Private DNS の基礎用語を知っている(詳説はしない)
- DifyはOSS版(Docker Compose)を想定
構成のゴール
- AOAIのパブリックアクセス無効
- VMからAOAIの名前解決が privatelink.openai.azure.com → 10.0.x.x になる
- 外部公開はFront Door経由のみ(VMのインバウンドはFront Doorのサービスタグで制限)
全体アーキテクチャ
graph TD
subgraph Internet["インターネット"]
User["👤 ユーザー<br/>(許可IPのみ)"]
end
subgraph Azure["Azure"]
AFD["Azure Front Door + WAF<br/>IP制限: 許可リストのみ Allow / Default: Block"]
subgraph VNet["VNet <br/>仮想ネットワーク(閉域網)"]
VM["Azure VM<br/>Dify コンテナ群"]
Disk["💾 永続データディスク<br/>(/data)"]
PE["Private Endpoint<br/>(プライベートIP 10.0.x.x)"]
end
AOAI["Azure OpenAI Service<br/>(パブリックアクセス無効)"]
end
User -->|"HTTPS"| AFD
AFD -->|"NSG: AzureFrontDoor.Backend のみ許可"| VM
VM --- Disk
VM -->|"VNet内通信<br/>(Internet不使用)"| PE
PE -->|"Azure Backbone"| AOAI
style AFD fill:#f96,stroke:#333,stroke-width:2px
style VM fill:#9cf,stroke:#333,stroke-width:2px
style AOAI fill:#9f9,stroke:#333,stroke-width:2px
style PE fill:#fcf,stroke:#333,stroke-width:1px
- ユーザー → Dify:Front Door + WAF でIP制限
- Dify → AOAI:Private Link で閉域接続(インターネット不使用)
- SSH管理:NSGでIP制限 + Entra ID認証(オプション)
構築手順の全体像
| フェーズ | 作業内容 | 所要時間(目安) |
|---|---|---|
| 1 | VNet / Subnet / NSG 作成 | 15分 |
| 2 | VM作成 + Docker + Dify デプロイ | 30分 |
| 3 | Azure OpenAI の Private Link 化 | 30分 |
| 4 | Front Door + WAF 設定 | 30分 |
| 5 | 動作確認 + 監視設定 | 15分 |
以降、各フェーズの要点とハマりポイントを解説する。
フェーズ1:ネットワーク基盤(VNet / NSG)
VNet / Subnet 作成
# 変数定義
RG_NAME="your-rg"
LOCATION="japaneast"
VNET_NAME="dify-vnet"
SUBNET_NAME="dify-subnet"
NSG_NAME="dify-nsg"
# VNet作成(単一サブネット構成)
az network vnet create \
-g $RG_NAME -n $VNET_NAME \
--address-prefix 10.0.0.0/16 \
--subnet-name $SUBNET_NAME \
--subnet-prefix 10.0.1.0/24
NSGルール設計
ここが最初の設計判断ポイント。Front Doorを前段に置く場合、VMのNSGは以下の2ルールだけでよい。
# Web (80/443): Front Doorからのみ許可
az network nsg rule create \
-g $RG_NAME --nsg-name $NSG_NAME -n AllowWebFromFrontDoor \
--priority 100 \
--source-address-prefixes "AzureFrontDoor.Backend" \
--destination-port-ranges 80 443 \
--access Allow --protocol Tcp --direction Inbound
# SSH (22): 管理者IPのみ許可
az network nsg rule create \
-g $RG_NAME --nsg-name $NSG_NAME -n AllowAdminSSH \
--priority 110 \
--source-address-prefixes "<YOUR_ADMIN_IP>/32" \
--destination-port-ranges 22 \
--access Allow --protocol Tcp --direction Inbound
サービスタグ AzureFrontDoor.Backend を使うことで、Front Doorからのトラフィックだけを許可できる。IPアドレスのハードコードが不要で、Azure側のIP変更にも自動追従する。
フェーズ2:VM + Dify デプロイ
VMスペック(推奨最小構成)
| 項目 | 値 |
|---|---|
| サイズ | Standard_B2s(2 vCPU / 4GB RAM) |
| OS | Ubuntu 22.04 LTS |
| ディスク | OSディスク 30GB + データディスク 64〜100GB |
永続データディスクの設計
Difyのデータ(DB、ナレッジベース、設定)はOSディスクとは分離して永続ディスクに配置する。これにより、VMの再作成時もデータが保全される。
# データディスクをアタッチ
az vm disk attach \
-g $RG_NAME --vm-name $VM_NAME \
--name dify-data-disk \
--new --size-gb 100
# VM内でマウント(初回のみ)
sudo mkfs.ext4 /dev/sda
sudo mkdir -p /data
sudo mount /dev/sda /data
# fstabに追記(再起動後も自動マウント)
UUID=$(sudo blkid /dev/sda -s UUID -o value)
echo "UUID=$UUID /data ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab
Difyのデプロイ
cd /data
git clone https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
docker compose up -d
Tips: Difyの永続データ(volumes/)は/data配下にシンボリックリンクを張ると、バックアップ対象が明確になる。
フェーズ3:Azure OpenAI の Private Link 化
なぜPrivate Linkが必要か
通常、DifyからAOAIへのAPI呼び出しはインターネットを経由する。Private Linkを使うと、この通信がAzureのバックボーンネットワーク内で完結し、パブリックインターネットを一切通らなくなる。
【Before】VM → インターネット → AOAI(パブリックエンドポイント)
【After】 VM → VNet内Private IP → Azure Backbone → AOAI(閉域)
手順1:AOAIリソース作成 + カスタムドメイン有効化
AOAI_NAME="your-aoai"
az cognitiveservices account create \
-g $RG_NAME -n $AOAI_NAME \
--kind OpenAI --sku s0 -l $LOCATION --yes
# カスタムドメイン有効化(Private Link使用時に必須)
az cognitiveservices account update \
-g $RG_NAME -n $AOAI_NAME \
--custom-domain $AOAI_NAME
ハマりポイント1: Private Linkを使う場合、カスタムドメインの有効化が必須。これを忘れると名前解決が正しく動かない。
手順2:Private Endpoint 作成
PE_NAME="dify-pe-aoai"
az network private-endpoint create \
-g $RG_NAME -n $PE_NAME \
--vnet-name $VNET_NAME \
--subnet $SUBNET_NAME \
--private-connection-resource-id \
$(az cognitiveservices account show -g $RG_NAME -n $AOAI_NAME --query id -o tsv) \
--group-id account \
--connection-name aoai-conn
この時点で、サブネット内にPrivate Endpoint用のNICが作成され、プライベートIPが1つ割り当てられる。
手順3:Private DNS Zone の設定
Private Endpointを作っただけでは、VMからAOAIのFQDNを引いたときにパブリックIPが返ってしまう。Private DNS Zoneを設定し、VNet内ではプライベートIPに解決されるようにする。
DNS_ZONE="privatelink.openai.azure.com"
# DNS Zone作成
az network private-dns zone create \
-g $RG_NAME -n $DNS_ZONE
# VNetとリンク
az network private-dns link vnet create \
-g $RG_NAME -n aoai-dns-link \
--zone-name $DNS_ZONE \
--virtual-network $VNET_NAME \
--registration-enabled false
# Private EndpointのDNSレコードを自動作成
az network private-endpoint dns-zone-group create \
-g $RG_NAME --endpoint-name $PE_NAME \
-n aoai-dns-group \
--private-dns-zone $DNS_ZONE \
--zone-name aoai-zone
手順4:パブリックアクセスの無効化
AOAIのネットワーク設定で「パブリックアクセスを無効」にする。これにより、Private Endpoint経由以外のアクセスが完全に遮断される。
現時点(2026年3月)ではAzure Portal上での手動設定が確実。
手順5:疎通確認
# VM内から実行
nslookup your-aoai.openai.azure.com
# 期待する結果:
# Name: your-aoai.privatelink.openai.azure.com
# Address: 10.0.1.x ← VNet内のプライベートIPが返ればOK
ハマりポイント2:nslookupの結果がパブリックIPのままの場合、Private DNS ZoneとVNetのリンクが正しく設定されていない。az network private-dns link vnet listで確認する。
フェーズ4:Front Door + WAF によるアクセス制御
なぜFront Doorを使うのか
NSGだけでもIP制限は可能だが、Front Door + WAFを使うメリットがある。
| 比較項目 | NSGのみ | Front Door + WAF |
|---|---|---|
| IP制限 | 可能 | 可能(より柔軟) |
| WAF(攻撃防御) | なし | あり |
| SSL終端 | VM側で管理 | Front Doorが管理 |
| カスタムドメイン | 自前設定 | マネージド証明書対応 |
| 将来のスケーリング | 困難 | 容易 |
WAFポリシーの設計
ポリシーモード:Prevention(遮断)
ルール1: AllowOfficeIPs(優先度100)
条件: RemoteAddr が 許可IPリスト に一致
アクション: Allow
ルール2: DenyAll(優先度1000)
条件: RemoteAddr が 0.0.0.0/0
アクション: Deny
ハマりポイント3: 接続元のIPがIPv6の場合、IPv4のみの許可リストでは弾かれる。IPv6アドレスも確認してリストに追加すること。
Difyの.env設定
Front Door経由でアクセスする場合、DifyのURLをFront Doorのエンドポイントに変更する必要がある。
# .env の該当箇所を修正
CONSOLE_API_URL=https://your-endpoint.azurefd.net
CONSOLE_WEB_URL=https://your-endpoint.azurefd.net
SERVICE_API_URL=https://your-endpoint.azurefd.net
APP_WEB_URL=https://your-endpoint.azurefd.net
# 反映
docker compose down && docker compose up -d
フェーズ5:監視設定
最低限、以下の2つのアラートを設定する。
VM CPU高騰アラート
az monitor metrics alert create \
-g "$RG_NAME" \
-n "alert-vm-cpu-high" \
--scopes "$VM_ID" \
--condition "avg Percentage CPU > 80" \
--window-size 5m \
--evaluation-frequency 1m \
--severity 2 \
--action "$ACTION_GROUP_ID"
Front Door ヘルスチェック失敗アラート
az monitor metrics alert create \
-g "$RG_NAME" \
-n "alert-afd-origin-health" \
--scopes "$AFD_ID" \
--condition "avg OriginHealthPercentage < 100" \
--window-size 1m \
--evaluation-frequency 1m \
--severity 1 \
--action "$ACTION_GROUP_ID"
Tips: Front Doorのヘルスプローブパスはデフォルトで/だが、Difyのログイン画面が200を返すだけの場合がある。可能であれば/health等の専用エンドポイントを用意するとより確実。
構築時のハマりポイントまとめ
| # | ハマりポイント | 原因 | 対処法 |
|---|---|---|---|
| 1 | Private Link接続後もパブリックIP経由になる | カスタムドメインが未設定 |
az cognitiveservices account update --custom-domain を実行
|
| 2 | nslookupでプライベートIPが返らない | Private DNS ZoneとVNetが未リンク |
private-dns link vnet create を確認
|
| 3 | Front Door経由でアクセスできない | Difyの.envがVM直接アクセスのURLのまま | .envのURL群をFront Doorエンドポイントに変更 |
| 4 | WAFで許可IPなのにブロックされる | IPv6アドレスが許可リストにない |
curl -6 ifconfig.me でIPv6を確認して追加
|
| 5 | Front Doorのオリジン登録でエラー | host-nameにIPアドレスを直接指定 | VMのDNSラベル名を使用する |
コスト目安(月額・参考値)
| リソース | SKU | 月額目安 |
|---|---|---|
| Azure VM | Standard_B2s | 約5,000円 |
| Azure OpenAI | S0(従量課金) | 利用量に依存 |
| Front Door | Standard | 約5,000円〜 |
| データディスク | 100GB Standard SSD | 約1,500円 |
| バックアップ | Recovery Services | 約500円 |
| 合計(AOAI除く) | 約12,000円〜/月 |
※ 2026年3月時点の概算。為替レートや利用量により変動。
まとめ
- Private Link により、Dify ↔ AOAI間の通信をAzureバックボーン内に閉じることができる
- Front Door + WAF で、ユーザーアクセスをIP単位で制御できる
- 構築自体はAzure CLIで2〜3時間程度で完了するが、DNS周りの設定ミスがもっとも多いハマりポイント
- 本構成はPoC向けの最小構成であり、本番運用では冗長化・バックアップ・監視の強化が必要
参考リンク