Python + responder + gunicorn + systemd + Nginx で Web アプリを起動する
以前に Python + falcon + gunicorn + systemd でシンプルな Web アプリを起動する というメモを書きました。 最近は responder が流行りらしいので、Python + responder + gunicorn + systemd + nginx 構成で「responder アプリケーションをデーモン化する」手順をメモしておきます。
今回の環境
今回は以下の環境を用いました。
- CentOS8
- pyenv をシステム全体にインストール済み
- Python 3.8.1
ライブラリのインストール
予め responder と gunicorn を pip でインストールしておきます。
| pip install gunicorn responder
|
アプリケーションの配置
アプリケーションを配置するディレクトリを作成します。 今回は /opt/hello
としました。
| mkdir /opt/hello
cd /opt/hello
|
アプリケーションとして /opt/hello/app.py
を以下の内容で新規作成しました。
1
2
3
4
5
6
7
8
9
10
11
12 | cat << EOF > /opt/hello/app.py
import responder
api = responder.API()
@api.route("/")
async def hello(req, resp):
resp.text = "hello, world!"
if __name__ == '__main__':
api.run()
EOF
|
アプリケーション実行用ユーザの作成
セキュリティを確保する為、このサービスは root ユーザでは無く、hello
という専用ユーザで動作させることにします。 hello
というシステムユーザを作成します。
| groupadd -r hello
useradd -g hello -s /sbin/nologin -r hello
|
今回の環境では UID / GID は以下になりました。
| # id hello
uid=994(hello) gid=991(hello) groups=991(hello)
|
gunicorn の設定
今回は下記の内容で gunicorn 用の設定ファイルを用意します。 パラメータは環境に合わせて調整します。 responder は ASGI なのでワーカクラスには uvicorn.workers.UvicornWorker
を指定します。
| cat << EOF > /opt/hello/settings.py
worker_class = 'uvicorn.workers.UvicornWorker'
EOF
|
gunicorn を systemd 経由で起動する
まず先にこのサービスを識別する PID ファイルを作成しておきます。
| mkdir /run/hello/
touch /run/hello/pid
chown -R hello:hello /run/hello/
|
systemd 経由で起動出来るよう、設定ファイルを用意します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | cat << EOF > /etc/systemd/system/hello.service
[Unit]
Description=Hello World!
Requires=hello.socket
After=network.target
[Service]
Type=notify
User=hello
Group=hello
RuntimeDirectory=gunicorn
WorkingDirectory=/opt/hello
ExecStart=/usr/local/pyenv/shims/gunicorn app:api --config settings.py
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
|
Nginx と gunicorn 間は UNIX ドメインソケットで通信させますので、ソケットファイルも用意しておきます。 www-data
は Nginx インストール時に作成されるユーザです (この時点ではまだ Nginx をインストールしていない為、システム上に www-data
ユーザは存在しません)。
| cat << EOF > /etc/systemd/system/hello.socket
[Unit]
Description=hello socket
[Socket]
ListenStream=/run/hello.sock
User=www-data
[Install]
WantedBy=sockets.target
EOF
|
ソケットファイルを有効化しておきます。
| systemctl enable --now hello.socket
|
systemd の設定を追加したので一度、設定を読み込み直します。
後は状態を確認しながらサービスを起動します。
| systemctl status hello.service
systemctl start hello.service
systemctl enable hello.service
systemctl status hello.service
|
設定ファイルに間違いが無ければ gunicorn が起動するはずです。
Nginx のインストール
CentOS8 標準リポジトリ上の Nginx はバージョンが古いので、Nginx 公式リポジトリからインストールします。 Nginx リポジトリを追加します。
| cat << EOF > /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
EOF
|
インストールします。 AppStream が優先されないよう、AppStream の無効化を明示的に指定してインストールします。
| dnf --disablerepo=AppStream -y install nginx
|
Nginx と gunicorn 間の通信設定
Nginx と gunicorn 間は UNIX ドメインソケットで通信させます。 今回は以下のように設定しました。
1
2
3
4
5
6
7
8
9
10
11
12 | cat << EOF > /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name 127.0.0.1;
access_log /var/log/nginx/access.log main;
location / {
proxy_pass http://unix:/run/hello.sock;
}
}
EOF
|
Nginx の起動
後は Nginx の起動&自動起動をすれば完了です。
| systemctl enable nginx
systemctl start nginx
|
接続テスト
ローカルから接続テストを実行してみます。
| # curl http://localhost/
hello, world!
|
問題なければリモートからブラウザで接続テストしてみます。
参考
/etc/nginx/conf.d/default.conf
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 | server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
|