こんにちは!内藤です!
北海道にもついに夏到来です!
湿度のない北海道の夏は非常に過ごしやすい季節ですので、旅行のご予定がある方はこの機会にせひ!
今回は軽い話題で、Docker-Composeではまった話をまとめます。
TL;DR
自前でビルドしたコンテナで、一部のディレクトリにホストOSのディレクトリをマウントする場合、同じパスに対してイメージビルド時にCOPYコマンドでファイルコピーしても、コンテナからは見えなくなる
つまり、docker-compoerでマウントしたホスト側のディレクトリが優先される
何が起きたか
開発用のモックサーバをdocker-compose を利用して作成していました。
モックサーバはdjangoで作成しており、ソースコード自体はホストと共有します。
ただし、django動作のための設定ファイルは固定名で動作させていたため、ホスト側のファイルを変更したくありませんでした。
上記を実現するため、Dockerfileは下記のような記載になっていました。
FROM python:3.8-bullseye
WORKDIR /opt/app
RUN apt-get update \
&& apt-get install -y libpq5 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt /opt/app
RUN pip install -r requirements.txt
COPY ./settings/settings.py.docker ./settings/settings.py
最下行のCOPYコマンドが、設定ファイルのひな型をコピーしている処理です。
上記のDockerfileでビルドしたイメージを起動するdocker-compose.ymlは、下記のようになっています。
version: "3"
services:
# APIサーバ
api_server:
build:
context: .
dockerfile: ./src/api_server/Dockerfile
command: >
python manage.py migrate &&
python manage.py collectstatic --noinput &&
uwsgi --ini /opt/app/uwsgi_docker.ini"
volumes:
- ./src/api_server/:/opt/app:rw # ソースコードはホストと共有
- ./docker_static/api:/var/www/apistatic:rw
networks:
api_server_network:
# APIサーバ (nginx)
api_server_nginx:
image: nginx:latest
volumes:
- ./docker_nginx_conf/api.conf:/etc/nginx/conf.d/api.conf
- ./docker_static/api:/static
ports:
- "28000:8000"
networks:
api_server_network:
depends_on:
- api_server
networks:
api_server_network:
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "false"
先に示したDockerfileは、docker-compose.ymlでは「./src/api_server/Dockerfile」にあります。
ボリュームマウント設定で、ソースコードの存在する「./src/api_server」をコンテナ内の「/opt/app」にマウントしています。
上記でdjangoのコンテナを立ち上げると、migrateを実行するところで「settings.pyがない」というエラーになります。
動作を追いかける
Dockerfileに記載されているCOPYコマンドは、イメージのビルド時に実行されます。
イメージ上のディレクトリツリーは、下記のようになります。
/opt
|- app/
| |- settings
| | |- settings.py
docker-composeにて上記のイメージが起動すると、docker-compose.ymlの記述によって、/opt/appにホストOSのディレクトリがマウントされます
/opt
|- app/
| |- 以下、ホストOS側のディレクトリの内容
マウントされることで、イメージ内にあったファイルへアクセスできなくなり、結果的に「settings.pyがない」というエラーになっていました。
最終的な解決策
原因がわかれば、解決は簡単です。
そもそも、ホスト側のsettings/ディレクトリを汚さないようにするのが目的でしたので、ホストOS側に別のディレクトリを作成し、そのディレクトリをsettings/ディレクトリにマウントします。
そのままでは別に作成したディレクトリは空ですので、
- Dokcerfile内で全然別のディレクトリに、ホストOS側のsettings/ディレクトリの内容をコピーしておく
- docker-compose.yml内で、コンテナ内にコピーしておいたsettings/ディレクトリの内容を書き戻す
- 必要な設定ファイル「settings.py」をコピーする
としてやればよさそうです。
最終的に、Dockerfileとdocker-compose.ymlは、下記のようになります。
FROM python:3.8-bullseye
WORKDIR /opt/app
RUN apt-get update \
&& apt-get install -y libpq5 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt /opt/app
RUN pip install -r requirements.txt
RUN mkdir /var/settings \
&& chmod 777 /var/settings
COPY ./settings/ /var/settings
version: "3"
services:
# APIサーバ
api_server:
build:
context: .
dockerfile: ./src/api_server/Dockerfile
command: >
bash -c "cp -r /var/settings/* /opt/app/settings/ &&
cp /opt/app/settings/settings.py.docker /opt/app/settings/settings.py &&
python manage.py migrate &&
python manage.py collectstatic --noinput &&
uwsgi --ini /opt/app/uwsgi_docker.ini"
volumes:
- ./src/api_server/:/opt/app:rw # ソースコードはホストと共有
- ./docker_settings/api_server/:/opt/app/settings:rw # 設定ディレクトリだけ別途共有
- ./docker_static/api:/var/www/apistatic:rw
networks:
api_server_network:
# APIサーバ (nginx)
api_server_nginx:
image: nginx:latest
volumes:
- ./docker_nginx_conf/api.conf:/etc/nginx/conf.d/api.conf
- ./docker_static/api:/static
ports:
- "28000:8000"
networks:
api_server_network:
depends_on:
- api_server
networks:
api_server_network:
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "false"