[SadServers] 解説 "Melbourne": WSGI with Gunicorn
"Melbourne": WSGI with Gunicorn
SadServersの "Melbourne": WSGI with Gunicorn の解説です。
SadServers って何? って人は、 SadServers解説 を見てください。
一言でいうと、LeetCode (コーディング問題集) の Linuxサーバ設定版のようなものです。
問題
問題を見ていきます。 >
の箇所は DeepLでの翻訳です。
Scenario: "Melbourne": WSGI with Gunicorn
Level: Medium
Description: There is a Python WSGI web application file at /home/admin/wsgi.py , the purpose of which is to serve the string "Hello, world!". This file is served by a Gunicorn server which is fronted by an nginx server (both servers managed by systemd). So the flow of an HTTP request is: Web Client (curl) -> Nginx -> Gunicorn -> wsgi.py . The objective is to be able to curl the localhost (on default port :80) and get back "Hello, world!", using the current setup.
> /home/admin/wsgi.py に Python WSGI ウェブアプリケーションファイルがあり、その目的は "Hello, world!" という文字列を提供することです。このファイルは、nginxサーバをフロントとするGunicornサーバによって提供されます(両サーバはsystemdによって管理されています)。つまり、HTTPリクエストの流れはWeb Client (curl) -> Nginx -> Gunicorn -> wsgi.py .目標は、現在の設定で、localhost (on default port :80) を curl して "Hello, world!" を返せるようにすることです。
Test: curl -s http://localhost returns Hello, world! (serving the wsgi.py file via Gunicorn and Nginx)
> curl -s http://localhost return Hello, world!(wsgi.pyファイルをGunicornとNginxで提供する)
Time to Solve: 20 minutes.
OS: Debian 11
Python WSGIというのは、Webサーバと Pythonアプリケーションサーバを結びつける規格です。
[Gunicorn](https://github.com/benoitc/gunicorn)というのは、Python WSGIを実装している、アプリケーションサーバで、wsgi.py
を実行しています。
WebサーバNginx
は、Python WSGIに対応しているので、WSGI対応のGunicorn
と連携することができます。
つまり、次のような関係となっています。
解説
早速、curlを実行します。
admin@ip-172-31-43-152:/$ curl -s http://localhost
# 何も返ってこない。
# 詳細を見るために、-s の代わりに -v を指定する。
admin@ip-172-31-43-152:/$ curl -v http://localhost
* Trying 127.0.0.1:80...
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to localhost port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to localhost port 80: Connection refused
port 80に接続できていません。 port 80 で待ち受けるアプリは、Nginxなので、Nginxが正しく動作しているかを確認します。
admin@ip-172-31-43-152:/$ systemctl status nginx
● nginx.service - A high performance web server and a reverse prox>
Loaded: loaded (/lib/systemd/system/nginx.service; disabled; >
Active: inactive (dead)
Docs: man:nginx(8)
Active: inactive (dead)
となっているので、Nginxは起動していません。 なので、起動します。
# nginxの起動
admin@ip-172-31-43-152:/var/log/nginx$ sudo systemctl start nginx
# 起動したか確認する。
admin@ip-172-31-43-152:/var/log/nginx$ sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; disabled; vendor preset: enabled)
Active: active (running) since Thu 2023-01-19 07:48:03 UTC; 6s ago
Docs: man:nginx(8)
Process: 964 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited>
Process: 965 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=>
Main PID: 966 (nginx)
Tasks: 3 (limit: 524)
Memory: 11.2M
CPU: 41ms
CGroup: /system.slice/nginx.service
├─966 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─967 nginx: worker process
└─968 nginx: worker process
Jan 19 07:48:03 ip-172-31-43-152 systemd[1]: Starting A high performance web server and a reverse>
Jan 19 07:48:03 ip-172-31-43-152 systemd[1]: Started A high performance web server and a reverse >
正しく起動しました。では、curlを再度実行します。
admin@ip-172-31-43-152:/etc/nginx$ curl -v http://localhost
* Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 502 Bad Gateway
< Server: nginx/1.18.0
< Date: Thu, 19 Jan 2023 08:53:46 GMT
< Content-Type: text/html
< Content-Length: 157
< Connection: keep-alive
<
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
* Connection #0 to host localhost left intact
port 80には接続できましたが、502エラーとなっています。
Gunicornとの接続ができているか確認するために、Nginxの設定を見ます。
# Nginx の設定ファイルのあるフォルダに移動
admin@ip-172-31-43-152:/$ cd /etc/nginx
# gunicorn関連の設定があるか確認。
admin@ip-172-31-43-152:/etc/nginx$ grep guni *
grep: conf.d: Is a directory
grep: modules-available: Is a directory
grep: modules-enabled: Is a directory
grep: sites-available: Is a directory
grep: sites-enabled: Is a directory
grep: snippets: Is a directory
# 無いので、サブフォルダを確認
admin@ip-172-31-43-152:/etc/nginx$ grep guni */*
sites-available/default: proxy_pass http://unix:/run/gunicorn.socket;
sites-enabled/default: proxy_pass http://unix:/run/gunicorn.socket;
/etc/nginx/sites-available/default
と /etc/nginx/sites-enable/default
の 2ファイルにgunicorn関連の設定があります。 どうやら、Unixドメインソケット unix:/run/gunicorn.socket
に接続しているようです。
Gunicorn側の設定を見てみます。設定ファイルなどよくわからないので、とりあえず、ps で 実行時の引数を見てみます。
admin@ip-172-31-43-152:/etc/nginx$ ps auxww | grep guni
admin 592 0.0 4.7 27480 21980 ? Ss 07:43 0:00 /usr/bin/python3 /usr/local/bin/gunicorn --bind unix:/run/gunicorn.sock wsgi
admin 653 0.0 4.1 27560 19308 ? S 07:43 0:00 /usr/bin/python3 /usr/local/bin/gunicorn --bind unix:/run/gunicorn.sock wsgi
どうやら、 Gunicornは、 Unixドメインソケット unix:/run/gunicorn.sock
で待ち受けているようです。
unix:/run/gunicorn.sock
と unix:/run/gunicorn.socket
で違いがありますね。こちらを修正します。
/etc/nginx/sites-available/default
を vi
で開いて、
proxy_pass http://unix:/run/gunicorn.socket;
を
proxy_pass http://unix:/run/gunicorn.sock;
に修正します。/etc/nginx/sites-enable/default
は /etc/nginx/sites-available/default
のシンボリックリンクなので修正不要です。
Nginxの設定を変更したので、Nginxを再起動して、curl
を実行します。
# Nginxの再起動
admin@ip-172-31-43-152:/$ sudo systemctl restart nginx
# curl実行
admin@ip-172-31-43-152:/etc/nginx/sites-available$ curl -v http://localhost
* Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Thu, 19 Jan 2023 07:56:53 GMT
< Content-Type: text/html
< Content-Length: 0
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
200が返ってくるようになりました。ただ、Hello, world!
は返ってきていません。
/home/admin/wsgi.py
ファイルを見ます。
admin@ip-172-31-43-152:~$ cd /home/admin/
admin@ip-172-31-43-152:~$ cat wsgi.py
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '0'), ])
return [b'Hello, world!']
Content-Length:0
となっています。 返す文字列 Hello, world!
は 13バイト (13文字)なので、ここを 13
に vi
で修正します。
admin@ip-172-31-43-152:~$ cat wsgi.py
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '13'), ])
return [b'Hello, world!']
curlを再度実行します。
# curl実行
admin@ip-172-31-43-152:/etc/nginx/sites-available$ curl -v http://localhost
* Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Thu, 19 Jan 2023 07:56:53 GMT
< Content-Type: text/html
< Content-Length: 0
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
おや、 Content-Length: 0
のままです。 wsgi.py
の修正が反映されていないようなので、Gunicornの再起動します。
[update] Gunicornをsystemctlで再起動するように修正。
admin@ip-172-31-44-185:~$ sudo systemctl restart gunicorn
admin@ip-172-31-44-185:~$ sudo systemctl status gunicorn
● gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2023-02-05 14:06:19 UTC; 5s ago
TriggeredBy: ● gunicorn.socket
Main PID: 962 (gunicorn)
Tasks: 2 (limit: 524)
Memory: 16.8M
CPU: 198ms
CGroup: /system.slice/gunicorn.service
├─962 /usr/bin/python3 /usr/local/bin/gunicorn --bind unix:/run/gunicorn.sock wsgi
└─963 /usr/bin/python3 /usr/local/bin/gunicorn --bind unix:/run/gunicorn.sock wsgi
Feb 05 14:06:19 ip-172-31-44-185 systemd[1]: Started gunicorn daemon.
Feb 05 14:06:20 ip-172-31-44-185 gunicorn[962]: [2023-02-05 14:06:20 +0000] [962] [INFO] Starting gunico>
Feb 05 14:06:20 ip-172-31-44-185 gunicorn[962]: [2023-02-05 14:06:20 +0000] [962] [INFO] Listening at: u>
Feb 05 14:06:20 ip-172-31-44-185 gunicorn[962]: [2023-02-05 14:06:20 +0000] [962] [INFO] Using worker: s>
Feb 05 14:06:20 ip-172-31-44-185 gunicorn[963]: [2023-02-05 14:06:20 +0000] [963] [INFO] Booting worker >
a
再起動したら、 curl
を実行
admin@ip-172-31-43-152:~$ curl -v http://localhost
* Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Thu, 19 Jan 2023 08:00:51 GMT
< Content-Type: text/html
< Content-Length: 13
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Hello, world!admin@ip-172-31-43-152:~$
正しく、 Hello, world!
が返ってきました。
Check My Solution
で確認して終わりです。
Discussion