内藤 裕二/ 2023年 7月 3日/ 技術

こんにちは!内藤です!
北海道にもついに夏到来です!
湿度のない北海道の夏は非常に過ごしやすい季節ですので、旅行のご予定がある方はこの機会にせひ!

今回は軽い話題で、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/ディレクトリにマウントします。
そのままでは別に作成したディレクトリは空ですので、

  1. Dokcerfile内で全然別のディレクトリに、ホストOS側のsettings/ディレクトリの内容をコピーしておく
  2. docker-compose.yml内で、コンテナ内にコピーしておいたsettings/ディレクトリの内容を書き戻す
  3. 必要な設定ファイル「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"