Python + falcon + gunicorn + systemd でシンプルな Web アプリを起動する
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 経由で起動出来るよう、設定ファイルを用意します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | 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 status hello.service
systemctl start hello.service
systemctl status hello.service
|
設定ファイルが間違いが無ければ以下のように systemd 経由で gunicorn が起動するはずです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | # 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
|