Docker-Flask-PostgreSQLで簡単MPAテンプレを作ってみる(インフラ編)

## TL;DR
インフラやアプリの理解を深めるために、勉強も兼ねてDocker-Flask-PostgreSQLで下記の図のようにインフラを構築してみました。
![MPA Multi Page Application Template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802241/28c7b138-ea08-28b0-8615-3ff80c7f3a07.png)
緑色に部分が実現したい場所であり、青色の部分が今回記事にしたものです。
一通り動くところまでの手順と、エラーが出たところやそこをどう改善したのかをまとめました。
以下のリポジトリにサンプルコードを公開しました。フォルダ構成や記事の中で出てこないファイルなどはこちらをご覧ください。

サンプルコード→ https://github.com/hiseumn/flask-postgresql-sample

## インフラの設定と実行
### 個々のDockerコンテナの説明

#### Nginx
以下はDocker-compose.ymlのnginxの抜粋です。
```yaml:docker-compose.yml
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./for_develop/nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - flask 
```
- `nginx`はサービス名なので、わかりやすいものであれば何でもいいです。
- `image`はDocker Hubから最新のイメージを落としてきて使用します。
詳細はここからみれます。https://hub.docker.com/
- `ports`はホストのポート80をコンテナのポート80にマッピングします。ホストのポート80へのリクエストがNginxコンテナに転送されます。ブラウザでhttp://localhost にアクセスすると、そのリクエストはNginxコンテナのポート80で処理されます。
- `volumes`はホストシステムとコンテナ間でデータを共有するために使用されます。ローカルの./for_develop/nginx/nginx.confファイルをコンテナ内の/etc/nginx/nginx.confにマウントします。マウントとは、コンピュータシステムにおいてファイルシステムやディレクトリ、デバイスなどを特定の場所に接続して、アクセス可能にする操作を指します。これにより、自分のユースケースに合わせたユーザーやアプリケーションがデータやリソースを利用できるようになります。これにより、カスタムNginx設定を使用できます。
- `depends_on`は、サービスの依存状態を設定することができます。この場合、NginxコンテナはFlaskコンテナに依存しており、NginxコンテナはFlaskコンテナが起動してから起動します。

```conf:nginx.conf
events{
    worker_connections 1024;
}
http{
    server {
        location / {
            # httpリクエストをリダイレクト
            proxy_pass http://flask:5000/;
        }
    }
}
```
nginx.confの説明は割愛しますが、ここではhttpリクエストを全て後続のFlaskにリダイレクトしています。

#### Flask
以下はDocker-compose.ymlのFlaskの抜粋です。
```yaml:docker-compose.yml
  flask:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - ./src/flask_postgresql_sample:/app
    depends_on:
      - postgresql
```
- `flask`はサービス名です
- `build`はDockerfileのディレクトリを指定します。ここでは、現在のディレクトリをビルドコンテキストとして指定します
- `ports`はホストのポート5,000をコンテナのポート5,000にマッピングします。ホストマシン上のアプリケーションはポート5,000を通じてコンテナ内のFlaskアプリケーションにアクセスできます
- `volumes`はホストマシンのディレクトリをコンテナ内のディレクトリにマウントします。ホストの`./src/flask_postgresql_sample`ディレクトリをコンテナ内の`/app`ディレクトリにマウントします。これにより、ホスト側のソースコードをコンテナ内で使用できます。開発中にホストのコードを変更すると、コンテナ内でも即座に反映されます
- `depends_on`はサービスの依存状態を設定することができます。この場合、Flaskコンテナはpostgresqlコンテナに依存しており、Flaskコンテナはpostgresqlコンテナが起動してから起動します

##### Dockerfileの作成
Flaskのサーバーは、パッケージのインストール等コマンドを実行する必要があるため、今回はDocker Hubからイメージを取得することができません。DockerイメージをビルドするためのDockerfileは下記の通りです。
```python:dockerfile
# ベースイメージを指定
FROM python:3.12-slim

# 作業ディレクトリを設定
WORKDIR /app

# 必要なパッケージをインストール
RUN pip install --upgrade pip

# 依存関係をインストール
COPY ./src/flask_postgresql_sample/app.py /app/app.py
COPY requirements.lock requirements.lock
COPY pyproject.toml pyproject.toml
COPY README.md README.md

RUN pip install -r requirements.lock

# コマンドを実行
CMD ["python", "app.py"]
```

#### PostgreSQL
以下はDocker-compose.ymlのPostgreSQLの抜粋です。
```yaml:docker-compose.yml
  postgresql:
    image: postgres:latest
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
    ports: 
      - "5432:5432"
```
- `postgresql`はサービス名です。
- `image`はDocker Hubから最新のイメージを落としてきて使用します。
- `environment`はコンテナの環境変数を設定します。これにより、コンテナ内のPostgreSQLインスタンスの初期設定を行います。
    - `POSTGRES_USER`はデータベースの管理ユーザー名を指定します。ここでは、ユーザー名がpostgresに設定されています。
    - `POSTGRES_PASSWORD`はデータベースの管理ユーザーのパスワードを指定します。ここでは、パスワードがpostgresに設定されています。
    - `POSTGRES_DB`は初期データベースの名前を指定します。ここでは、データベース名がpostgresに設定されています。
このデータベースはコンテナが初めて起動したときに自動的に作成されます。
- `ports`はホストのポート5432をコンテナのポート5432にマッピングします。これにより、ホストマシン上のアプリケーションはポート5432を通じてコンテナ内のPostgreSQLデータベースにアクセスできます。

### dockerコンテナのオーケストレーション
今までの説明をまとめると以下のようになります。
```yaml:docker-compose.yml
services:
  flask:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - ./src/flask_postgresql_sample:/app
    depends_on:
      - postgresql
  postgresql:
    image: postgres:latest
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
    ports: 
      - "5432:5432"
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./for_develop/nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - flask
```

### Dockerコンテナの起動
docker-compose.ymlがあるディレクトリに移動し、`docker-compose up`コマンドを実行します。
```zsh:terminal
docker-compose up
```

## 詰まったところ
### Flaskのサービスにアクセスできない
#### ポート番号5,000がすでに使われているため使用できないと言われた
![スクリーンショット 2024-06-24 14.39.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802241/08363bca-23f0-8b6a-3c1e-9c5f552883d6.png)
調べてみたところ、Macの`AirPlayレシーバー`がオンになっているとうまくいかないようでした。設定を開き、Airと調べると`AirDropとHandoff`のところに`AirPlayレシーバー`があります。これをオフにしたらうまくいきました。
![スクリーンショット 2024-06-24 14.45.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802241/0f49120f-bbcb-72a9-a451-c8426f6a4cc3.png)

##　参考資料

https://qiita.com/JNJDUNK/items/6e0573448ac180ee9777

以上