当サイトは、アフィリエイト広告を利用しています
Dockerコンテナを使ってPythonのFlaskを使った開発ができる環境を
作成し、その中でFlaskの超簡単APIを作って外部サーバーであるGunicornで動作させて
動きを確認する
この記事を読めばflaskのプログラムを作って、Gunicorn上で
動かすまでの流れは理解できると思う。
GunicornでFlaskのWEBアプリケーションを動かす場合下記の
二つの方法がある
今回はよりシンプルな「Gunicornにアプリケーションインスタンスを直接渡す方法」を使う
「Gunicornのカスタムアプリケーションとして起動する方法」については下記でまとめています。
またflaskを使ったREST APIの実装については下記記事で紹介してます
コンテナで動作させるflaskプロジェクトの永続化方法について
永続化についてはバインドマウントを使用する
プロジェクトが大きくなってきた場合などは、バインドマウントではなく
名前付きボリュームをつかったほうがいいかもだが
とりあえず動きを確認するだけなので、バインドマウントで作成する。
Dockerのボリューム関係については下記記事でまとめているので
気になる方はご参照ください
またFlaskには開発用に内部サーバーがついているが
今回は外部サーバーであるGunicornを使って動かすようにする。
flaskには簡易的な内部サーバーが付属しているため
gunicornなしでも動かすことができる。
開発段階では特に問題はないが、逆に開発段階から
gunicornを使うことで下記のようなメリットがある
Gunicornは複数のワーカーを使用してリクエストを並行して処理することができる。
そのためFlaskの内部サーバーよりも高いパフォーマンスを出せる
ぶっちゃけ早い。
Gunicornは本番環境での使用に耐えうる設計がされており
長時間の稼働においても安定している
Gunicornは多くの設定オプションがあり、アプリケーションのニーズに
合わせて細かく調整することが可能。
正直、簡単なAPIを作って動きを確認したいだけだが、
実際にサーバーにデプロイするとなると、内部サーバーではなく
外部サーバー(gunicorn等)を使う必要があるので
早い内から触っておこうという考え。
開発はVScodeの拡張機能である「Remote Development」を
使ってVScodeからコンテナにアタッチして行う。
Remote Developmentを使ってコンテナにアタッチして
開発する方法については下記記事参照
コンテナはDockerComposeコマンドで作成して
作成済みのコンテナにVScodeからアタッチする方法を使う。
VScodeで拡張機能のRemote Developmentに含まれるDevContainersを
使ってコンテナ作成からVScodeでやる方法でも初めてもいいと思う。
※個人的にコンテナ作成はDockerComposeコマンドでやりたい派です。
上記のような条件で開発をしていくので環境条件は下記のようになる
Docker for Windowsのインストール方法については
下記記事で紹介しています
全体的な構成は下記のようになる
.|-- Dockerfile|-- app| |-- api.py| `-- wsgi.py|-- docker-compose.yml|-- gunicorn_config.py`-- requirements.txt
コンテナをdocker-composeを使って構築するための
設定を各ファイルに書いていく。
FROM python:3.10# workspaceディレクトリ作成、移動WORKDIR /workspace# プロジェクトディレクトリにコピーCOPY requirements.txt /workspace# 必要モジュールのインストールRUN pip install --upgrade pipRUN pip install -r requirements.txt
Flask==3.0.2gunicorn==21.2.0
必要なライブラリをインストールする
今回はFlaskとgunicornだけとなる
version: "3"services:flaskapi:container_name: "flask-api"build:context: .dockerfile: Dockerfileports:- "5000:5000"volumes:# バインドマウント- .:/workspace# サーバー(gunicorn)実行command: gunicorn wsgi:flask_api --config gunicorn_config.py# command: gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app wsgi:flask_apitty: true
※コメントアウトしているコマンドについては後述する
from flask import Flask# flaskインスタンス作成def create_app():flask_api = Flask(__name__)@flask_api.route('/')def hello():return "Hello from Flask!!!"return flask_api
エンドポイントを定義したflaskインスタンスを作成するファイル
必ず作成したflaskインスタンスを返却するようにする
アプリのエントリーポイントとなるファイル
# flaskインスタンスを取得する関数をimportfrom app.api import create_app# flaskインスタンスを取得flask_api = create_app()
wsgi.pyは、WSGIサーバー(Gunicorn)とFlaskアプリケーションの接続をする
エントリーポイントとしての役割を持たせるため、api.pyとは分けている。
wsgi.pyは本番環境でのデプロイメントにおいて、WSGI互換のサーバー(例えばGunicornやuWSGIなど)と
アプリケーションを接続するために使用される
※wsgi.pyはWSGIサーバーがアプリケーションオブジェクトを認識するためのエントリポイントとして機能し、
多くのサーバーとフレームワークがこのファイルをデフォルトで探しにくる
イメージととしては下記のような感じでwsgi.pyが
FlaskとGunicornのインターフェース的な役割をしている
そのためwsgi.pyではflaskインスタンスをグローバルスコープで保持しておく必要がある
# ワーカープロセスの数workers = 1# バインドするホストとポートbind = '0.0.0.0:5000'# リロードオプションを有効にするreload = True# ディレクトリの変更chdir = '/workspace/app'
gunicornのコマンドライン引数をgunicorn_config.pyで
定義している。
引数の内容については上記ソースのコメントの通り
コマンドの「wsgi:flask_api」の部分に関しては
GunicornがFlaskアプリケーションの場所を知るために必要な情報であり、
gunicorn_config.pyに記載することはできない。
また全部コマンド引数として書くことも可能でその場合は
コメントアウトしていたコマンドになる
command: gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app wsgi:flask_api
引数に全て書いた場合は「gunicorn_config.py」は使わないので不要となる。
設定が少なければ全部コマンドに書いてもいいかもしれない。
gunicornコマンドからアプリ起動までについてちょっと詳しくまとめておく。
command: gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app wsgi:flask_api
のコマンドの重要なところはFlaskアプリケーションの場所を示してる
下記の部分になる、
--chdir /workspace/app wsgi:flask_api
これは/workspace/app配下にあるwsgi.pyファイルのflask_api(flaskインスタンス)を
さしている。
つまりguniconrnでflaskインスタンスを
起動するという意味になっている。
そしてwsgi.pyでは
# flaskインスタンスを取得する関数をimportfrom app.api import create_app# flaskインスタンスを取得flask_api = create_app()
グローバルスコープで宣言した「flask_api」にcreate_app()で生成した
flaskインスタンスを保持させている。
これでgunicornは起動時にwsgi.pyを通してflaskインスタンスを見つけることができる
各種設定ファイルが完成したのでコンテナを作る
$ docker compose up -d
コンテナ確認
$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa711645ba284 flask-simple-api-flaskapi "gunicorn wsgi:flask…" 4 seconds ago Up 2 seconds 0.0.0.0:5000->5000/tcp flask-api
問題なくできている。
docker-compose.ymlに起動コマンドを書いているので
コンテナを作成して時点でコンテナ内でgunicornが起動している
そのためホストのブラウザから「http://localhost:5000」にアクセスすると
下記のようにレスポンスが表示される。
またはたホストのターミナル当で「curl http://localhost:5000」を
実行する
$ curl http://localhost:5000Hello from Flask!!!
とちゃんと帰ってくるのでサーバー起動できていることがわかる。
gunicorn_config.pyで
# リロードオプションを有効にするreload = True
としているのでホットリロードされるようになっている。
※コマンド引数の場合は「--reload」で指定している
サーバーの起動を自分でしたい場合は
docoker-compose.ymlの下記の部分を消して
コンテナ作成する
version: "3"services:flaskapi:container_name: "flask-api"build:context: .dockerfile: Dockerfileports:- "5000:5000"volumes:# バインドマウント- .:/workspace# サーバー(gunicorn)実行# command: gunicorn wsgi:flask_api --config gunicorn_config.py# command: gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app wsgi:flask_apitty: true
そうすれば、コンテナ作成時に起動されない。
手動起動する場合はdocoker-compose.ymlでコメントアウトしている
コマンドをコンテナ内のターミナルで入力すれば起動できる。
root@08975fb0fe23:/workspace# gunicorn wsgi:flask_api --config gunicorn_config.py[2024-02-23 17:44:02 +0000] [1230] [INFO] Starting gunicorn 21.2.0[2024-02-23 17:44:02 +0000] [1230] [INFO] Listening at: http://0.0.0.0:5000 (1230)[2024-02-23 17:44:02 +0000] [1230] [INFO] Using worker: sync[2024-02-23 17:44:02 +0000] [1247] [INFO] Booting worker with pid: 1247
止めたい時は、「ctrl + c」で止める。
コンテナ起動と同時にgunicornを起動した場合、コンテナ内で停止されるには
killコマンドを使うしかないので、開発中などは手動起動にしておいたほうがいいかもしれない。
※「--reload」を設定している場合、killしてもすぐ新プロセスで起動するので
設定を変更する必要もある。。
いちいちコマンドを打ち込むのが面倒な場合は
VScodeのタスクに登録しておけば、コマンドパレットからすぐ起動できる
.vscode/task.jsonができるので下記のように書き換える
{"version": "2.0.0","tasks": [{"label": "Run Gunicorn","type": "shell","command": "gunicorn","args": ["wsgi:flask_api","--config","gunicorn_config.py"],"group": {"kind": "build","isDefault": true}}]}
この記事ではgunicornは起動時にflaskインスタンスを探しに行くためコマンドで
gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app wsgi:flask_api
のようにflaskインスタンス指定して起動していた
※「wsgi.py」の中の「flask_api」を指定
ただ別の方法として
gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app 'api:create_app()'
flaskインスタンスを作成する関数自体を指定して起動することもできる
※クォーテーションで囲む必要あり
ただwsgi.pyのところでも書いたが、個人的には
のように役割を分けたほうがいいのではないかと思う。
コンテナでflaskの超簡単なAPIを作って
Gunicornで動かすまでの流れをまとめてみた。
今後はもっと踏み込んでREST API等を作ってみる。
この記事で紹介したflaskapiのデバッグ方法については
下記記事で紹介しています