Python で falcon と gunicorn を使い、シンプルな Web アプリケーションを作り、更にそれを systemd に登録するまでの手順をメモしておきます。
今回の環境
今回は以下の環境を用いました。
- CentOS8
- pyenv 環境
- Python 3.8.0
ライブラリのインストール
予め falcon と gunicorn を pip でインストールしておきます。
pip install falcon gunicorn
falcon アプリケーションの配置
アプリケーションを配置するディレクトリを作成します。 今回は /opt/hello
としました。
mkdir -p /opt/hello/ cd /opt/hello/
実際のアプリケーションは以下のように、最低限としました。
cat << EOF > /opt/hello/hello.py import falcon import json class Hello: def on_get(self, req, resp): resp.body = "Hello, World!" app = falcon.API() app.add_route('/', Hello()) EOF
gunicorn の設定
今回は下記の内容で gunicorn 用の設定ファイルを用意します。
cat << EOF > /opt/hello/settings.py import multiprocessing bind = '0.0.0.0:5000' worker_class = 'sync' workers = 3 max_requests = 100 max_requests_jitter = 5 EOF
bind
このアプリケーションが Listen するアドレス / ポート番号を指定します。
worker_class
gunicorn のワーカークラスには以下の 5 種類を指定出来ます。
- sync
- gevent
- eventlet
- tornado
- gthread
今回はシンプルに sync を指定しました。
workers
ワーカーの同時起動数を指定します。 ワーカークラスが sync の場合、ワーカー数は「CPU 数 x 2 + 1」が推奨だそうです。 今回は 1CPU の貧弱な環境です… その為、ワーカー数は 3 としました。
# grep processor /proc/cpuinfo | wc -l 1
max_requests
ワーカーが処理した回数がこの値に達した場合、該当のワーカーを再起動します。
max_requests_jitter
max_requests
しか指定していないと、全てのワーカーが近いタイミングで再起動する可能性が高くなってしまいます。 この値を指定しておくことで「ゼロ ~ 指定値」の値を max_requests
に加算したタイミングでワーカーが再起動され、結果的にワーカーごとの再起動タイミングがずれやすくなります。
テスト起動
作成した falcon アプリケーションを gunicorn 経由で起動するには以下のように実行します。 今回は「hello.py」ファイルの「app」がエントリーポイントになる為、gunicorn には hello:app
と指定します。 今回は settings.py
というファイルに設定項目を用意してありますが、gunicorn の引数で起動パラメータを渡すことも可能です。
gunicorn hello:app --config settings.py
実際に起動してみると以下のようになります。 設定ファイルで指定している通り、ワーカーが 3 つ起動していることが分かります。
# gunicorn hello:app --config settings.py [2019-12-17 21:49:37 +0900] [3788] [INFO] Starting gunicorn 20.0.4 [2019-12-17 21:49:37 +0900] [3788] [INFO] Listening at: http://0.0.0.0:5000 (3788) [2019-12-17 21:49:37 +0900] [3788] [INFO] Using worker: sync [2019-12-17 21:49:37 +0900] [3834] [INFO] Booting worker with pid: 3834 [2019-12-17 21:49:37 +0900] [3835] [INFO] Booting worker with pid: 3835 [2019-12-17 21:49:37 +0900] [3836] [INFO] Booting worker with pid: 3836
同一サーバからアクセスしてみると期待した応答があるはずです。
# curl 127.0.0.1:5000
Hello, World!
systemd 経由で起動する
systemd 経由で起動出来るよう、設定ファイルを用意します。
cat << EOF > /etc/systemd/system/hello.service [Unit] Description = Hello World! After = network.target [Service] PermissionsStartOnly = true PIDFile = /run/hello/pid User = root Group = root WorkingDirectory = /opt/hello ExecStartPre = /bin/mkdir /run/hello ExecStartPre = /bin/chown -R root:root /run/hello ExecStart = /root/.pyenv/shims/gunicorn hello:app --pid /run/hello/pid --config settings.py ExecReload = /bin/kill -s HUP $MAINPID ExecStop = /bin/kill -s TERM $MAINPID ExecStopPost = /bin/rm -rf /run/hello PrivateTmp = true Restart=always [Install] WantedBy = multi-user.target EOF
systemd の設定を追加したので一度、設定を読み込み直します。
systemctl daemon-reload
後は状態を確認しながらサービスを起動します。
systemctl status hello.service systemctl start hello.service systemctl status hello.service
設定ファイルが間違いが無ければ以下のように systemd 経由で gunicorn が起動するはずです。
# systemctl status hello.service ● hello.service - Hello World! Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled) Active: inactive (dead) # systemctl start hello.service # systemctl status hello.service ● hello.service - Hello World! Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled) Active: active (running) since Tue 2019-12-17 22:05:34 JST; 5s ago Process: 6952 ExecStartPre=/bin/chown -R root:root /run/hello (code=exited, status=0/SUCCESS) Process: 6951 ExecStartPre=/bin/mkdir /run/hello (code=exited, status=0/SUCCESS) Main PID: 6954 (gunicorn) Tasks: 4 (limit: 12167) Memory: 37.7M CGroup: /system.slice/hello.service ├─6954 /root/.pyenv/versions/3.8.0/bin/python3.8 /root/.pyenv/versions/3.8.0/bin/gunicorn hello:app --pid /run/hello/pid --config > ├─7041 /root/.pyenv/versions/3.8.0/bin/python3.8 /root/.pyenv/versions/3.8.0/bin/gunicorn hello:app --pid /run/hello/pid --config > ├─7042 /root/.pyenv/versions/3.8.0/bin/python3.8 /root/.pyenv/versions/3.8.0/bin/gunicorn hello:app --pid /run/hello/pid --config > └─7043 /root/.pyenv/versions/3.8.0/bin/python3.8 /root/.pyenv/versions/3.8.0/bin/gunicorn hello:app --pid /run/hello/pid --config > Dec 17 22:05:34 172-020-000-153 systemd[1]: Starting Hello World!... Dec 17 22:05:34 172-020-000-153 systemd[1]: Started Hello World!. Dec 17 22:05:34 172-020-000-153 gunicorn[6954]: [2019-12-17 22:05:34 +0900] [6954] [INFO] Starting gunicorn 20.0.4 Dec 17 22:05:34 172-020-000-153 gunicorn[6954]: [2019-12-17 22:05:34 +0900] [6954] [INFO] Listening at: http://0.0.0.0:5000 (6954) Dec 17 22:05:34 172-020-000-153 gunicorn[6954]: [2019-12-17 22:05:34 +0900] [6954] [INFO] Using worker: sync Dec 17 22:05:34 172-020-000-153 gunicorn[6954]: [2019-12-17 22:05:34 +0900] [7041] [INFO] Booting worker with pid: 7041 Dec 17 22:05:34 172-020-000-153 gunicorn[6954]: [2019-12-17 22:05:34 +0900] [7042] [INFO] Booting worker with pid: 7042 Dec 17 22:05:34 172-020-000-153 gunicorn[6954]: [2019-12-17 22:05:34 +0900] [7043] [INFO] Booting worker with pid: 7043