1
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?

Webアプリ(React + Flask + MySQL)にDocker(Compose)を導入して詰まった所をまとめてみた

Last updated at Posted at 2024-10-28

はじめに

Webアプリ(React + Flask + MySQL)にDocker(Compose)を導入した際に色々と詰まったのでまとめてみました。なおローカルの開発環境での実行を前提としています。

フォルダ構成

.
├─ app
│  └─ backend(Flaskコンテナ)
│    └─ requirement.txt
│    └─ ...
│  └─ frontend(Reactコンテナ)
│    └─ package.json
│    └─ yarn.lock 
│    └─ ...
├─ db
│  └─ mysql(MySQLコンテナ)
│    └─ ...
└─ docker
   └─ backend
     └─ Dockerfile
   └─ frontend
     └─ Dockerfile
   └─ mysql
     └─ Dockerfile
   └─ compose.yml

アーキテクチャ

ip: 0.0.0.0がポイント

241016_docker.drawio.png

詰まった所

COPYができない

以下のように指定するとCOPYができない。

Dockerfile
WORKDIR usr/src/app/backend
COPY ../app/backend .
compose.yml
build:
    context: .
    dockerfile: ./backend/Dockerfile

解決策

以下のように指定するとCOPYができる。

compose.yml
build:
    context: ../
    dockerfile: docker/backend/Dockerfile

原因

Dockerは基本的にDockerfileがあるディレクトリ以下にあるファイルしかビルドすることができない。ここで、compose.ymlでbuild-contextを上の階層に調整し、dockerfileは「contextで指定したディレクトリ」から見たパスを指定する。
これで、ビルド対象のファイルをDockerfileがあるディレクトリ以下にあるファイルとみなすことができる。

補足

  • build-contextとはbuild時に指定するフォルダのこと
  • build-contextにあるファイルに対してCOPY等の操作を行うことができる

COPYとバインドマウントの関係について

コンテナにスクリプトやデータをコピーすると重くなるので、スクリプトやデータはホストに置き、コンテナには実行環境のみコピーして、バインドマウントによりスクリプトを実行する。
つまり、requirement.txtのみCOPYし実行環境を構築する。
なお、compose.ymlのvolumesでは相対パスで記述可能となる。

Dockerfile
WORKDIR usr/src/app/backend
COPY app/backend/requirement.txt .
RUN pip install -r requirement.txt
compose.yml
volumes:
    - ../app/backend:.

また、gitでnode_modulesをコミット対象外とする場合は、新たに環境構築する際、ホスト側にnode_modulesがない状態でコンテナを作成することになる。この時、Reactコンテナの方は「../app/frontend:.」とバインドマウントするとコンテナのnode_modulesを上書きしエラーとなる。
よって、「usr/src/app/frontend/node_modules」によりコンテナにnode_modulesを作成する必要がある。

Dockerfile
WORKDIR usr/src/app/frontend
COPY app/frontend/package.json app/frontend/yarn.lock .
RUN yarn install
compose.yml
volumes:
    - ../app/frontend:.
    - usr/src/app/frontend/node_modules

補足

  • バインドマウントとはホストのファイルをコンテナのファイルのように扱うことができること

MySQLにアクセスができない

以下のように指定したがアクセスができなかった。

config.yml
mysql:
    host=localhost
    port=3306
compose.yml
mysql:
    ports: 3306:3306

解決策

以下のように指定した所アクセスができた。

config.yml
mysql:
    host=mysql
    port=3306

原因

ここでのlocalhostはホストではなくFlaskコンテナ自身を指すためMySQLコンテナへアクセスはできない。ポートフォワードはホストからコンテナへのアクセスとなり、コンテナからコンテナへのアクセスは不可となる。
よって、コンテナ間通信はコンテナ名を使用する必要がある。

React・Flaskにローカルブラウザからアクセスができない①

以下のように指定したがアクセスができなかった。

package.json
{
  "scripts": {
    "HOSTの指定なし(デフォルトはlocalhost)"
  }
}
run.py
if __name__ == '__main__':
    # HOSTの指定なし(デフォルトはlocalhost)
    app.run(port=5000)
compose.yml
frontend:
    ports: 3000:3000

backend:
    ports: 5000:5000

解決策

以下のように指定した所アクセスができた。

package.json
{
  "scripts": {
    "start": "HOST=0.0.0.0 react-scripts start"
  }
}
run.py
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

原因

localhostは内部(自分)からのアクセスのみ可能で外部からのアクセスは不可能となる。ここで、0.0.0.0を開発サーバとして立てた場合に限り、同一ネットワーク上にある全てのデバイスからアクセス可能なサーバーになる。つまり、外部からのアクセスが可能となる。
よって、コンテナ内のReact・Flaskサーバーを0.0.0.0として立てれば、ホスト(外部)からのアクセスが可能となる。

React・Flaskにローカルブラウザからアクセスができない②

以下のように指定したがアクセスができなかった。

compose.yml
backend:
    # 特段指定なし
frontend:
    depends_on:
        backend

解決策

以下のように指定した所アクセスができた。

compose.yml
backend:
    health_check:
        test: ["CMD", "curl", "-f", "http://localhost:5000"]
        interval: 30s
        timeout: 30s
        retires: 3

frontend:
    depends_on:
        backend:
            condition: service_healthy

原因

コンテナ間の依存関係を保つために、backendのコンテナ作成後にfrontendのコンテナを作成するようdepends_onで指定していたがアクセスできないことがあった。これは、depends_onがコンテナの起動順は制御するが依存先のコンテナ(backend)で必要なサービスが起動しているかまではチェックしないためである。
そこで、health_checkを利用し、depends_onのconditionで依存先のコンテナ(backend)で必要なサービスが起動しているかまでチェックしてからfrontendのコンテナを作成する設定をすることで、コンテナ間の依存関係を保つことができる。

print文がターミナルに出力されない

以下のように指定したが出力ができなかった。

Dockerfile
CMD ["python", "app.py"]

解決策

以下のようにログにより出力ができた。

ターミナル
docker compose logs backend -f
### コンテナ内
ここにprint文が出力される
Dockerfile
CMD ["python", "-u", "app.py"]

補足

  • -uオプションはPythonをunbufferedモードで実行し、標準出力と標準エラーを即ターミナルに表示させることができる

おわりに

Dockerを理解するにはNetworkとLinuxの知識が必須のためWebアプリ作成のためにはインフラの知識が重要と改めて認識しました。

参考文献

1
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
1
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?