Dockerコンテナで動くFlaskアプリケーションと自宅のMySQLデータベースを接続する方法

## はじめに
今回は、FlaskアプリケーションをDockerコンテナを用いて構築し、**自宅のMySQLデータベースにデータを保存して表示するWebアプリケーションを作成**します。

FlaskはPythonでのWebアプリケーション開発に適した軽量フレームワークであり、MySQLは一般的に使用されるリレーショナルデータベース管理システムです。

Dockerを使うことで、Flaskアプリケーションの環境を簡単に構築・管理できます。また、**今回は自宅のMySQLサーバーを使用することで、既存のデータベースや設定をそのまま利用可能**にします。

この記事では、プロジェクトの構成から各ファイルの作成手順、さらにアプリケーションの動作確認までの手順を詳細に説明します。

## 知識整理

:::note
今回、必要な知識は以下になります。 
**Docker**
アプリケーションとその依存関係をコンテナ化して、どの環境でも同じように動かせる仮想化技術です。開発環境と本番環境の整合性が取りやすくなります。

**Dockerfile**
Dockerイメージを作るための設定ファイルです。ベースイメージや依存パッケージのインストール、アプリの起動方法などを記述し、これをもとにイメージを作成します。

**Docker Compose**
複数のコンテナを一括で管理するツールです。今回は、自宅のMySQLに接続するため、MySQLの設定は省略し、Flaskアプリケーションのみの管理を行います。

**Flask**
Pythonで書かれたシンプルで軽量なWebフレームワークです。小規模なアプリやAPIの構築に向いており、ルーティングやテンプレートエンジンの機能を提供します。

**MySQL**
広く使用されているリレーショナルデータベース管理システム（RDBMS）です。自宅のMySQLサーバーを利用してデータの保存・検索・管理を行います。
:::

自宅環境にMySQLデータベースを構築する手順については、過去の記事で詳細に記載していますので、興味のある方は参考にしてください。

https://qiita.com/free-honda/items/fe0564241343e9e9937d

## プロジェクト構成
今回作成するFlaskアプリケーションのディレクトリ構成は以下のとおりです。

```bash
/flask-app/
├── Dockerfile         # Flask用のDockerイメージを構築する設定ファイル
├── docker-compose.yml # Flaskコンテナをまとめて管理する設定ファイル
├── requirements.txt   # FlaskなどのPythonライブラリの依存関係を記述
├── app.py             # Flaskアプリケーションのメインファイル
└── /templates/        # HTMLテンプレートを格納するディレクトリ
    └── index.html
```

## 各ファイルの作成手順
### 1. Dockerfileの作成
以下の内容でDockerfileを作成します。このファイルにより、Pythonベースのイメージを使用してFlaskアプリケーションを実行するコンテナを構築します。

```Dockerfile
# ベースイメージとして公式のPythonイメージを使用
FROM python:3.9-slim

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

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
    build-essential \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# ローカルのrequirements.txtをコンテナにコピー
COPY requirements.txt .

# Pythonの依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのソースコードをコンテナにコピー
COPY . .

# 環境変数の設定（Flaskアプリを起動するため）
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0

# コンテナのポート5000を公開
EXPOSE 5000

# Flaskアプリケーションを起動
CMD ["flask", "run"]
```

### 2. docker-compose.ymlの作成
自宅のMySQLサーバーに接続するFlaskアプリケーションを定義するdocker-compose.ymlファイルを作成します。

```docker-compose.yml
version: '3'  # Docker Composeのバージョン

services:
  web:  # Flaskアプリケーション
    build: .  # Dockerfileからビルド
    ports:
      - "5001:5000"  # ホストの5001をコンテナの5000にマッピング
    environment:
      - FLASK_ENV=development  # デバッグモードを有効化
```

:::note warn
私の環境では、Flaskを使った個人開発を行っており、すでに5000番ポートが使用中です。そのため、コンテナ内ではポート5000でFlaskを動作させつつ、ホスト側からはポート5001でアクセスできるように設定しました。
:::

### 3. requirements.txtの作成
Flaskアプリケーションの依存関係を管理するrequirements.txtを作成します。FlaskとMySQL用のライブラリをインストールするため、以下の内容を記載します。

```requirements.txt
Flask==2.3.2
mysql-connector-python==8.0.33
```

### 4. app.pyの作成
Flaskアプリケーションのメインファイルであるapp.pyを作成し、以下の内容を記述します。このファイルでは、自宅のMySQLに接続し、データを挿入および取得して表示します。

