Python
nginx
Flask
docker

Flask+uwsgi+nginxの環境が作りたい?それ、Dockerなら1コマンドで出来るよ。

Flask使ってアプリケーション作ったのはいいけど、デプロイ面倒くさいなぁって思っている人向け。

# python run.py 
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Flaskの公式ドキュメント http://flask.pocoo.org/docs/1.0/quickstart/ には

This launches a very simple builtin server, which is good enough for testing but probably not what you want to use in production. For deployment options see Deployment Options.

開発用サーバはテスト用には十分だけど、本番環境では非推奨って書いてあるので、Dockerを使って楽をしようと思った。

TL;DR

https://github.com/lboavde1121/flaskapp

make run

上記コマンド1つで、Flask+uwsgi+nginxの環境をDockerに構築します。
読むのが面倒な方は、上のリポジトリをクローンしてmake runしてください。

イメージとしては、
Docker <-> nginx <-> uwsgi <-> Flask
こんな感じ。
気が向いたらお絵かきします。

動作環境

  • MacOS
  • Windows
  • Linux(EC2 AmazonLinux)

で動作確認済み。
※下記ツールversion等はMacOS

今回の主役Docker

  • Docker : 18.03.0-ce
  • Docker-compose : 1.20.1

そしてビルド→起動を1コマンドにするためにmake

  • make : GNU Make 3.81

※Windows10 Pro以外のWindowsはDocker Toolbox使おう。
https://docs.docker.com/toolbox/toolbox_install_windows/

構成

Flask部分(./app/src配下)は大したことしてないので今回触れません。
Githubに全部あげてるのでそちらからどうぞ。

.
├── Makefile
├── README.md
├── app
│   ├── Dockerfile
│   ├── requirements.txt
│   ├── src
│   │   ├── config
│   │   │   ├── __init__.py
│   │   │   └── default.py
│   │   ├── run.py
│   │   ├── server
│   │   │   ├── __init__.py
│   │   │   └── hoge
│   │   │       ├── __init__.py
│   │   │       └── hoge_api.py
│   │   └── tests
│   │       ├── __init__.py
│   │       └── test_hoge.py
│   └── uwsgi.ini
├── docker-compose.yml
└── nginx
    ├── Dockerfile
    └── nginx.conf

docker-compose

初めは、1コンテナで構築しようと思っていたが、nginxとかPythonインストールとか面倒だったのでDocker-composeを使って

  • nginx
  • Flask+uwsgi

上記2コンテナをSocketで繋いだ。
  
以下docker-composeの設定ファイル

docker-compose.yml
version: "3"
services:

  uwsgi:
    build: ./app
    volumes:
      - ./app:/var/www/
    ports:
      - "3031:3031"
    environment:
      TZ: "Asia/Tokyo"

  nginx:
    build: ./nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    links:
      - uwsgi
    ports:
      - "4231:80"
    environment:
      TZ: "Asia/Tokyo"

Flaskコンテナ(uwsgi)にapp/*
nginxコンテナにnginx/nginx.confをマウント。

nginxコンテナ

次はnginxコンテナのDockerfile。

nginx/Dockerfile
# ベースイメージの設定
FROM nginx
CMD ["nginx", "-g", "daemon off;","-c","/etc/nginx/nginx.conf"]

nginx起動のコマンドは「RUN」ではなく「CMD」or「ENTRYPOINT」で起動する。(当たり前だけど)

※「CMD」or「ENTRYPOINT」はここ参考

Flaskとuwsgiで繋ぐためにnginxの設定ファイル(nginx.conf)を書き換える。

nginx/nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    upstream uwsgi {
        server uwsgi:3031;
    }

    server {
        listen 80;
        charset utf-8;

        location / {
            include uwsgi_params;
            uwsgi_pass uwsgi;
        }

        location /static {
           alias /static;
        }

        location /media {
            alias /media;
        }
    }
}

Flask+uwsgiコンテナ

以下FlaskコンテナのDockerfile

app/Dockerfile
# ベースイメージ
FROM python:3.6

RUN mkdir /var/www
# workdirの指定
WORKDIR /var/www

# 依存Pythonライブラリ一覧コピー
COPY requirements.txt ./

# 依存Pythonライブラリインストール
RUN pip install --no-cache-dir -r requirements.txt


WORKDIR /var/www/src

CMD ["uwsgi","--ini","/var/www/uwsgi.ini"]

Dockerのマウントのタイミングが、
起動時(ビルド時は未マウント)
のようで、ビルド時に必要なファイル(requirements.txt)はビルド時にコピー(Dockerfile内に明示的に記載する)必要がある。

そして、nginxとFlaskを繋ぐuwsgiの設定ファイル。

app/uwsgi.ini
[uwsgi]
wsgi-file = /var/www/src/run.py
callable = app
master = true
processes = 1
socket = :3031
chmod-socket = 666
vacuum = true
die-on-term = true
py-autoreload = 1

py-autoreload = 1
これを書いておくと、ソース修正したときに自動でリロードしてくれる。

Makefile

NAME=flaskapp

run:
    docker-compose build
    docker-compose up -d

stop:
    docker stop ${NAME}_uwsgi_1 ${NAME}_nginx_1
    docker rm ${NAME}_uwsgi_1 ${NAME}_nginx_1

これだけ。

さぁ! 1コマンドで実行だ!!

# make run
# make run
docker-compose build
Building uwsgi
Step 1/7 : FROM python:3.6
 ---> efb6baa1169f
Step 2/7 : RUN mkdir /var/www
 ---> Using cache
 ---> 00445d785d14
Step 3/7 : WORKDIR /var/www
 ---> Using cache
 ---> 215c53d71716
Step 4/7 : COPY requirements.txt ./
 ---> Using cache
 ---> cabb91df7ca0
Step 5/7 : RUN pip install --no-cache-dir -r requirements.txt
 ---> Using cache
 ---> 62f2a1a976d4
Step 6/7 : WORKDIR /var/www/src
 ---> Using cache
 ---> aaee265830d6
Step 7/7 : CMD ["uwsgi","--ini","/var/www/uwsgi.ini"]
 ---> Using cache
 ---> db35ff6f215b
Successfully built db35ff6f215b
Successfully tagged flaskapp_uwsgi:latest
Building nginx
Step 1/2 : FROM nginx
 ---> b175e7467d66
Step 2/2 : CMD ["nginx", "-g", "daemon off;","-c","/etc/nginx/nginx.conf"]
 ---> Using cache
 ---> 036cf8c36459
Successfully built 036cf8c36459
Successfully tagged flaskapp_nginx:latest
docker-compose up -d
Creating flaskapp_uwsgi_1 ... done
Creating flaskapp_nginx_1 ... done

スクリーンショット 2018-04-13 2.36.32.png

おしまい

実際はもっと設定必要だと思います。
DBはRDS想定なので構築しませんでしたが、同じようなノリでいけると信じています。

質問やフィードバックお待ちしています。
Twitter: https://twitter.com/rt_s2t

参考