技術

Nginx+Flask+MySQLでREST APIを構築【3】

docker-composeで、Nginx+Flask+MySQLの構成でREST APIを実装していきます。

前回は、とりあえずサンプルAPIがresponseできるところまででした。

今回は、以下のことをやっていきます。

  • DBデータ永続化
  • マイグレーション

ディレクトリ構成(ファイル構成)

ディレクトリ構成は以下の通りです。

/
    |---src/
    |   |---controller/
    |   |       |---health.py
    |   |---app.ini
    |   |---app.py
    |   |---uwsg.log
    |   |---wsgi.py
    |   |---config.py
    |   |---db.py
    |   |---models/
    |       |---user/
    |   |---migrations/
    |---docker/
        |---api/
        |       |---Dockerfile
        |       |---requirement.txt
        |---db/
        |       |----data/
        |       |----log/
        |       |----my.cnf
        |---web/
        |       |----nginx.conf
        |---docker-compose.yml

前回からの変更点は以下になります。

  • フォルダ名をappからsrcに変更しました。
    前回の余談にも記載しましたが、モジュール名とパッケージ名が同名によるエラーが発生したためです。
    (リファクタ内容は省略します。)
  • ./src/models/user 追加
    サンプルとしてuserテーブルを作成します。
  • ./src/migirations/ 追加
    マイグレーションを実行したら自動で生成されます。
  • ./src/config.py 追加
    DBの設定値などです。
  • ./src/db.py 追加
    appと紐付けるDBインスタンスを作成します。
  • ./docker/db/data 追加
    データ永続化に使うディレクトリです。
  • ./docker/db/log 追加
    データ永続化に使うディレクトリです。

Flask拡張ライブラリを追加

DBを使用するにあたり、必要となるライブラリを追加します。

./docker/api/requirements.txt

flask==2.1.1
uWSGI==2.0.20
+ Flask-SQLAlchemy==2.5.1
+ Flask-Migrate==3.1.0
+ PyMySQL==1.0.2
+ cryptography==37.0.2

DBデータ永続化

DBを導入していくので、まずはデータ部分を永続化しておきます。

docker-compose.ymlに以下を追加します。

./docker/docker-compose.yml

  db:
    (中略)
    volumes:
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
+      - ./db/data:/var/lib/mysql
+      - ./db/log:/var/log/mysql

DBコンフィグ

DBを動かして行くので、前回、空ファイルで作成していたmy.cnfに最低限の設定だけしておきます。

文字コードとタイムゾーンの設定だけ入れておきます。

./docker/db/my.cnf

[mysqld]
character-set-server = utf8mb4
default-time-zone = 'Asia/Tokyo'


[client]
default-character-set = utf8mb4

Flaskで使用するDBを作成

config作成

DB接続のconfigを定義します。

./src/config.py

class BaseConfig(object):
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@{host}/{db_name}?charset=utf8'.format(**{
        'user': "root",
        'password': "password",
        'host': "db",
        'db_name': "api"
    })
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    DEBUG_TB_INTERCEPT_REDIRECTS = False

hostは同じdocker-compose内のDBを使うので、dbになります。

DBインスタンス作成

Flaskのappで使用するDBインスタンスを作成します。

appと同じモジュール内で記述しているサンプルが多いのですが、今回はdb.pyに分けました。db.pyは以下の様に記述します。

./src/db.py

from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()


def init_db(app):
    db.init_app(app)
    Migrate(app, db)

app.pyモジュールと分けた理由は、このあと作成するmodelファイル内で。dbインスタンスをimportするときに以下のエラーが出たからです。

ImportError: cannot import name 'app' from partially initialized module

app.pyモジュール内で、modelをimportする必要があるのですが、そのmodel内ではまだ作成されていないappモジュールを使おうとして、上記のエラーが出るみたいな感じです。

なのでmodelもapp.pyモジュール内で書いてしまえば、このエラーは避けられるのですが、modelは別モジュールにしたかったので、db.pyを作成して回避しました。

モデル作成

modelを作成します。今回はサンプルとしてidとnameがあるだけのuserテーブルを作ることにします。

./src/models/user.py

from datetime import datetime
from db import db


class User(db.Model):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

appを修正

作成したconfigやdbをFlaskのappに適用するので、app.pyを以下の様にします。

./src/app.py

from flask import Flask

from db import init_db
from models import user
from controller import health

app = Flask(__name__)

# DB
app.config.from_object('config.BaseConfig')
init_db(app)

# Controller
app.register_blueprint(health.bp)


if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0')
from models import user

はソース内では使わないのですが、migration実行時にこのimport文がないとテーブルを認識してくれないので、記述してあります。

PYTHONPATHの設定

ローカルのIDE上では問題ないのですが、後述するDBマイグレーション実行時にNot Found moduleのエラーがでました。

なので、PYTHONPATHを追加しておきます。docker-compose.ymlに以下を追加します。

./docker/docker-compose.yml

services:
  api:
    (中略)
    environment:
+      PYTHONPATH: /usr/src:$PYTHONPATH

ソースの変更は以上で完了です。

docker-compose upを実行して、コンテナが問題なく起動することを確認します。

DBマイグレーション

コンテナが起動したらDBマイグレーションを実行していきます。

最終的にはDockerコマンドなどで自動化したいですが、今回は確認も兼ねて手動で実行していきます。

まずは以下のコマンドでFlaskのコンテナに入ります。

docker exec -it sample_api /bin/bash

コンテナ内で以下のコマンドを実行します。

flask db init

実行後、./docker/db/にmigrationsフォルダが生成されます。

initが終わったら以下のコマンドを実行します。

flask db migrate

このコマンドはマイグレーションされていそうな感じがしますが、実際はされていません。migirations/versions/にrevisionファイルが作成されるだけです。

マイグレーション適用は以下のコマンドになります。

flask db upgrade

以上でマイグレーション完了です。問題なければ、DBにuserテーブルが作成されているはずです。

まとめ

以上、DBマイグレーションまででした。

これで、REST APIを作っていく土台はできたと思います。後はAPI周りを作り込んでいくだけかと思います。

土台で残っている所は、API keyやhttps化、loggerの設定、マイグレーションの自動化などでしょうか。この辺りは、APIを作り込んでいく仮定で必要になったら、随時対応していくことになるかと思います。

というわけで、FlaskのREST APIの構築の備忘録は今回で終わり予定です。

ありがとうございました。