🦄

SadServers解説#21 "Melbourne": WSGI with Gunicorn

2024/05/17に公開

https://ja.wikipedia.org/wiki/メルボルン

問題概要

シナリオ

Gunicornを使用したWSGI

問題詳細

/home/admin/wsgi.pyというPython WSGIウェブアプリケーションファイルがあり、そのアプリケーションは文字列 "Hello, world!" を返します。このファイルはGunicornサーバーによって提供され、その手前にはnginxサーバーが配置されています(両方のサーバーはsystemdによって管理されています)。

HTTPリクエストのフローは次のようになります

ウェブクライアント (curl) -> Nginx -> Gunicorn -> wsgi.py

この設定を使用して、localhost(アクセスするポートは、デフォルトポートの80)にcurlコマンドを実行し、「Hello, world!」が返されるようにすることが目標です。

解決判定

Check My Solutionボタンをクリックしてください。
解答が正解かどうか、コマンドプロンプト上で確認することも可能です。次のコマンドを実行して、以下と同じ出力が得られた場合は正解です。

$ curl -s http://localhost
Hello, world!

 

問題解決の方針

【表示する】

今回の問題は、WEBアプリケーションのトラブルシューティングです。
コマンドのエラーメッセージを見たり、ログを見たりしながら、一つずつ問題に対処していきましょう。

解決の手順を表示する
  1. curlコマンドを実行し、エラーを読んで対処する
  2. nginxgunicornが起動しているか確認し、問題があれば対処する
  3. 問題に対処したら、再度curlを実行してエラーメッセージを読んで対処する
  4. 解決せず行き詰ってしまったら、ログを探し、問題に対処する

 

ヒント

一部、SadServers公式のヒントを改変しています。

ヒント1

まずは、curlコマンドを実行してみましょう。

実行コマンド

-Iのオプションを指定することで、レスポンスヘッダーを表示することができます。(-sオプションは、進捗やエラーを表示しないオプションです。)

$ curl -s http://localhost
$ curl -I http://localhost
curl: (7) Failed to connect to localhost port 80: Connection refused

そもそもWEBアプリケーションへの通信が通っていないようです。

ヒント2

nginxgunicornの状態を確認しましょう。

実行コマンド

nginxgunicornsystemdに登録されているとのことなので、systemctlコマンドでサービスの状態を確認しましょう。

$ 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: inactive (dead)
       Docs: man:nginx(8)
$ systemctl status gunicorn
● gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2024-05-16 14:09:40 UTC; 2min 25s ago
TriggeredBy: ● gunicorn.socket
   Main PID: 611 (gunicorn)
      Tasks: 2 (limit: 524)
     Memory: 17.1M
        CPU: 296ms
     CGroup: /system.slice/gunicorn.service
             ├─611 /usr/bin/python3 /usr/local/bin/gunicorn --bind unix:/run/gunicorn.sock wsgi
             └─681 /usr/bin/python3 /usr/local/bin/gunicorn --bind unix:/run/gunicorn.sock wsgi

May 16 14:09:40 [hostname(マスクしています)] systemd[1]: Started gunicorn daemon.
May 16 14:09:41 [hostname(マスクしています)] gunicorn[611]: [2024-05-16 14:09:41 +0000] [611] [INFO] Starting gunicorn 20.1.0
May 16 14:09:41 [hostname(マスクしています)] gunicorn[611]: [2024-05-16 14:09:41 +0000] [611] [INFO] Listening at: unix:/run/gunicorn.sock (611)
May 16 14:09:41 [hostname(マスクしています)] gunicorn[611]: [2024-05-16 14:09:41 +0000] [611] [INFO] Using worker: sync
May 16 14:09:41 [hostname(マスクしています)] gunicorn[681]: [2024-05-16 14:09:41 +0000] [681] [INFO] Booting worker with pid: 681

nginxが起動していないので、起動します。

$ sudo systemctl restart nginx
$ 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 2024-05-16 14:12:47 UTC; 3s ago
       Docs: man:nginx(8)
    Process: 884 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 885 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 886 (nginx)
      Tasks: 3 (limit: 524)
     Memory: 11.1M
        CPU: 34ms
     CGroup: /system.slice/nginx.service
             ├─886 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─887 nginx: worker process
             └─888 nginx: worker process

May 16 14:12:47 [hostname(マスクしています)] systemd[1]: Starting A high performance web server and a reverse proxy server...
May 16 14:12:47 [hostname(マスクしています)] systemd[1]: Started A high performance web server and a reverse proxy server.

特にエラーもなく起動できたようです。

ヒント3

nginxが起動したので、再びcurlコマンドを実行してみましょう。

$ curl -s http://localhost
<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>
$ 
$ curl -I http://localhost
HTTP/1.1 502 Bad Gateway
Server: nginx/1.18.0
Date: Thu, 16 May 2024 14:13:21 GMT
Content-Type: text/html
Content-Length: 157
Connection: keep-alive

