当サイトは、アフィリエイト広告を利用しています

【Docker × Flask】コンテナのFlaskAPIをGunicornで起動する方法

作成日:2024月02月23日
更新日:2024年08月21日

Dockerコンテナを使ってPythonのFlaskを使った開発ができる環境を
作成し、その中でFlaskの超簡単APIを作って外部サーバーであるGunicornで動作させて
動きを確認する

この記事を読めばflaskのプログラムを作って、Gunicorn上で
動かすまでの流れは理解できると思う。

GunicornでFlaskのWEBアプリケーションを動かす場合下記の

  • Gunicornにアプリケーションインスタンスを直接渡す方法
  • Gunicornのカスタムアプリケーションとして起動する方法

二つの方法がある

今回はよりシンプルな「Gunicornにアプリケーションインスタンスを直接渡す方法」を使う
「Gunicornのカスタムアプリケーションとして起動する方法」については下記でまとめています。

またflaskを使ったREST APIの実装については下記記事で紹介してます

プロジェクトの永続化について

コンテナで動作させるflaskプロジェクトの永続化方法について
永続化についてはバインドマウントを使用する

プロジェクトが大きくなってきた場合などは、バインドマウントではなく
名前付きボリュームをつかったほうがいいかもだが
とりあえず動きを確認するだけなので、バインドマウントで作成する。

Dockerのボリューム関係については下記記事でまとめているので
気になる方はご参照ください

またFlaskには開発用に内部サーバーがついているが
今回は外部サーバーであるGunicornを使って動かすようにする。

なぜ外部サーバーであるGunicornを使うのか?

flaskには簡易的な内部サーバーが付属しているため
gunicornなしでも動かすことができる。

開発段階では特に問題はないが、逆に開発段階から
gunicornを使うことで下記のようなメリットがある

パフォーマンスの向上

Gunicornは複数のワーカーを使用してリクエストを並行して処理することができる。
そのためFlaskの内部サーバーよりも高いパフォーマンスを出せる

ぶっちゃけ早い。

安定性の向上

Gunicornは本番環境での使用に耐えうる設計がされており
長時間の稼働においても安定している

柔軟な設定

Gunicornは多くの設定オプションがあり、アプリケーションのニーズに
合わせて細かく調整することが可能。

正直、簡単なAPIを作って動きを確認したいだけだが、
実際にサーバーにデプロイするとなると、内部サーバーではなく
外部サーバー(gunicorn等)を使う必要があるので
早い内から触っておこうという考え。

開発方法

開発はVScodeの拡張機能である「Remote Development」を
使ってVScodeからコンテナにアタッチして行う。

Remote Developmentを使ってコンテナにアタッチして
開発する方法については下記記事参照

コンテナはDockerComposeコマンドで作成して
作成済みのコンテナにVScodeからアタッチする方法を使う。

VScodeで拡張機能のRemote Developmentに含まれるDevContainersを
使ってコンテナ作成からVScodeでやる方法でも初めてもいいと思う。
※個人的にコンテナ作成はDockerComposeコマンドでやりたい派です。

環境

上記のような条件で開発をしていくので環境条件は下記のようになる

  • Windows10
  • Docker version 24.0.6(Docker for Windows)
  • VScode
  • Remote Development(VScodeの拡張機能)
  • Flask 3.0.2
  • gunicorn 21.2.0

Docker for Windowsのインストール方法については
下記記事で紹介しています

構成

全体的な構成は下記のようになる

プロジェクト構成
.
|-- Dockerfile
|-- app
| |-- api.py
| `-- wsgi.py
|-- docker-compose.yml
|-- gunicorn_config.py
`-- requirements.txt

Flaskアプリ動作コンテナの設定

コンテナをdocker-composeを使って構築するための
設定を各ファイルに書いていく。

Dockerfile

Dockerfile
FROM python:3.10
# workspaceディレクトリ作成、移動
WORKDIR /workspace
# プロジェクトディレクトリにコピー
COPY requirements.txt /workspace
# 必要モジュールのインストール
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
  • pythonのイメージを使用する
  • requirements.txtを使って必要なライブラリ等をインストールする

requirements.txt

requirements.txt
Flask==3.0.2
gunicorn==21.2.0

必要なライブラリをインストールする
今回はFlaskとgunicornだけとなる

docker-compose.yml

