0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WSL2とRancher Desktopのdocker composeマウントでハマった話

0
Posted at

概要

WSL上のRancher DesktopでDifyをDocker Composeから起動したところ、nginxとsandboxが再起動ループに入り、Difyへアクセスできなくなった。

調査の結果、ホストに存在する設定ファイルがCompose経由ではコンテナへ正しくマウントされず、ファイルがディレクトリになったり、マウント先が空になったりしていた。

Rancher Desktop(Docker Desktopもかも)の場合、docker run -vではうまくいくが、compose経由のマウントがうまくいかないことがあるらしい。

最終的に、設定ファイルを派生DockerイメージへCOPYしてマウントではなくイメージに焼き込み、専用entrypointから読み込むことでbind mountへの依存を回避した。その時に備忘録。

環境

  • WSL2 Ubuntu
  • Rancher Desktop 1.23.1
  • Docker Compose

発生した問題

Difyを次のコマンドで起動した。

docker compose -f docker/docker-compose.yaml up -d

nginxコンテナがエラーを吐いていたため、down後、-d オプションを外して、コンテナログを確認すると、nginxとsandboxが再起動を繰り返すエラーを確認。

nginxで発生していたエラー

nginxのログには次のエラーが出ていた。

cp: -r not specified; omitting directory '/docker-entrypoint-mount.sh'
nginx exited with code 1

Composeでは、ホスト側のentrypointをコンテナへbind mountしていた。

本来、/docker-entrypoint-mount.shはファイルとしてマウントされるはずである。ところが、コンテナ内ではディレクトリとして認識されていた。

必要ファイルを誤って消してしまったかもしれないので、再cloneと、volumeの削除、コンテナの再生成を行ったが同じエラーが発生する。

最小構成でのbind mountテスト

Dify固有の問題か、Docker全体の問題かを切り分けるため、docker runで最小構成のマウントを試した。

ファイルのマウント

docker run --rm \
-v /home/dify/docker/nginx/docker-entrypoint.sh:/x \
alpine:3.20 \
ls -la /x

ディレクトリのマウント

docker run --rm \
-v /home/dify/docker/nginx/conf.d:/conf \
alpine:3.20 \
ls -la /conf

このテストでは、ファイルとディレクトリの両方が正常にマウントされた。

この結果から、次のことが分かった。

  • WSL上のbind mountが全面的に壊れているわけではない
  • ホスト側ファイルも正常に存在する
  • Difyのファイル構成にも問題はない
  • Rancher DesktopとDocker Composeを組み合わせた場合に再現する

Rancher Desktop側のマウント状態

docker inspectでマウント情報を確認すると、ホストパスが直接使われているのではなく、Rancher Desktop
が生成した中間パスが使用されていた。

/mnt/wsl/rancher-desktop/run/docker-mounts/...

概念的には次の経路になる。

WSL上のホストファイル
        ↓
Rancher Desktopの中間マウント
        ↓
Dockerコンテナ

Compose上のパスは正しいものの、この中間マウントを通過した後、コンテナ内ではファイルがディレクトリとして見えていた。

このため、Rancher DesktopとCompose間のパス変換またはマウント生成処理で問題が起きている可能性が高いと判断した.ただし、Rancher Desktop内部のどの処理が原因なのかまでは特定していない。

原因の整理

確認できた事実は次のとおり。

  1. ホスト側のファイルは存在する
  2. Composeが解決したパスも正しい
  3. コンテナを再作成しても再現する
  4. WSLとRancher Desktopを更新しても再現する
  5. 単純なdocker run -vでは正常に動く
  6. Compose経由ではファイルがディレクトリになる
  7. Compose経由ではディレクトリの内容が空になる
  8. nginxとsandboxの両方で発生する

以上から、原因はDifyの設定ファイル不足ではなく、WSL、Rancher Desktop、Docker Compose間のbind mount処理にある可能性が高い。

回避方針

bind mountが安定しないため、設定ファイルを派生DockerイメージへCOPYする方針にした。
ただし、設定ファイルを元のマウント先へ直接COPYするだけでは不十分である。

