CDK for Terraform で Dify on Azure

DifyをAzure上に構築するこちらの [「dify-azure-terraform」](https://github.com/nikawang/dify-azure-terraform) を参考に、学習がてら [CDK for Terraform](https://developer.hashicorp.com/terraform/cdktf)で書き直してみました。せっかくなので備忘録として記録を残します。そして書き直したリポジトリはこちらです。因みにデプロイしたDifyの動作確認などは細かくおこなってません🙇

https://github.com/ty-shinnosuke/cdktf-dify-on-azure


## 全体の概要
書き直しながらAzureの構成は大まかに以下のようになっていることが分かりました。

そして、ただ書き直すだけではもったいないので、一部、Azure Key Vaultでシークレットを管理するようにしました。が...PostgreSQLのパスワードだけで、他のプライマリキーなどは手を付ける前に力尽きました...

1. **Virtual Network**
    - ACA Subnet (10.0.16.0/20): Azure Container Apps 用
    - Private Link Subnet (10.0.1.0/24): Private Endpoint 用
    - PostgreSQL Subnet (10.0.2.0/24): PostgreSQL 用
2. **Azure Container Apps**
    - Nginx: リバースプロキシ
    - Web: フロントエンド
    - API: バックエンド
    - Worker: 非同期処理用ワーカー
    - Sandbox: ユーザーのコード実行環境
    - SSRF Proxy: プロキシサービス
3. **データストア**
    - PostgreSQL Flexible Server（PgVector 拡張機能付き）
    - Azure Cache for Redis
    - Storage Account
        - Blobコンテナ
        - ファイル共有 (コンテナからマウント)
4. **監視**
    - Log Analytics: コンテナアプリのログ収集と分析
5. **シークレット**
    - Azure Key Vault (PostgreSQLのパスワードだけ)  

各コンポーネント間を図にすると以下のような感じになっていると理解しました。
※PostgreSQLやRedisは省略

```mermaid
flowchart TD
subgraph Azure["Container Apps Environment"]
direction TB

        nginx["Nginx Container
        Port: 80
        External: true"]

        web["Web Frontend
        Port: 3000
        CPU: 1.0
        Memory: 2Gi"]

        api["API Server
        Port: 5001
        CPU: 2.0
        Memory: 4Gi"]

        worker["Worker
        CPU: 2.0
        Memory: 4Gi"]

        sandbox["Sandbox
        Port: 8194
        CPU: 0.5
        Memory: 1Gi"]

        ssrfproxy["SSRF Proxy (Squid)
        Ports: 3128
        CPU: 0.5
        Memory: 1Gi"]

        subgraph Storage["Azure Storage"]
            direction LR
            blob["Azure Blob Storage"]
            files["Azure File Shares"]
        end
    end

    subgraph External["Internet"]
        internet["Internet"]
    end

    %% External connections
    nginx --> web
    nginx --> api

    %% Internal connections
    web --> api
    api --> worker
    api --> sandbox
    sandbox --> ssrfproxy
    ssrfproxy --> internet

    %% Storage connections
    api --> blob
    worker --> blob
    nginx -.- files
    sandbox -.- files
    ssrfproxy -.- files

    %% Styling
    classDef container fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:black
    classDef storage fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:black
    classDef external fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:black

    class nginx,web,api,worker,sandbox,ssrfproxy container
    class blob,files storage
    class postgres,redis,internet external

```

Azure ファイル共有でUploadしたNginxやSquidのコンフィグファイルをContainer Appsからマウントしており、その中の`squid.conf`を見るとプロキシサーバーはSSRF (Server-Side Request Forgery) プロキシとして動作し、Sandboxコンテナからの外部向けリクエストを制御していることが分かりました。
また、ポート8194をリバースプロキシ[(アクセラレータモード)](https://wiki.squid-cache.org/ConfigExamples/Reverse/BasicAccelerator#squid-configuration-file) としてsandboxに転送する設定が書かれています。環境変数 `CODE_EXECUTION_ENDPOINT` が `http://sandbox:8194` となっている部分を `http://ssrf_proxy:8194` に変更しポートを開けることで、sandbox へのインバウンドも Squid 経由にする。といった事を見越しているのだろうと考えています（間違っているかもしれませんが…）。

- 以下参考
    - https://docs.dify.ai/learn-more/faq/install-faq#id-18.-why-is-ssrf_proxy-needed
    - https://github.com/langgenius/dify/blob/main/docker/docker-compose.yaml
    - https://github.com/langgenius/dify/issues/5052#issuecomment-2157110023

## さいごに

最終的には、ほぼ写経みたいな感じになってしまいましたが、何がどうなっているのかな～という感じで書き直していったのでAzureの構成やDifyに関しての解像度は少し上がった気がしてます。あと、Azure with CDKTF 楽しいです。