```app.py
from flask import Flask, render_template, request, redirect
import mysql.connector

app = Flask(__name__)

def get_db_connection():
    return mysql.connector.connect(
        host='YOUR_MYSQL_HOST',  # 自宅のMySQLサーバーのホスト名またはIPアドレス
        user='YOUR_USER',  # MySQLのユーザー名
        password='YOUR_PASSWORD',  # MySQLのパスワード
        database='flask_db'  # 接続するデータベース名
    )

@app.route('/')
def home():
    connection = get_db_connection()
    cursor = connection.cursor()
    cursor.execute("SELECT message FROM greetings")
    messages = cursor.fetchall()
    cursor.close()
    connection.close()
    return render_template('index.html', messages=messages)

@app.route('/add', methods=['POST'])
def add_message():
    message = request.form['message']
    connection = get_db_connection()
    cursor = connection.cursor()
    cursor.execute("INSERT INTO greetings (message) VALUES (%s)", (message,))
    connection.commit()
    cursor.close()
    connection.close()
    return redirect('/')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
```

:::note warn
YOUR_MYSQL_HOST, YOUR_USER, YOUR_PASSWORD を自宅のMySQLサーバーに合わせて設定してください。
:::

### 5. templates/index.htmlの作成
HTMLテンプレートを保存するtemplatesディレクトリを作成し、その中にindex.htmlを配置します。以下はそのサンプルです。

```templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>今日もお疲れ様！！</title>
</head>
<body>
    <h1>今日は金曜日！明日から夢の週末ですね！</h1>
    <form action="/add" method="post">
        <input type="text" name="message" placeholder="Enter your message" required>
        <button type="submit">Add Message</button>
    </form>
    <h2>Messages:</h2>
    <ul>
        {% for message in messages %}
        <li>{{ message[0] }}</li>
        {% endfor %}
    </ul>
</body>
</html>
```

### 6. MySQLテーブルの初期設定

ここでは、rootユーザーで自宅環境のMySQLにログインして、必要な設定を行います。

```bash
sudo mysql -u root -p
```

パスワードを入力すると、MySQLにログインできます。

まず、flask_dbという名前のデータベースが存在しない場合は、以下のコマンドで作成してください。

```sql
CREATE DATABASE flask_db;
```

次に、greetingsというテーブルを作成し、データを保存できるようにします。

```sql
USE flask_db;
CREATE TABLE greetings (
    id INT AUTO_INCREMENT PRIMARY KEY,
    message VARCHAR(255) NOT NULL
);
```

実際のターミナル画面は以下のようになります。

![スクリーンショット 2024-10-18 20.46.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/d98d78f8-8e09-6479-9568-0d05214f6a7f.png)

## Dockerのビルドと実行
プロジェクトの準備が整ったら、Dockerイメージをビルドし、docker-composeを使ってFlaskアプリケーションのコンテナを起動します。以下のコマンドを実行してください。

```bash
docker-compose up --build
```

ビルド中の実行画面は以下のようになります。

![スクリーンショット 2024-10-18 20.48.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/45c70a85-c4a8-7f48-1359-4d83b9f4410c.png)

私の環境では、以下のエラーが発生しました。

**エラーの例:**

```bash
web-1  | Traceback (most recent call last):
web-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 649, in connect
web-1  |     sock = socket.create_connection(
web-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
```

:::note alert
**ファイアウォールの設定を確認したところ、ポート3306（MySQLのデフォルトポート）が開放されていないことがわかりました。** MySQLに外部から接続できるようにするためには、このポートを開放する必要があります。
:::

ポート3306を開放し、設定を反映させるには、以下のコマンドを実行します。

```bash
firewall-cmd --add-port=3306/tcp --permanent
firewall-cmd --reload
firewall-cmd --list-all
```

設定が適用されたら、再度上記のコマンドを実行し、ポートが開放されていることを確認してください。portsセクションに3306/tcpが含まれていれば成功です。

## アプリケーションの動作確認
Flaskアプリケーションが起動したら、ブラウザで「http://localhost:5001」にアクセスします。

![スクリーンショット 2024-10-19 3.00.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/efaecbda-07be-bd13-b868-27516f940578.png)

テキストボックスにメッセージを入力して送信すると、メッセージが自宅のMySQLに保存され、ページに表示されます。

![スクリーンショット 2024-10-19 3.01.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/680fc9ed-e1e2-3a3c-f67f-ef799aa984e5.png)


## まとめ

今回の手順では、Dockerを用いてFlaskアプリケーションをコンテナ化し、自宅のMySQLデータベースに接続しました。

これにより、開発環境を統一しつつ、既存のデータベースを活かした開発が可能になりました。

Dockerの利点を活かし、他のサービスとの連携や拡張もスムーズに行えます。今回の検証はかなり、苦戦しました！（笑）

## 関連記事

https://qiita.com/free-honda/items/6e4653403bba582627eb

https://qiita.com/free-honda/items/6273289e813eabcbd9d9