とほほのPython入門 - WSGI

目次

WSGIとは

インストール

AlmaLinux 9 + SimpleServer

開発時点であればシンプルサーバーを起動することができます。シングルプロセス、シングルスレッドとなるため商用環境では Apache または Nginx と組み合わせましょう。

必要なモジュールをインストールしてください。

# dnf -y install python3-pip
# pip3 install wsgi_static_middleware

アプリケーションフォルダを作成してください。

# mkdir -p /opt/myapp

/opt/myapp/wsgi.py を下記の様に作成してください。

from wsgiref.simple_server import make_server
from wsgi_static_middleware import StaticMiddleware

def application(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return ["OK\n".encode("UTF-8")]

if __name__ == "__main__":
    static_dirs = ["/opt/myapp/static"]
    app = StaticMiddleware(application, static_root="static", static_dirs=static_dirs)
    with make_server("0.0.0.0", 80, app) as httpd:
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            pass

下記で実行してください。

# python3 -B /opt/myapp/wsgi.py

curl で動作確認します。

$ curl http://localhost
OK

CSS や JS や画像などのスタティックファイルは /var/myapp/static 配下に置くことができます。

AlmaLinux 9 + Apache + mod_wsgi

mod_wsgi の公式サイトは下記にあります。

必要なパッケージをインストールしてください。

# dnf -y install httpd gcc python3-pip python3-devel httpd-devel
# pip3 install mod-wsgi

アプリケーションフォルダを作成してください。

# mkdir -p /opt/myapp

下記の内容で /opt/myapp/wsgi.py を作成してください。

def application(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return ["OK\n".encode("UTF-8")]

mod_wsgi-*.so ファイルのパス名を下記で調べてください。

# find / -name "mod_wsgi*.so"

/etc/httpd/conf.d/wsgi.conf ファイルに下記を記述してください。LoadModule には上記で調べたパスを指定してください。

Alias /favicon.ico /opt/myapp/static/img/favicon.ico

<VirtualHost "*:80">
    LoadModule wsgi_module /usr/local/lib64/python3.9/site-packages/mod_wsgi/server/mod_wsgi-py39.cpython-39-x86_64-linux-gnu.so
    DocumentRoot /opt/myapp
    WSGIScriptAlias / /opt/myapp/wsgi.py
    Alias /static/ /opt/myapp/static/
    <Directory /opt/myapp>
        Require all granted
    </Directory>
</VirtualHost>

Apache を起動してください。

-- ホストOSの場合 --
# systemctl start httpd
-- コンテナの場合 --
# /usr/sbin/httpd -DFORGROUND

curl で動作確認します。

$ curl http://localhost
OK

コンフィグファイルに関する詳細は下記を参照してください。

AlmaLinux 9 + Nginx + uWSGI

Nginx の場合は uWSGI を使用します。uWSGI の公式サイトは下記にあります。

必要なモジュールをインストールしてください。

dnf -y install python3 python3-devel python3-pip nginx gcc
pip3 install uwsgi

uwsgi を起動するユーザを作成してください。

useradd myapp

アプリケーションフォルダ、ログフォルダ、PIDファイル格納フォルダを作成してください。

mkdir -p /opt/myapp
mkdir -p /var/log/myapp
mkdir -p /var/run/myapp

/opt/myapp/uwsgi.ini ファイルを作成してください。

[uwsgi]
master = true
socket = 127.0.0.1:8080
processes = 5
uid = myapp
gid = myapp
chdir = /opt/myapp
wsgi-file = /opt/myapp/wsgi.py
module = wsgi:application
logto = /var/log/myapp/uwsgi.log
pidfile = /var/run/myapp/uwsgi.pid

/opt/myapp/wsgi.py ファイルを作成してください。

def application(environ, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return ["OK\n".encode("UTF-8")]

/etc/nginx/nginx.conf ファイルに下記を追記してください。

server {
    ...
    location = /favicon.ico {
        root /opt/myapp/static/img;
    }
    location /static {
        root /opt/myapp;
    }
    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:8080;
    }
}

Nginx を起動します。

/usr/sbin/nginx

uWSGI を起動します。

uwsgi --ini /opt/myapp/uwsgi.ini &

停止するには下記を実行します。

uwsgi --stop /var/run/myapp/uwsgi.pid

curl で動作確認します。

$ curl http://localhost
OK

.ini ファイルに関する詳細は下記を参照してください。

下記などはおすすめの設定です。

# X-Forwarded-For のIPアドレスをロギングする
log-x-forwarded-for = true
# 日時のフォーマットを指定する
logformat-strftime = true
logdate = %%Y-%%m-%%d %%H:%%M:%%S
# ログフォーマットを指定する
logformat = %(ftime) %(status) %(addr) %(uri)
# ファイルをtouchするとログをローテートする(uwsgiにローテートさせる場合)
touch-logrotate = /var/run/myapp/logrotate
# ファイルをtouchするとログを再オープンする(logrotateでローテートする場合)
touch-logreopen = /var/run/myapp/logreopen

WSGIインタフェース仕様

エントリポイント(application)

Webサーバーがリクエストを受信すると application() を呼び出します。第一引数は環境変数、第二引数はレスポンスを返すための関数が渡されます。

def application(environ, start_response):
    headers = [("Content-Type", "text/plain")]
    start_response("200 OK", headers)
    return ["OK\n".encode("UTF-8")]

環境変数(environ)

environ に渡される環境変数には下記などがあります。

詳細は下記を参照してください。

GETパラメータの取得

GET パラメータは下記の様に読み込みます。

import urllib.parse

params = {}
qs = urllib.parse.parse_qs(environ["QUERY_STRING"])
for key, value in qs.items():
    if len(value) == 1:
        params[key] = value[0]
    else:
        params[key] = value

POSTパラメータの取得

POST パラメータは下記の様に読み込みます。

import urllib.parse

params = {}
content_length = environ["CONTENT_LENGTH"]
wsgi_input = environ["wsgi.input"]
if content_length:
    body = wsgi_input.read(int(content_length)).decode()
    qs = urllib.parse.parse_qs(body)
    for key, value in qs.items():
        if len(value) == 1:
            params[key] = value[0]
        else:
            params[key] = value

JSONデータの取得

JSONデータは下記の様に読み込みます。

import json

params = {}
content_length = environ["CONTENT_LENGTH"]
wsgi_input = environ["wsgi.input"]
if content_length:
    body = wsgi_input.read(int(content_length)).decode()
    params = json.loads(body)

HTTPステータス(HTTP status)の応答

HTTPステータスは下記の様に返します。

def application(environ, start_response):
    headers = [("Content-Type", "text/plain")]
    start_response("200 OK", headers)
    return ["OK\n".encode("UTF-8")]

HTTPヘッダー(HTTP headers)の応答

レスポンスヘッダーは下記の様にして返します。

def application(environ, start_response):
    headers = [
        ("Content-Type", "text/html"),
        ("Content-Encoding", "gzip")
    ]
    start_response("200 OK", headers)
    return ["OK\n".encode("UTF-8")]

HTTPボディー(HTTP body)の応答

HTTPボディーは下記の様に返します。UTF-8 でエンコードするなどバイナリデータとして返却してください。

def application(environ, start_response):
    headers = [("Content-Type", "text/plain")]
    start_response("200 OK", headers)
    return ["OK\n".encode("UTF-8")]