9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flask+MySQLで作るAPIをDocker化するまで

Last updated at Posted at 2020-01-10

これは?

APIを作リたい。Flask+MySQLで作りたい。そしてDocker化したい。
この手順をメモとして残す。個人的にDocker化するところが良く分かっていないので、ここを集中的に。

やること

  1. Flask単体で簡単なAPIを作る。
  2. 1をDocker化する。
  3. MySQLを用意する。
  4. FlaskとMySQLの連携をdocker-composeで実行できるようにする。

1. Flask単体で簡単なAPIを作る。

まずなんでもいいから単純なAPIを作る。今回は、会員リスト(仮)から条件にあうデータだけをリストにして返すものを作ることに決めた。

下のファイルから、prefectureがリクエストのパラメータと一致するmail_addressのリストを返却する機能を作る。

{personal_info.csv}
mail_address,sex,age,name,prefecture
hoge1@gmail.com,male,18,itirou,tokyo
hoge2@gmail.com,male,23,zirou,osaka
hoge3@gmail.com,male,31,saburou,tokyo
hoge4@gmail.com,female,29,itiko,tokyo
hoge5@gmail.com,mail,11,shirou,osaka
hoge6@gmail.com,female,42,fumiko,tokyo

Flaskを使ったコードはこちら

超簡単で、app.pyを実行して、http://127.0.0.1:5000/に?pref=xxxをつけると、pref=xxxに一致するmail_addressがリストで返却される。

スクリーンショット 2020-01-02 20.43.38.png スクリーンショット 2020-01-02 20.43.26.png スクリーンショット 2020-01-02 20.43.51.png

次は、このAPIをDocker化する。

2. 1をDocker化する。

「Docker化する」とは、Dockerfileを作ってdocker buildしてDocker imageを作り、docker runでコンテナ内でapp.pyを実行してAPIリクエストが投げられる状態を作ることを目指す。

Dockerfileとは、作りたいコンテナに対し、"ベースとなるイメージを指定し、作成するコンテナの設定を記述して、コンテナ内でコマンドを実行する"動作を記述するもの。

2-1 ベースとなるイメージを指定

pythonでflaskを実行できれば良いので、pythonのイメージをベースにする。

2-2 コンテナの設定を記述

flaskのコードを動かすためには、以下が必要。

  • ローカルのソースコードやデータをコンテナに配置する。
  • 必要なライブラリをinstallする。

2-3 コンテナ内でコマンドを実行する。

作成したapp.pyを実行する。

これら2-1~2-3を意識して作ったDockerfileが以下。

# 2-1 ベースとなるイメージを指定
FROM python:3.6  
# 2-2 コンテナの設定を記述
ARG work_dir=/work  # Dockerfile内で扱う変数の作成 
ADD pure_flask $work_dir/pure_flask  # コードを/work/にコピー(ディレクトリをコピーする時は、右側にディレクトリ名を記述しないといけないので注意)

WORKDIR $work_dir/pure_flask  #  cd work_directoryのイメージ

RUN pip install -r requirements.txt  # requirements.txtで必要なライブラリをインストール
# 2-3 コンテナ内でコマンドを実行
CMD ["python", "/work/pure_flask/app.py"]   # CMDは基本的にDockerfile内に1つだけ。

requirements.txtは超簡単にflaskだけ記述。

{requirements.txt}
flask

この状態のDockerfileがあるディレクトリでdocker build -t flask:ver1 .を実行する。
上記コマンドの.はカレントディレクトリにあるDockerfileを使ってイメージをbuildすることを指す。

次に、docker run -it -d -p 5000:5000 flask:ver1を実行。
-dはバックグラウンド実行を、-p 5000:5000はローカルの5000番ポートと、コンテナの5000番ポートのポートフォワーディングを指定することを指してます。

この状態でローカルマシンでlocalhost:5000でブラウザで確認すると、APIのreturnが確認できる。

スクリーンショット 2020-01-03 11.01.41.png

