Difyをプロキシ環境（独自証明書が必要）でローカルデプロイする手順

# 概要

- [Dify](https://github.com/langgenius/dify)を、プロキシおよび独自証明書が必要な環境に対してローカルデプロイする際に必要な設定を記載します。各設定がない場合のエラー（ログ等に表示）についても可能な限り記載しています
- 記載違い等あれば遠慮なくご指摘ください

# ローカルデプロイ時のライセンスについて

:::note
Difyのローカルデプロイ利用は、[ライセンス](https://github.com/langgenius/dify?tab=License-1-ov-file#readme)を十分に確認の上ご利用ください。2025年1月28日時点では、商業利用は可能、ただし作成できるテナント（ワークスペース）は１つのみ、ロゴの変更不可などの制限（制限を外す場合は商業ライセンスが必要）などの条件があります。
:::

# 前提条件

- Ubuntu 20.04.6 LTS
- Docker以外のホスト環境はプロキシ設定済み（[参考](https://zenn.dev/wsuzume/articles/f9935b47ce0b55)）
- Dockerは既にインストール済み
- 検証Difyのバージョンは`0.15.0`

# 手順

- プロキシはサンプルとして`http://192.168.1.1:8080`を記載

## (1) Docker全体のプロキシの設定

[公式ドキュメント](https://docs.docker.jp/config/daemon/systemd.html)に従い、プロキシの設定を行います。

### 1. `docker image pull`が利用するプロキシを設定

:::note
この設定で`docker image pull`がプロキシを経由するようになる。`NO_PROXY`を記載してもよいが、`docker image pull`でしか利用されないため省略しても構わない
:::

:::note warn
未設定時はdockerイメージがプルされない
:::

```bash
# Dockerデーモンの設定を編集  
sudo systemctl edit docker
```

```:書き込む内容の例
[Service]
Environment="HTTP_PROXY=http://192.168.1.1:8080"
Environment="HTTPS_PROXY=http://192.168.1.1:8080"
```

```bash
# デーモンの設定を更新
sudo systemctl daemon-reload
# Docker デーモンを再起動
sudo systemctl restart docker    
```

### 2. ビルドおよび起動したコンテナで利用するプロキシを設定

:::note
この設定でビルドおよび起動したコンテナから外部のサービスに接続可能となる
:::

:::note warn
未設定時はDify内に外部LLMサービスを追加できない（404エラーなど）
:::


`~/.docker/config.json`にプロキシを設定する

```json:~/.docker/config.json
{
  "proxies": {
    "default": {
      "httpProxy": "http://192.168.1.1:8080",
      "httpsProxy": "http://192.168.1.1:8080"
    }
  }
}
```  

ただし[こちらにあるように](https://zenn.dev/wsuzume/articles/f9935b47ce0b55#~%2F.docker%2Fconfig.json)**ビルド時およびすべての起動したコンテナに反映される**ため確認のうえ設定すること。

少なくとも`no_proxy`は後述のように`docker-compose.yaml`へ記載しイメージごと指定すると影響が少ないと思われる。

今回は影響範囲と作業量を踏まえ、プロキシは`config.json`に記載し`no_proxy`のみ`docker-compose.yaml`への設定としたが、利用するイメージにより異なると思われる

## (2) Difyのデプロイ

:::note
この作業でDifyが起動可能となる
:::

:::note warn
`cp .env.example .env`を忘れていた場合は、`docker-weaviate-1`コンテナが起動しない
:::


[公式](https://docs.dify.ai/getting-started/install-self-hosted/docker-compose)の手順に従い設置する

```bash
git clone https://github.com/langgenius/dify.git
cd dify/docker
copy .env.example .env
docker compose up -d
```

- ログを確認したい場合は`docker compose up`で起動する
- サイズが大きくネットワークによって`git clone`に失敗する場合は、別環境（Windowsなどでも可）でクローンしVSCodeなどでコピーする方法でもよい

## (3) APIを利用したコンテナに対してSSL証明書設定

:::note
`langgenius/dify-api`を利用したコンテナ（`docker-api-1`、`docker-worker-1`）から外部にプロキシ経由でアクセス時に外部証明書を利用するための設定となる。
`venv`で利用している証明書に独自証明書を追加する必要がある。
[こちらの対処](https://github.com/langgenius/dify/discussions/4600#discussioncomment-9965360)では証明書に追記が繰り返されることへの対策となる
:::

:::note
可能であれば`ca-certificates`を利用した方法が望ましいと想定されるが、`docker-compose.yaml`の編集での対応手順が不明であったためこの対応としている。
:::

:::note warn
設定していない場合は、外部LLMサービスを追加時に`[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate`というエラーが表示、またはログに記載され追加できない
:::

1. 起動しているコンテナ`docker-api-1`から現在の証明書をホストにコピーする
    コマンド内のPythonバージョン、コピー先は環境に合わせて変更
    ```bash
    docker cp docker-api-1:/app/api/.venv/lib/python3.12/site-packages/certifi/cacert.pem /home/exsample/cacert.pem
    ```
2. コピーした証明書を編集し、独自証明書の内容を最後に追加する
3. 編集した証明書を`./volumes/app/ssl_certs/cacert.pem`として配置（必要に応じてsudo）
4. `docker compose down`でコンテナを停止
5. `docker-compose.yaml`を編集
    - `api`と`worker`に追加する（内容は同じ）
    - この設定により編集した証明書がコンテナに複写される
    - Pythonバージョンは環境に合わせて変更する
    
    ```yaml:追加 api volumes
    services:
      api:
        volumes:
          # Added line to mount the SSL certificate
          - ./volumes/app/ssl_certs/cacert.pem:/usr/local/share/ca-certificates/cacert.pem
    ```

    ```yaml:追加 api entrypoint
    services:
      api:
        entrypoint: 
          # Append certificates to certifi's CA bundle
          - /bin/bash
          - -c 
          - |
            cp /usr/local/share/ca-certificates/cacert.pem /app/api/.venv/lib/python3.12/site-packages/certifi/cacert.pem
            /entrypoint.sh
    ```
    
    ```yaml:追加 worker volumes
    services:
      worker:
        volumes:
          # Added line to mount the SSL certificate
          - ./volumes/app/ssl_certs/cacert.pem:/usr/local/share/ca-certificates/cacert.pem
    ```

    ```yaml:追加 worker entrypoint
    services:
      worker:
        entrypoint: 
          # Append certificates to certifi's CA bundle
          - /bin/bash
          - -c 
          - |
            cp /usr/local/share/ca-certificates/cacert.pem /app/api/.venv/lib/python3.12/site-packages/certifi/cacert.pem
            /entrypoint.sh
    ```
6. `docker compose up` or `docker compose up -d`でコンテナを起動


## (4) APIを利用したコンテナに対してプロキシ除外設定

:::note
`langgenius/dify-api`を利用したコンテナ（`docker-api-1`、`docker-worker-1`）からコンテナのベクターストアのアドレス`http://weaviate`にアクセスするための設定。
`docker-api-1`、`docker-worker-1`は外部LLMサービスを利用のためにプロキシ設定は必要だが、ベクターストアコンテナの利用にはプロキシ除外が必要なため。
:::

:::note
内部LLMなどの利用にも関連する設定となる。
:::

:::note warn
`weaviate`を設定していない場合は、ナレッジのインデックス作成とインデックス検索でエラー（`Meta endpoint! Unexpected status code: 503, with response body: None.`）が表示、またはログに記載され実行できない。
`docker-worker-1`で設定がない場合はインデックス作成に失敗し、`docker-api-1`で設定がない場合はインデックス検索に失敗する。
:::

:::note warn
`sandbox`を設定していない場合は、コードブロックの実行でエラー（`Run failed: Code execution service is unavailable`）が表示、またはログに記載され実行できない。
:::

1. `docker compose down`でコンテナを停止
2. `docker-compose.yaml`を編集
    - `api`と`worker`に追加する（内容は同じ）
    - この設定に（環境変数として`no_proxy`に`weaviate`を指定）よりベクターストアのコンテナ`weaviate`に`api`と`worker`からアクセス可能となる
    - 内部のLLMサービスなどにアクセスしたい場合はプライベートアドレスを適宜追加で指定する（下記の設定は`172.17.*.*`のプライベートアドレスの指定例。ワイルドカード`*`は無効な場合もある）
    
    ```yaml:追加 api environment
    services:
      api:
        environment:
          no_proxy: 'localhost,127.0.0.1,172.17.*.*,.local,weaviate,sandbox'
    ```
    
    ```yaml:追加 worker environment
    services:
      worker:
        environment:
          no_proxy: 'localhost,127.0.0.1,172.17.*.*,.local,weaviate,sandbox'
    ```

3. `docker compose up` or `docker compose up -d`でコンテナを起動

# 参考

https://zenn.dev/wsuzume/articles/f9935b47ce0b55

https://docs.docker.jp/config/daemon/systemd.html

https://docs.dify.ai/ja-jp/learn-more/faq/install-faq

https://github.com/langgenius/dify/issues/734

https://zenn.dev/mato/articles/3a29146d711d6e