エラーメッセージが変わりました。
BadGateway?WEBアプリケーションに到達できてはいますが、何か問題が起きているようです。
ログを調査してみましょう。

実行コマンド1

/var/log以下のログを見てみます。

$ less /var/log/
alternatives.log       btmp                   daemon.log             dpkg.log.1             lastlog                runit/                 user.log.1
alternatives.log.1     btmp.1                 daemon.log.1           faillog                messages               syslog                 wtmp
apt/                   chrony/                debug                  journal/               messages.1             syslog.1               
auth.log               cloud-init-output.log  debug.1                kern.log               nginx/                 unattended-upgrades/   
auth.log.1             cloud-init.log         dpkg.log               kern.log.1             private/               user.log               
$ less /var/log/nginx/
access.log    access.log.1  error.log     error.log.1   
$ less /var/log/nginx/error.log
$ 
$ 
$ 
$ cat /var/log/
alternatives.log       btmp                   daemon.log             dpkg.log.1             lastlog                runit/                 user.log.1
alternatives.log.1     btmp.1                 daemon.log.1           faillog                messages               syslog                 wtmp
apt/                   chrony/                debug                  journal/               messages.1             syslog.1               
auth.log               cloud-init-output.log  debug.1                kern.log               nginx/                 unattended-upgrades/   
auth.log.1             cloud-init.log         dpkg.log               kern.log.1             private/               user.log               
$ cat /var/log/nginx/
access.log    access.log.1  error.log     error.log.1   
$ cat /var/log/nginx/error.log
2024/05/16 14:13:14 [crit] 888#888: *1 connect() to unix:/run/gunicorn.socket failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.socket:/", host: "localhost"
2024/05/16 14:13:21 [crit] 888#888: *3 connect() to unix:/run/gunicorn.socket failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: , request: "HEAD / HTTP/1.1", upstream: "http://unix:/run/gunicorn.socket:/", host: "localhost"

最後の行を見ると、/run/gunicorn.socketというファイルで問題が起きているようです。
このファイルを見てみましょう。

実行コマンド2
$ ls -l /run/gunicorn.socket
ls: cannot access '/run/gunicorn.socket': No such file or directory
$ ls -l /run/ | grep gunicorn
srw-rw-rw-  1 root    root       0 May 16 14:09 gunicorn.sock

/run/gunicorn.socketというファイルは存在せず、代わりに/run/gunicorn.sockというファイルが存在しています。
nginxの設定内で、指定しているファイル名が間違っているようですね。修正しましょう。

$ sudo cp /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/default.bak
$ vi /etc/nginx/sites-enabled/default
$ sudo vi /etc/nginx/sites-enabled/default
$ diff /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/default.bak
6c6
<         proxy_pass http://unix:/run/gunicorn.sock;
---
>         proxy_pass http://unix:/run/gunicorn.socket;
$ systemctl restart gunicorn
ヒント4
$ curl -s http://localhost
$ curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Thu, 16 May 2024 14:22:20 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive

相変わらず、所望の結果は得られないようですが、エラーメッセージの内容が変わりました。よく見てみましょう。

実行コマンド

Content-Length: 0と出ています。コンテンツの長さが0?WEBコンテンツの設定がおかしいようです。

$ cat /etc/nginx/sites-enabled/default 
$ cat /home/admin/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と指定されています。
この数字を大きな数字に変えてみましょう。今回は、大きめに100としてみます。

$ cp /home/admin/wsgi.py /home/admin/wsgi.py.bak
$ vi /home/admin/wsgi.py
$ diff /home/admin/wsgi.py /home/admin/wsgi.py.bak 
2c2
<     start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '100'), ])
---
>     start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '0'), ])
$ sudo systemctl restart gunicorn
$ curl -s http://localhost
Hello, world!

curlが通りました!

 
「いきなり問題を解き始めても調べるばかりになってしまう…」 「やりたいことが分かっても、コマンドが分からない…」 という方は、下記の記事でLinuxのコマンドを復習してから、SadServersの問題に取り掛かってみてはいかがでしょうか。
https://zenn.dev/comf_nakamura/articles/linux_command

 

余談

$ cat /etc/nginx/sites-enabled/default 
$ cat /home/admin/wsgi.py 
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '0'), ])
    return [b'Hello, world!']

WEBアプリケーションのファイルを見ていて、最後の行でbがコードの構文ミスかと思っていたら、その後の文字列がバイト列であることを示すプレフィックスらしいです。
bを消しても表示されるか出来心でやってみましたが、やはり表示できませんでした。

$ diff /home/admin/wsgi.py /home/admin/wsgi.py.bak
2,3c2,3
<     start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '100'), ])
<     return ['Hello, world!']
---
>     start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', '0'), ])
>     return [b'Hello, world!']
$ curl -s http://localhost
$ curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Thu, 16 May 2024 14:52:20 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive

問題一覧はこちら

https://zenn.dev/comf_nakamura/articles/sadservers_sitemap

Discussion