ちなみにこの状態でdocker psdocker imagesは以下のようになる。
もしdocker psで何も表示されない場合はエラーでこけてる可能性があるため、docker ps -aで表示したコンテナIDを使って、docker logs [コンテナID]をしてあげると理由がわかるかも。

$ docker ps
> CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
> dc371c597cef        flask:ver1          "python /work/pure_f…"   9 minutes ago       Up 9 minutes        0.0.0.0:5000->5000/tcp   quizzical_margulis
$ docker images
> REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
> flask               ver1                b4cda0e56563        9 minutes ago       923MB
> python              3.6                 138869855e44        5 days ago          913MB

無事、1. Flask単体で簡単なAPIを作ると同じ動作がDocker上でも確認できた。

次は、MySQLを使うための準備をする。

3. MySQLを用意する。

MySQLもDockerで用意する。今回は、Docker-compose.yamlの利用も同時にする。

Docker-compose.yamlは、Dockerfileと似ているが、複数のコンテナの連携などを記述するためにある。
DBは接続されることが前提のため、Docker-composeで記述するのが自然だと思う。

3-1 ベースとなるイメージを指定

MySQLが使いたいため、MySQLのイメージをベースにする。

3-2 コンテナの設定を記述

MySQLの設定ファイルやデータベースの初期化をする。

3-3 コンテナ内でコマンドを実行する。

MySQLのプロセスを起動する。

Dockerfileは以下。

# 3-1 ベースとなるイメージの指定
FROM mysql:5.7

# 3-2 コンテナの設定を記述
COPY conf.d/mysql.cnf /etc/mysql/conf.d/mysql.cnf  # 文字コードの設定のセット
COPY initdb.d/init.sql /docker-entrypoint-initdb.d/init.sql  # 初期化用のSQLファイルのセット

Dockerfileの中でcopyしているconfigファイルとinit.sqlはそれぞれ以下。

{mysql.cnf}
[mysqld]
character-set-server=utf8

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8
{init.sql}
CREATE TABLE `personal_info` (
    mail_address VARCHAR(100) NOT NULL,
    sex VARCHAR(6) NOT NULL,
    age INT NOT NULL,
    name VARCHAR(50) NOT NULL,
    prefecture VARCHAR(50) NOT NULL,
    createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (mail_address)
);

INSERT INTO `personal_info` (mail_address, sex, age, name, prefecture, createdAt, updatedAt)
VALUES
('hoge1_1@gmail.com', 'male',   18, 'ichirou1' , 'tokyo', current_timestamp(), current_timestamp()),
('hoge2@gmail.com', 'male',   23, 'zirou2', 'osaka', current_timestamp(), current_timestamp()),
('hoge3_3@gmail.com', 'male',   31, 'saburou', 'tokyo', current_timestamp(), current_timestamp()),
('hoge4@gmail.com', 'female', 29, 'itiko', 'tokyo', current_timestamp(), current_timestamp()),
('hoge5_5@gmail.com', 'mail',   11, 'shirou', 'osaka', current_timestamp(), current_timestamp()),
('hoge6@gmail.com', 'female', 42, 'fumiko', 'tokyo', current_timestamp(), current_timestamp());

docker-compose.yamlは以下。

version: '3'  # docker-compose.yamlの記述のバージョン
services:  
  db:  
    build: mysql  # mysqlディレクトリ配下のDockerfileを指定
    container_name: db_server  # Dockerコンテナの名前
    ports:
      - '3306:3306'  # ポートフォワーディング指定
    environment:  # 環境変数の設定
      MYSQL_ROOT_PASSWORD: pass  # MySQLのrootユーザーのパスワード
      MYSQL_DATABASE: testdb     # MySQLのスキーマ
      TZ: Asia/Tokyo             # タイムゾーンの指定
    volumes:  # MySQLのデータの永続化をするためのボリュームマウント
      - db-data/:/var/lib/mysql
    # 3-3 コンテナ内でコマンドを実行する
    command: mysqld  # mysqldコマンドの実行
volumes:
  db-data:

