Docker入門2）DockerによるFlaskアプリ構築

# WSL2上でDockerを用いてFlaskアプリケーションを構築する方法

この記事では、WSL2にセットアップされたUbuntu環境上でDockerを使用して、簡単なFlaskアプリケーションをビルドし、ローカルでデプロイする手順を説明します。想定読者は、以下の前提条件を満たしており、Dockerの基本的な使い方を知りたいと思っているDocker初心者を想定しています。

## 前提条件
1. WSL2にUbuntuをインストール済み
    - 参考サイト：https://note.com/hiro20180901/n/nc798a07485e2
2. DockerをWSL2のUbuntu環境にインストール済み
    - 参考サイト：https://zenn.dev/kawauso_danu/articles/fbb0fce738ef6d
3. VSCodeでWSL2に接続済み
    - 参考サイト：https://zenn.dev/s_ryuuki/articles/4b9631674adea4
      
## 1. プロジェクトのディレクトリ構造

まず、以下のようにプロジェクトのディレクトリを構成します：

```
/myflaskapp/
│
├── Dockerfile
├── app.py
└── requirements.txt
```

### ファイルの詳細

- **Dockerfile** — Dockerイメージをビルドするための設定ファイルです。
- **app.py** — Flaskアプリケーションの本体です。
- **requirements.txt** — 必要なPythonの依存パッケージをリストします。

## 2. Flaskアプリケーションの作成

以下の内容でFlaskアプリケーション（app.py）を作成します。Hello Worldと表示するだけのシンプルなアプリです。


```python 
#app.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
    #host='0.0.0.0'を指定しないとローカルホストでFlaskにアクセスできない
```

app.pyで使用するライブラリ（依存関係）を、requirements.txtに記載します。今回はバージョンを指定していません。

```txt
Flask
```

## 3. Dockerfileの作成

以下の内容で`Dockerfile`を作成します：

```dockerfile
# 1. Pythonの公式Slimイメージをベースとして使用
FROM python:3.8-slim

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

# 3. 依存関係ファイルをコピーし、インストール
COPY requirements.txt .
RUN pip install -r requirements.txt

# 4. アプリケーションのファイルをコピー
COPY . .

# 5. コンテナの5000番ポートを開放
EXPOSE 5000

# 6. コンテナ起動時にFlaskアプリケーションを実行
CMD ["python", "app.py"]
```

### 各コマンドの詳細
<details> <summary> `FROM python:3.8-slim` </summary>

- ベースイメージとして、Pythonの公式Dockerイメージ（軽量版）を使用します
- ベースイメージに対して、2行目以降で追加処理を実行するような感じです
- これを指定しないと、Dockerイメージをビルドする起点がないため、Dockerビルドに失敗します
</details>

<details> <summary> `WORKDIR /app` </summary>

- `/app`をコンテナ内の作業ディレクトリとして設定します。このディレクトリは、その後のRUN,CMD,COPYなどの命令で使用される基準パスとなります
- これによって、コピーやコマンド実行の際に都度ファイルパスを指定する手間が省けます。また、Dockerfile内の命令を読みやすくし、管理を簡単にする効果があります
- これを指定しないと、Dockerイメージが持つデフォルトの作業ディレクトリ（通常はルートディレクトリ /）が使用されます
</details>

<details> <summary> `COPY requirements.txt .` </summary>

- `requirements.txt`を、現在の作業ディレクトリ（.）にコピーします
- 最後のピリオド「 . 」は、現在の作業ディレクトリ（この場合 /app ）を意味します
- COPY A B で、AをBにコピーするコマンドです
</details>

<details> <summary> `RUN pip install -r requirements.txt` </summary>

- `requirements.txt`に記載された依存関係をインストールします
</details>


<details> <summary> `COPY . . ` </summary>

-  ホストの現在のディレクトリの内容（ソースコード）を、コンテナ内の作業ディレクトリにコピーします
-  最初の . はホストの現在のディレクトリを指し、二番目の . はコンテナ内の現在の作業ディレクトリ（この場合は /app）を指します
-  これがないと、アプリケーションのファイルをコンテナ内にコピーできないので、アプリケーションを実行できません
</details>

<details> <summary> `EXPOSE 5000` </summary>

- Flaskのデフォルトポートが5000であるため、コンテナの5000番ポートを外部に公開します
- 指定しないと、そのポートがコンテナ外からアクセス可能であることをDockerに伝える情報がなくなります。デフォルトでは、どのポートも開放されていません
</details>

<details> <summary> `CMD ["python", "app.py"]` </summary>

- コンテナが起動した際にFlaskアプリケーションを実行するためのコマンドです
- ターミナルで python app.py で起動するのと同じ感じで、第一引数には実行するプログラム(python.exe)、第二引数にはプログラムに渡す引数(pythonファイル名)を指定します
</details>


## 4. Dockerイメージのビルド

ターミナルで以下のコマンドを実行して、Dockerイメージをビルドします：

```bash
docker build -t myflaskapp .
```

### コマンド説明
- `docker build`: Dockerイメージをビルドするためのコマンドです
- `-t myflaskapp`: ビルドされるイメージに`myflaskapp`というタグを割り当てます
- `.`: Dockerfileがあるディレクトリを指します

## 5. Dockerコンテナの実行

ビルドしたイメージを実行するには、以下のコマンドをターミナルで実行します：

```bash
docker run -p 5000:5000 myflaskapp
```

### コマンド説明
- `docker run`: Docker コンテナを起動するためのコマンドです
- `-p 5000:5000`: ホストマシンの5000ポートとコンテナの5000ポートをマッピングします。これにより、ホストからコンテナのアプリケーションにアクセスできるようになります
- `myflaskapp`: 起動するDockerイメージの名前です

### Flaskアプリへのアクセス

コンテナが正常に起動した後、Webブラウザを開き、アドレスバーに `http://localhost:5000` を入力してアクセスします。これにより、Flaskアプリケーションが表示されるはずです。"Hello, World!" というメッセージがブラウザに表示されたら、アプリケーションのデプロイが成功したことになります。
![](https://storage.googleapis.com/zenn-user-upload/449fea17eb33-20240506.png)

### ポート番号を変更した場合
```bash
docker run -p 1234:5000 myflaskapp
```
上記のように、ホスト側のポート番号を1234番に指定した場合、`http://127.0.0.1:1234/`にアクセスすると、同様の画面が表示されます。
![](https://storage.googleapis.com/zenn-user-upload/9516592c3f9b-20240506.png)


## 今後の展望
- ほかの簡単なサンプルでDockerfileの記載方法やDockerコマンドなど、Dockerの理解を深める
- StreamlitアプリケーションをDockerでデプロイ可能する
- StreamlitとOpenAIとLangChainを組み合わせたChatGPTクローンアプリをDockerでデプロイ可能にする
    - OpenAIやLangChainはバージョンアップによりコード仕様が変わり互換性がなくなるケースが少なくない
    - 現状は、OpenAIのv0.x系とv1.x系でPython仮想環境を別々に構築して切り替えて使用しているが、不便に感じている
    - 今後は、Docker導入により、バージョン管理の効率化と、他者環境でのデプロイの容易化をしていきたい