例えばsandboxの/confへ直接コピーしても、コンテナ起動時にbind mountされると、イメージ内の/confがマウントによって隠される。そこで次の構成にした。

Dockerイメージ

/opt/dify-sandbox/conf/config.yaml
                ↓ 起動時にコピー
bind mountされた /conf/config.yaml
                ↓
            /mainを起動

イメージ内の別パスへ設定を保存し、独自entrypointから実際の利用先へコピーする。

nginxの回避実装

FROM docker.io/library/nginx:1.31.1

COPY nginx/nginx.conf.template /opt/dify-nginx/nginx.conf.template
COPY nginx/proxy.conf.template /opt/dify-nginx/proxy.conf.template
COPY nginx/https.conf.template /opt/dify-nginx/https.conf.template
COPY nginx/conf.d/default.conf.template /opt/dify-nginx/conf.d/default.conf.template
COPY nginx/docker-entrypoint-embedded.sh /docker-entrypoint-embedded.sh

RUN chmod +x /docker-entrypoint-embedded.sh

テンプレートをbind mount先ではない/opt/dify-nginxへ保存する。

#!/bin/sh
set -eu

TEMPLATE_DIR="${NGINX_TEMPLATE_DIR:-/opt/dify-nginx}"
CONF_DIR="/etc/nginx/conf.d"

mkdir -p "$CONF_DIR"

env_vars=$(printenv | cut -d= -f1 | sed 's/^/$/g' | paste -sd, -)

envsubst "$env_vars" \
< "$TEMPLATE_DIR/nginx.conf.template" \
> /etc/nginx/nginx.conf

envsubst "$env_vars" \
< "$TEMPLATE_DIR/proxy.conf.template" \
> /etc/nginx/proxy.conf

envsubst "$env_vars" \
< "$TEMPLATE_DIR/conf.d/default.conf.template" \
> "$CONF_DIR/default.conf"

exec nginx -g 'daemon off;'

このentrypointは、問題のある/docker-entrypoint-mount.shを参照しない。イメージ内のテンプレートからnginx設定を生成し、そのままnginxを起動する。

通常のComposeファイルを直接変更せず、WSL用のoverrideを追加した。

services:
nginx:
    image: dify-nginx-wsl:local
    build:
    context: .
    dockerfile: nginx/Dockerfile.wsl
    entrypoint:
    - /docker-entrypoint-embedded.sh

sandbox:
    image: dify-sandbox-wsl:local
    build:
    context: .
    dockerfile: sandbox/Dockerfile.wsl
    entrypoint:
    - /entrypoint-embedded.sh

起動時は通常構成とWSL用overrideを重ねる。

docker compose \
-f docker/docker-compose.yaml \
-f docker/docker-compose.wsl.yaml \
up -d --build

後ろに指定したdocker-compose.wsl.yamlが、nginxとsandboxのイメージ、ビルド設定、entrypointだけを上書きする。

注意点

この回避策では、設定ファイルをDockerイメージへ組み込んでいる。

そのため、次の場合はイメージの再ビルドが必要になる。

  • nginxの設定テンプレートを変更した場合
  • sandboxのconfig.yamlを変更した場合
  • ベースイメージを更新した場合
docker compose \
-f docker/docker-compose.yaml \
-f docker/docker-compose.wsl.yaml \
up -d --build

また、パスワードやAPIキーなどの秘密情報をイメージへ直接COPYするのは避けるべきである。秘密情報は環境変数やDocker secretsなどで注入する方がよい。
一度イメージに秘密情報を焼き込んでしまった場合、リポイ時取りにプッシュしてしまうと、後から気づいた時に結構面倒なことになるので注意。

今回イメージへ組み込んだのは、Difyの起動に必要な静的設定ファイルである。


GitHubのissuesをGPTに調査させたところ、似たような事例が結構前からopenのまま残っている。

Rancher DesktopやDocker DesktopとWSL内Ubuntuでの開発では、特にcompose周りの挙動が怪しいことがあり、はまりポイントが多い気がする。(ネットワークとか、マウントとか)

wsl containersに期待。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?