Flask + uWSGI + NginxのサイトをDocker上に構築する

タイトルのとおりですが、Flaskで作ったAPIサイトをuWSGI+Nginxの構成で、Docker上に構築してみました。
この構成であれば、かなり本番運用に近い構成なのではないかと思います。

作業用ディレクトリの作成

作業用ディレクトリとして、flask-uwsgiを作成します。

mkdir flask-uwsgi
cd flask-uwsgi

Flask

まず、FlaskでのAPIサイトですが、下記のような感じで適当に作りましょう。
Flask-CORSなんかも入れておくと良いですよね。
MySQLからデータを取得するようにしてあります。MySQLの接続情報は環境変数から取ることにしています。

from flask import Flask, Response, request, jsonify
from flask_cors import CORS
import mysql.connector
import os

app = Flask(__name__)
CORS(app)

@app.route('/api/v1/get_user/<int:id>', methods=['GET'])
def get_user(id):
    conn = mysql.connector.connect(
        host=os.environ['MYSQL_HOST'] or 'localhost',
        port=os.environ['MYSQL_PORT'] or 3306,
        user=os.environ['MYSQL_USER'],
        password=os.environ['MYSQL_PASSWORD'],
        database=os.environ['MYSQL_DATABASE']
    )
    cur = conn.cursor(dictionary=True)
    cur.execute('SELECT * FROM users WHERE id=%s LIMIT 1', (id,))
    user = cur.fetchone()

    return jsonify({
        'id': id,
        'user_name': user['name']
    })

app.ini

uWSGIで動かすために、app.iniを作成します。

[uwsgi]
module = app
callable = app
die-on-term = true
chdir = /app
processes = 1
master = false
vacuum = true
socket = /tmp/uwsgi.sock
chmod-socket = 666

requirements.txt

Flaskの動作に必要なパッケージをインストールするため、requirements.txtを作成します。

Flask==2.0.3
Flask-Cors==3.0.10
uWSGI==2.0.20
mysql-connector-python==8.0.28

Dockerfileの作成とDockerコンテナイメージの作成

ここまでのファイルを1つのディレクトリ(flask-uwsgi)内にひとまとめにして、同じフォルダにDockerfileを作ります。

ベースイメージとして、alpineという軽量Linux上で動作するPythonのイメージを使用します。
COPYでカレントディレクトリのファイルを、コンテナ上の/appディレクトリにコピーし、そこを作業ディレクトリとします。
パッケージのインストール時にビルドが必要なため、いくつかのalpineのパッケージをインストールしています。

FROM python:alpine
WORKDIR /app
COPY . /app
RUN apk --update-cache add \
    gcc \
    g++ \
    build-base \
    linux-headers \
    python3-dev \
    pcre-dev
RUN pip install -r requirements.txt

Dockerコンテナイメージの作成は下記のようにします。

docker image build -t flask-uwsgi .

ビルドが成功すれば、Dockerコンテナイメージは作成完了です。

Nginx

いま作ったDockerコンテナイメージはuWSGIのソケットを介してFlaskが動作するものであるため、NginxでHTTPリクエストを受け付けてuWSGIのソケットを呼び出すようにします。

flask-uwsgiと同じ階層に、webディレクトリを作成します。

cd ..
mkdir web

webディレクトリにnginx.confファイルを作成し、下記のようにします。

server {
    listen       80;
    server_name  localhost;

    location /api/ {
        include uwsgi_params;
        uwsgi_pass unix:/tmp/uwsgi.sock;
    }
}

Nginx環境は公式のDockerコンテナイメージを試用するだけなので、特にDockerfileは作りません。

docker compose

最後に、flask-uwsgiやwebディレクトリと同じ階層に、docker-compose.yamlファイルを作成し、下記のようにします。

environmentで環境変数を指定することで、MySQLの接続情報を引き渡します。
また、/tmpディレクトリを共有してソケットでの通信を可能にします。

version: '3'
services:
  app:
    image: flask-uwsgi
    environment:
      - MYSQL_HOST=<MySQLホスト>
      - MYSQL_PORT=3306
      - MYSQL_USER=<MySQLユーザー名>
      - MYSQL_PASSWORD=<MySQLパスワード>
      - MYSQL_DATABASE=<データベース名>

    volumes:
      - socket:/tmp
    command: uwsgi --ini /app/app.ini

  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./web/nginx.conf:/etc/nginx/conf.d/default.conf
      - socket:/tmp

volumes:
  socket:

後は、起動するだけです。

docker compose up -d

ちゃんとデータベースを作っていれば、http://localhost/api/v1/get_user/1などとアクセスすると、ユーザーの名前が返ってくるはず。

終了する場合は、下記のとおり。

docker compose down

今回はFlaskのソースコードをイメージの中に組み込んでいるので、本番リリースなどにも対応できると思います。
CI/CDを構成する場合は、FlaskのソースコードをGitリポジトリにコミットしたら、自動的にDockerコンテナイメージができるという形が良いと思います。
逆に、開発環境として使用する場合は、PC上のカレントディレクトリが見える形にすることになるでしょう。

この記事を書いた人

井上 研一

株式会社ビビンコ代表取締役、ITエンジニア/経済産業省推進資格ITコーディネータ。AI・IoTに強いITコーディネータとして活動。2018年、株式会社ビビンコを北九州市に創業。IoTソリューションの開発・導入や、画像認識モデルを活用したアプリの開発などを行っている。近著に「使ってわかった AWSのAI」、「ワトソンで体感する人工知能」。日本全国でセミナー・研修講師としての登壇も多数。