Flask+MySQL on docker
Webアプリケーションを作りシステム作りが初めてであってもどこからどこまで実施すれば動くシステムができるのかを開発して体験してみます。
FlaskはpythonのWebアプリ開発フレームワークです。Webサーバーを立ち上げてURLに応じた画面を表示したり、リクエストに対する処理をpythonで作り処理結果を返すことができます。
勉強会スケジュール
- 開発環境の構築(Python, docker, githubの使い方・フローの説明)
今回、勉強会で必要なツール等のインストールのサポートと簡単な開発の流れを説明します。
- PythonでRest APIサーバープログラムの作成
何らかのデータモデルを参照・作成・変更・削除できるAPIを作ります。
各参加者に異なったデータモデルを割り当てるのでDBの設計、sqlの作成、そのモデルを扱うpythonプログラムの作成、apiの入出力の設計、リクエストを受け取って返却するプログラムの作成を行います。
- Vue.jsでフロント画面の作成
Vue.jsでWebフロントのサンプルプログラムを用意します。それに各自が作ったAPIを呼び出す、Vue.jsを使ったjavascriptプログラムを作成します。
- 本番環境の構築
AWS上にサービスを構築し、公開(リリース)します。
本番に構築する前に、開発環境に構築してプログラム、構築方法が正しいか確認します。
開発環境で問題ないことが確認できたら本番に構築します。また、本番構築後に再度構築が必要になった時、サービスを止めないで構築する方法などについても話をします。
事前準備
Flask+MySQL on dockerを始める準備を参考に環境を作ってください。
本作業は、ターミナルでコマンド打って実施していきます。
もし、Windowsで実施する場合はGit Bashなどシェルが動くターミナルで実施してください。
環境変数を設定する文法が異なると正しく動かないと思われます。
Project
今回の勉強会で作っていくシステムを次のリポジトリに公開しました。
プロジェクトはpythonのプログラムである flask_sv と後述しますが、DBのdockerコンテナである docker_mysql_flask_sv があります。
途中、作業しているディレクトリなどが切り替わりますのでご注意ください。
ここでは flask_sv での作業です。
次のコマンドを入力し、リポジトリよりcloneし、そのディレクトリに移動します。
$ git clone git@github.com:kaorunix/flask_sv.git
$ cd flask_sv
sshのキーを登録していない人は次のコマンドでも良いです。
$ git clone https://github.com/kaorunix/flask_sv.git
次のコマンドでPipenvを起動します。
$ pipenv shell
次のコマンドで必要なパッケージをインストールします。
$ pipenv install
$ pipenv install --dev
プロジェクトは以下の様なディレクトリ構成となっています。
.
├── Pipfile
├── Pipfile.lock
├── Readme.md
├── backend
│ ├── src
│ │ ├── api.py
│ │ ├── main.py
│ │ ├── model
│ │ │ ├── Account.py
│ │ │ ├── Authentication.py
│ │ │ ├── Authority.py
│ │ │ ├── Status.py
│ │ │ │ ....
│ │ │ ├── __init__.py
│ │ │ └── db.py
│ │ └── restapi
│ │ ├── __init__.py
│ │ └── AccountApi.py
│ └── tests
│ ├── __init__.py
│ ├── conftest.py
│ └── model
│ ├── __init__.py
│ ├── test_Account.py
│ ├── test_Status.py
│ └── test_db.py
└── tests
└── __init__.py
backend配下にアプリケーションサーバーのソースを入れてあります。
model配下にモデルを配置しています。
MVCモデルに準じてコード設計をしましょう。Model View Controller参照。
データベースのテーブル単位であったり、取り扱うデータ(例えば複数テーブルをJOINしたもの)単位にコードを書く事にします。
src直下のmain.pyの中にコントローラを実装します。
WEBアプリケーションFWであるflaskを使ってアプリケーションサーバーを実装します。
URL毎に異なった振る舞いをmain.pyを入り口に実装します。
docker
アプリケーションを動かすにはDBが必要です。
DBをdockerで用意してありますので次の手順に従って構築してください。
マシンスペックなどでdockerを入れられなかった人はMySQLサーバーをPC上で動かしても作業できます。その場合は、dockerの代わりにMySQLサーバーを使った場合も参考にしてください。
1. dockerインストール
Windows に Docker Desktop をインストール
2. mysql dockerイメージからコンテナを作成
ここより、docker_mysql_flask_sv の話題となります。
次のリポジトリに今回の開発で必要となるdocker-composeプロジェクトを作ってあります。
次のコマンドでgit cloneします。
$ git clone git@github.com:kaorunix/docker_mysql_flask_sv.git
ディレクトリ docker_mysql_flask_sv/docker-mysql の配下に .env というファイルを作ります。
MYSQL_PASSWORD=password
MYSQL_ROOT_PASSWORD=password
passwordのところは自分の環境用のパスワードに変更しておきましょう。
このファイルをはgitに登録しないことでパスワードの漏洩を防ぐことができます。
docker-compose.yml ファイルには次の様に記載されています。
.env で設定した変数を埋め込むことができます。
# versionは3系が最新版で、versionによって書き方が異なる
version: "3"
services:
mysql:
build: ./mysql/ #Dockerfileからビルドすることを示す
image: mysql:5.7 #original_mysql_world # イメージの名前
environment:
MYSQL_DATABASE: flask_sv
MYSQL_USER: creist
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- ./mysql/db:/docker-entrypoint-initdb.d #初期データをマウントする場所
ports:
- 3306:3306
Docker-docs-ja Compose ファイル内でのサービス定義などをみて書き方を調べてみてください。
docker-compose.yml があるディレクトリで次のコマンドを打つとmysqlのコンテナが起動されます。
$ docker-compose up -d
docker psコマンドで実行中のコンテナを確認できます。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a03b458af46 mysql:5.7 "docker-entrypoint.s…" 11 days ago Up 3 seconds 3306/tcp, 33060/tcp, 0.0.0.0:3316->3316/tcp docker-mysql_mysql_1
動いているのが確認できたらmysqlコマンドでログインできることを確認しましょう。
$ mysql -h 127.0.0.1 -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.23 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
Windowsの場合、クライアントであるmysqlプログラムのパスが通っていないかも知れません。
MySQL実行ファイルは、デフォルトでは次のパスにインストールされています。
C:¥Program Files¥MySQL¥MySQL Server 5.7¥bin
このパスを環境変数の実行パス path
に追加しておいてください。
もし、うまくdockerが起動できなかったら次のコマンドでログを確認し、問題を解決します。
$ docker logs -f コンテナID
docker-compose.ymlの次の行でPCのディレクトリをDockerコンテナの中にマウントしています。
volumes:
- ./mysql/db:/docker-entrypoint-initdb.d #初期データをマウントする場所
docker_mysql_flask_sv/docker-mysql/mysql/db 配下が /docker-entrypoint-initdb.d にマウントされる事になります。
docker mysqlは /docker-entrypoint-initdb.d にsqlファイルを置いておくとコンテナを作成・起動時に実行してくれます。
こちらを参考にしました。docker mysql(Initializing a fresh instance)
コンテナが起動できないトラブルシューティング
MySQLをインストールしたときにMySQLサーバーを起動する設定にしているとコンテナが起動できません。コンテナで公開しようとしているポートが既に使われているためです。
OSの設定からMySQLサーバーを起動しない設定に変更してください。
もし、他の用途でMySQLを使っていて、共存させたい場合、次の設定変更でも対応できます。
docker-compose.ymlのポート設定を変更し、自PC上で公開するポートは3307など空いているポートを指定します。
ports:
- 3307:3306
この場合、MySQLに接続するコマンドはポート番号を指定するため次の様になります。
mysql -h 127.0.0.1 -u root -p -P 3307
他にも接続するアプリケーションのポート番号を変更する必要があります。
ちなみに、flask_svでは、
flask_sv/backend/model/db.py
の中でポート番号の設定をしています。
dockerの代わりにMySQLサーバーを使った場合
MySQLサーバーにデータベースとユーザーを作成します。
userとpasswordを自分のユーザー名に置き換えてください。ブログの中ではcreist
としています。
$ mysql -u root
mysql> create DATABASE flask_sv;
mysql> CREATE USER user IDENTIFIED BY 'password';
mysql> GRANT ALL ON flask_sv.* TO user;
mysql> FLUSH PRIVILEGES;
データベースが作成できたらテーブルを作ります。テーブル作成するSQLがdocker_mysql_flask_sv プロジェクトにあります。
次の様にmysqlを実行してSQLを実行しましょう。
$ mysql -u user flask_sv < $docker_mysql_flask_sv/docker-mysql/mysql/DB/flask_sv.sql
アプリケーションの起動
flask_sv に戻ります。
Projectを確認し、 pipenv shell
や依存パッケージをインストールした状態で始めてください。
2. mysql dockerイメージからコンテナを作成で作成したDBのパスワード情報を環境変数に設定するシェルを用意しました。
次の DOCKER_HOME
に設定するパスを docker_mysql_flask_sv のパスに書き換えてください。
DOCKER_HOME=$HOME/projects/docker_mysql_flask_sv/docker-mysql
IFS=$'\n'
for line in `cat $DOCKER_HOME/.env`
do
export $line
done
このファイルは、使っているシェルに環境変数として読み込ませるので次のコマンドを実行してください。
$ source bin/env.sh
flask_sv アプリケーションは、次のコマンドで実行します。
flask_svのディレクトリから実行してください。
$ pipenv run python backend/src/main.py
* Serving Flask app "main" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
実行されると5000ポートでwebサービスが動きます。
http://127.0.0.1:5000 にブラウザでアクセスするとアプリケーションを実行することができます。
現時点ではデータがないのでmysqlでDBに接続し、次のSQLを実行してレコードを追加してください。
$ mysql -h 127.0.0.1 -u creist -p flask_sv
mysql> INSERT INTO `account` VALUES (1,'sample_account','2022-05-23 00:00:00','2030-12-31 00:00:00',999,'2022-10-25 23:12:03',999,'2022-10-25 23:12:03',3)
APIとしてaccountの取得をするAPIを用意しました。
http://127.0.0.1:5000/account/get/<account_id>
上記URLへのアクセスに対して次のレスポンスが返ります。
{
"body": {
"name": "account",
"id": <account_id>,
"account_name": <account_name>,
"start_on": "2021-01-01 10:00:00",
"end_on": "2025-12-31 21:00:00"
},
"status": {
"code" : "I0001",
"message" : "",
"detail" : ""
}
}
また、登録用のURL
http://127.0.0.1:5000/account/create
{
"account_name":
"start_on":
"end_on":
"opration_account_id":
}
のAPIに登録すると、作成日(created_at)、作成者(created_by)、更新日(updated_at)、更新者(updated_by)が更新される。
また、アカウントが作られた時はstatusに "1":"ACTIVE"が入る事にします。
statusは今後のことも考えて使用可能不可能を切り替えられる事にします。
- "0":"NEW" 作成後有効化前の状態
- "1":"ACTIVE" 有効状態
- "2":"INACTIVE" 無効状態
- "3":"DELETE" 削除状態
こういった決め事を考えることが設計です。
APIを正常に受け付けると次のレスポンスを返します。
{
"body" : "",
"status": {
"code" : "I0001",
"message" : "account {} was created.",
"detail" : ""
}
}
APIの設計を課題とします。後術再度確認します。
DB
現在、accountテーブルを用意してあります。
mysqlではデータベースという論理データベースを選択した後、目的のテーブルなどにアクセスできます。
- show databases
- use <データベース名>
- show tables
- describe
論理データベースの一覧は show databases
、テーブルの一覧は show tables
、 テーブル構造を確認するのは describe
コマンドで確認できます。これらのコマンドはmysql独自のコマンドです。他のデータベースはまた違うコマンドが用意されています。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| flask_sv |
| information_schema |
+--------------------+
2 rows in set (0.12 sec)
mysql> use flask_sv;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+--------------------+
| Tables_in_flask_sv |
+--------------------+
| account |
| authentication |
| authority |
+--------------------+
3 rows in set (0.01 sec)
mysql>
mysqlから確認すると次の様に確認できます。
mysql> desc account;
+--------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| account_name | varchar(64) | NO | UNI | NULL | |
| start_on | datetime | NO | | NULL | |
| end_on | datetime | NO | | NULL | |
| created_by | int | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_by | int | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
| status | int | NO | | NULL | |
+--------------+-------------+------+-----+---------+----------------+
9 rows in set (0.08 sec)
mysql>
アカウントのテーブルを作るSQLは以下の様になります。
SQLはどのRDBでも基本同じです。ここで慣れておけばOracleや、PostgreSQL、SQLServerなどを使う時に役に立ちます。
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int NOT NULL AUTO_INCREMENT,
`account_name` varchar(64) NOT NULL UNIQUE,
`start_on` datetime NOT NULL,
`end_on` datetime NOT NULL,
`created_by` int DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_by` int DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`status` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;