docker-compose.yml
version: "3"
services:
flaskapi:
container_name: "flask-api"
build:
context: .
dockerfile: Dockerfile
ports:
- "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_api
tty: true
  • docker composeでコンテナ作成するための設定をかく
  • コンテナはDockerfileをbuiildして作成
  • 永続化はバインドマウントでする
  • サーバー起動はgunicorn_config.pyを読み込んで行う。

※コメントアウトしているコマンドについては後述する

api.py

api.py
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インスタンスを返却するようにする

wsgi.py

アプリのエントリーポイントとなるファイル

wsgi.py
# flaskインスタンスを取得する関数をimport
from 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のインターフェース的な役割をしている
2024-06-11-21-35-06

そのためwsgi.pyではflaskインスタンスをグローバルスコープで保持しておく必要がある

gunicorn_config.py

gunicorn_config.py
# ワーカープロセスの数
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コマンドからアプリ起動までの流れ

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では

wsgi.py
# flaskインスタンスを取得する関数をimport
from app.api import create_app
# flaskインスタンスを取得
flask_api = create_app()

グローバルスコープで宣言した「flask_api」にcreate_app()で生成した
flaskインスタンスを保持させている。

これでgunicornは起動時にwsgi.pyを通してflaskインスタンスを見つけることができる

Flaskアプリ動作コンテナの作成

各種設定ファイルが完成したのでコンテナを作る

作成
$ docker compose up -d

コンテナ確認

確認
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a711645ba284 flask-simple-api-flaskapi "gunicorn wsgi:flask…" 4 seconds ago Up 2 seconds 0.0.0.0:5000->5000/tcp flask-api

問題なくできている。

Flaskアプリ動作コンテナの起動

docker-compose.ymlに起動コマンドを書いているので
コンテナを作成して時点でコンテナ内でgunicornが起動している

そのためホストのブラウザから「http://localhost:5000」にアクセスすると
下記のようにレスポンスが表示される。 2024-02-24-02-35-55

またはたホストのターミナル当で「curl http://localhost:5000」を
実行する

curl
$ curl http://localhost:5000
Hello from Flask!!!

とちゃんと帰ってくるのでサーバー起動できていることがわかる。

ホットリロードについて

gunicorn_config.pyで

gunicorn_config.py
# リロードオプションを有効にする
reload = True

としているのでホットリロードされるようになっている。
※コマンド引数の場合は「--reload」で指定している

サーバーの起動を自分でしたい場合

サーバーの起動を自分でしたい場合は
docoker-compose.ymlの下記の部分を消して
コンテナ作成する

docoker-compose.yml
version: "3"
services:
flaskapi:
container_name: "flask-api"
build:
context: .
dockerfile: Dockerfile
ports:
- "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_api
tty: true

そうすれば、コンテナ作成時に起動されない。

手動起動する場合

手動起動する場合はdocoker-compose.ymlでコメントアウトしている
コマンドをコンテナ内のターミナルで入力すれば起動できる。

gunicorn手動起動
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のタスクに登録しておけば、コマンドパレットからすぐ起動できる

2024-03-23-00-06-56

2024-03-23-00-09-32

2024-03-23-00-09-59

.vscode/task.jsonができるので下記のように書き換える

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
}
}
]
}

「F1」で「Run Gunicorn」を選ぶと起動する
2024-03-23-00-15-32

2024-03-23-00-16-43

余談(gunicorn起動の別の方法)

この記事ではgunicornは起動時にflaskインスタンスを探しに行くためコマンドで

コマンド_flaskインスタンス指定
gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app wsgi:flask_api

のようにflaskインスタンス指定して起動していた
※「wsgi.py」の中の「flask_api」を指定

ただ別の方法として

コマンド_flaskインスタンス作成関数
gunicorn --reload -w 1 --bind 0.0.0.0:5000 --chdir /workspace/app 'api:create_app()'

flaskインスタンスを作成する関数自体を指定して起動することもできる
※クォーテーションで囲む必要あり

ただwsgi.pyのところでも書いたが、個人的には

  • flaskインスタンスを作成 → api.py
  • gunicorntとの接続 → wsgi.py

のように役割を分けたほうがいいのではないかと思う。

まとめ

コンテナでflaskの超簡単なAPIを作って
Gunicornで動かすまでの流れをまとめてみた。

今後はもっと踏み込んでREST API等を作ってみる。
この記事で紹介したflaskapiのデバッグ方法については
下記記事で紹介しています

参考書籍

新着記事

タグ別一覧
top