nginxのプロセスとかワーカとかスレッドとかについてよくわからないので整理する備忘録
今nginxとflaskのコンテナを使ったシステムを動かしているのだが、そのあたりでワーカーとかそこらへんがよく整理できていなかったので、この機会に勉強し直してみる。
以前自分が書いた記事を参照してみる。 プロセスとスレッドとかタイムスライスとかスケジューリングがわからない - 巨人の足元でたじlog
こいつわかってねーなー。 この中にある有用な記事をもう一度読む。 イケてるエンジニアになろうシリーズ 〜メモリとプロセスとスレッド編〜 - もろず blog
とりあえずまとめ
プロセスは専用のメモリ領域を利用する スレッドは共有のメモリ領域を利用する
スレッドのほうがいいじゃん。という結論?だった。
この辺はいいんだ。結局これが実際のアプリケーション、ミドルウェアになったときにどこに効いてくるのかを知りたいのが今日の目的。
nginxとflaskのアプリケーションを作ってみる。
検証したいことは、flaskのアプリケーションで重たい処理があって、1リクエストを処理中に次のリクエストが来たらどうなるかということ。 これをnginxとかがどううまいことやってくれるのかっていうことを確認したい。
Dockerfile
FROM ubuntu:latest RUN apt-get update -y RUN apt-get install -y python-pip python-dev build-essential COPY . /app WORKDIR /app RUN pip install -r requirements.txt ENTRYPOINT ["python"] CMD ["app.py"]
requirements.txt
Flask==0.10.1
$ docker build . -t flask-thread:latest
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE flask-thread latest 5db7f165ba72 2 minutes ago 436MB
できた。
立ち上げる。
$ docker run -it -p 5000:5000 flask-thread
ブラウザからlocalhost:5000を叩く。 特に今は負荷もかけてないので、一瞬でレスポンスが帰ってくる
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 926-602-021 172.17.0.1 - - [25/Sep/2019 16:56:22] "GET / HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 16:56:22] "GET / HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 16:56:23] "GET /favicon.ico HTTP/1.1" 404 - 172.17.0.1 - - [25/Sep/2019 16:56:35] "GET / HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 16:57:01] "GET / HTTP/1.1" 200 -
sleep処理を作成
from flask import Flask import time from datetime import datetime app = Flask(__name__) @app.route('/') def hello_world(): return 'Flask Dockerized.Ver.0.1.4' @app.route('/sleep') def sleep(): time.sleep(3) dt = datetime.now return dt if __name__ == '__main__': app.run(debug=True,host='0.0.0.0')
これに連続して6回リクエストを送ってみると、
172.17.0.1 - - [25/Sep/2019 17:06:24] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:06:27] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:06:30] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:06:33] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:06:36] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:06:39] "GET /sleep HTTP/1.1" 200 -
と、1つの処理が終わってから3秒待っている。 つまり、一つの処理のレスポンスを返さない限りは他のリクエストの処理が開始できないということ。
このあたりに関しては昔調べてた気がした。 Flaskのデフォルトでは同時アクセスを処理できない - Qiita これにコメントしといた。
ところでいま適当に動かしてるのはFlask==0.10.1
だった。
最新版のFlask==1.1.1
に変更してみる。
* Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 209-172-304 172.17.0.1 - - [25/Sep/2019 17:14:07] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:14:08] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:14:10] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:14:12] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:14:13] "GET /sleep HTTP/1.1" 200 - 172.17.0.1 - - [25/Sep/2019 17:14:15] "GET /sleep HTTP/1.1" 200 -
3秒待たずして、次のリクエストが返ってきている。
これがマルチスレッドになっているということか。
ところで、会社のコードではworkerとかだった気がする。
flaskではなくて、gunicornの設定だった。
そもそも、flaskは裸で使うのはdev環境だけにしなさいって注意されてるしな。
gunicornの設定・挙動ををまた確認しなくては。
flask + gunicorn
そもそも今までなんの気なしに書いていたDockerfileの末尾
ENTRYPOINT ["python"] CMD ["app.py"]
これの表現気になった。
このあたりは雰囲気でやっている。
[docker] CMD とENTRYPOINT の違いを試してみた - Qiita
をみると、
docker image からdocker container を実行するときにCMD やENTRYPOINT の記述内容が実行されます。 1つのDockerfile にCMD、ENTRYPOINT は1度のみ。 ※ 複数記載されている場合、最後の1個が実行される。
とな。
話が違う。
一旦、以下で試してみる。
# ENTRYPOINT ["python"] # CMD ["app.py"] ENTRYPOINT ["python", "app.py"]
こちらでも挙動は特に問題なさそう。
このあたりはまたの機会に整理することにする。
docker-composeでgunicorn+nginx+flaskを動かしてみた話 - Qiita
gunicornの使い方なんとなく理解。
CMD ["gunicorn", "flask_sample:app"]
と書いてあるところの、flask_sampleがファイル名、appが最初に実行させたい関数か。
FROM ubuntu:latest RUN apt-get update -y RUN apt-get install -y python-pip python-dev build-essential COPY . /app WORKDIR /app RUN pip install -r requirements.txt ENTRYPOINT ["gunicorn", "app:app", "-b", "0.0.0.0:5000"]
これでbuildしてd run -it -p 5000:5000 flask-thread
すると、複数リクエストを投げると、1リクエストずつしか処理されていない。
これは最初にまずgunicornがリクエストを受け付けているが、gunicornはデフォルトでマルチスレッドとかになっていないからなはず。
これをマルチスレッドにしたかったら、gunicornのconfigに設定する。
gunicorn + Flask + nginx + Systemdで動かしてみた - Qiita
guniconf.py
import multiprocessing workers = 2
Dockerfile
ENTRYPOINT ["gunicorn", "app:app", "-b", "0.0.0.0:5000", "--config", "guniconf.py"]
これで2スレッドで実行できる。