上記ができたら、docker-compose up -dでdbコンテナを起動する。
その後、docker exec -it [コンテナID] /bin/bashでログインし、mysql -u root -pからパスワードを入力してテーブルを確認し、init.sqlの内容が入っていればOK。

[備考]mysql周りのtips

MySQLのデータを永続化させるためのvolumeにデータが保存されない場合、Dockerホストでdocker volume lsでvolumeを確認して削除してからdocker-compose up -dすると良さげ。

init.sqlは/docker-entrypoint-initdb.d/に固定でコピーする。

次は、flaskとMySQLを接続させる。

4. FlaskとMySQLの連携をdocker-composeで実行できるようにする。

2と4で作成した二つのDockerコンテナを結合させる。
結合させるために必要なことは、以下。

  • flaskもdocker-compose.yamlに入れる。
  • flaskからMySQLへの接続が可能なようにする(ネットワークを)。
  • flaskからMySQLへの接続が可能なようにする(コードを)。

4-1 flaskもdocker-compose.yamlに入れる

flaskのコンテナをMySQLのdocker-compose.yamlに追記したものは以下。

version: '3'
services:
  api:
    build: python
    container_name: api_server
    ports:
      - "5000:5000"
    tty: yes
    environment:
      TZ: Asia/Tokyo
      FLASK_APP: app.py
    depends_on:  # apiサーバーはdbサーバーが立ってから起動
      - db
    networks:  # apiとdbを接続するための共通ネットワーク指定
      - app_net
  db:
    build: mysql
    container_name: db_server
    ports:
      - '3306:3306'
    environment:
      MYSQL_ROOT_PASSWORD: pass
      MYSQL_DATABASE: testdb
      TZ: Asia/Tokyo
    volumes:
      - ./db-data:/var/lib/mysql
    command: mysqld
    networks:
      - app_net
volumes:
  db-data:
networks:
  app_net:
    driver: bridge

services直下の階層に、dbと同じ階層としてapiを記述。

4-2 flaskからMySQLへの接続が可能なようにする(ネットワークを)。

コンテナを二つ起動し、その間で接続をする場合、二つのコンテナを同一のネットワークで扱う必要がある。
4-1のdocker-compose.yaml内にそれぞれのserviceの中に以下を追記する。

networks:
      - app_net

これにより、各コンテナがどのネットワークを利用するかを指定できる。二つを同じネットワークにしていることを意味する。
そして、docker-compose.yamlのトップレベルの階層に以下を記述する。

networks:
  app_net:
    driver: bridge

これは、dockerのネットワークの作成を意味していて、driverをbridge指定で作る指定。
これでapiとdbのコンテナが同一ネットワーク上となるため、apiからdbへの接続が可能となる。

4-3 - flaskからMySQLへの接続が可能なようにする(コードを)。

flask側のコードとモジュールの修正。
pythonからMySQLに接続する方法は色々ありそうだが、今回はmysqlclientを使った。

pip install mysqlclientでmysqlclientをインストールし、以下のようなコードで接続して使う。

import MySQLdb

conn = MySQLdb.connect(user='root', passwd='pass', host='db_server', db='testdb')
cur = conn.cursor()
sql = "select * from personal_info;"
cur.execute(sql)
rows = cur.fetchall()

このrowsには、タプルが返却され、レコード数の長さのタプルが取得できる。

{rowsのイメージ.}
(
 (1レコード目の1カラム目の値, 1レコード目の2カラム目の値, ...), 
 (2レコード目の1カラム目の値, 2レコード目の2カラム目の値, ...),
 (3レコード目の1カラム目の値, 3レコード目の2カラム目の値, ...)
)

これをpythonで受け取り、リストに格納してjsonで返却するようなイメージ。

修正後のソースはここ
ディレクトリ構成とか、Dockerfileの位置などもこれを参照してもらえれば。

あとは、pythonで扱いたいモジュールが増えたのでrequirements.txtにmysqlclientを追記する。

これで完了。
docker-compose up -dで全て起動したら、ローカルでhttp://0.0.0.0:5000/?pref=osakaなどを入れると、結果が確認できる。

以上。

9
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?