24
41

More than 5 years have passed since last update.

nginx + Python でウェブサービスを(CentOS編)

Posted at

はじめに

nginxにPythonが動作する環境を作ります。つまりPythonでウェブサービスを作る基盤を作ります。Django・Flask・Bottleといったフレームワークは使いません。

本稿はCentOSを対象としています。Raspbianをご利用の場合は ラズパイ編 もご覧ください。

執筆時の「さくらのVPS」のCentOSのバージョンは7.5でした。

# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)

必要なものをインストール

nginxとPython、そして両者を繋ぐ役割を担うuwsgiをインストールします。

# yum update

# yum -y install nginx

# yum search python3
# yum -y install python34-devel
# yum -y install python34-setuptools
# easy_install-3.4 pip

# pip3 install uwsgi

各バージョンは次のようになりました。

# nginx -v
nginx version: nginx/1.12.2
# python3 --version
Python 3.4.8
# uwsgi --version
2.0.17

uwsgi 設定

必要なディレクトリを作成します。

# mkdir -p /etc/uwsgi/vassals

uwsgiの設定をemperor.iniに記述します。

/etc/uwsgi/emperor.ini
[uwsgi]
emperor = /etc/uwsgi/vassals
uid = nginx
gid = nginx
logto = /var/log/uwsgi/uwsgi.log
touch-logreopen = /var/log/uwsgi/touch-logreopen
master = true
vacuum = true
ignore-sigpipe = true
ignore-write-errors = true
disable-write-exception = true

上記のignore-sigpipe ignore-write-errors disable-write-exceptionは、/var/log/uwsgi/uwsgi.logに頻繁に現れるOSErrorを消すのに有効でした。

続いてバーチャルホストごとの設定を vassals/ホスト名.ini に記述します。原理的にはこの設定ファイルをバーチャルホストの数だけ用意する必要があります。しかしuwsgiには「設定ファイル内で%nと記述することで自身のファイル名を参照できる」という便利な機能があるため、個々のファイル内に具体的なホスト名を記述する必要がなく、実質的にはテンプレートを1個だけ作り、これにホスト名のシンボリックリンクを張ることで設定ファイルを容易に量産できます。まずはそのテンプレートをvhosts.iniとして作成します。

/etc/uwsgi/vhosts.ini
[uwsgi]
chdir = /home/app/%n/wsgi
module = wsgi:application
socket = /var/run/uwsgi/%n.sock
chmod-socket = 644
reload-mercy = 1
processes = %k
die-on-term = true
py-autoreload = 1
enable-threads = true
threads = 8

上記のpy-autoreloadは、Pythonスクリプトの内容を変更する度にuwsgiを再起動する必要を無くすための便利な設定です。またenable-threadsおよびthreadsは、nginxの受信した複数のリクエストを同時に処理させるために必要な設定です。この設定がないとuwsgiは先のリクエストを処理し終わるまで次のリクエストの処理を始めてくれません。

上記のテンプレートにホスト名のシンボリックリンクを作成します。こちらの例ではexample.comとしています。これをバーチャルホストの数だけこれを作成します。

# cd /etc/uwsgi/vassals
# ln -s ../vhosts.ini example.com.ini

uwsgiのサービス化

uwsgiが常に動作し続けるようにsystemdでサービス化します。

/etc/systemd/system/uwsgi.service
[Unit]
Description=uWSGI Emperor
After=syslog.target

[Service]
ExecStartPre=-/bin/mkdir -p /var/log/uwsgi
ExecStartPre=-/bin/chown -R nginx:nginx /var/log/uwsgi
ExecStartPre=-/bin/mkdir -p /var/run/uwsgi
ExecStartPre=-/bin/chown -R nginx:nginx /var/run/uwsgi
ExecStart=/usr/bin/uwsgi --ini /etc/uwsgi/emperor.ini
RuntimeDirectory=uwsgi
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

uwsgiを起動させます。startで起動したことをstatusで確認し、今後は自動的に起動されるようにenableを指定します。

# systemctl start uwsgi
# systemctl status uwsgi
# systemctl enable uwsgi

uwsgiの循環ログ

uwsgiのログが際限なく巨大化してしまうことをlogrotateによって予防しておきます。

/etc/logrotate.d/uwsgi
/var/log/uwsgi/uwsgi.log {
    compress
    daily
    missingok
    notifempty
    rotate 7
    sharedscripts
    size 1M
    postrotate
        touch /var/log/uwsgi/touch-logreopen
    endscript
}

nginx 設定

バーチャルドメインexample.comの設定例です。

/etc/nginx/conf.d/example.com.conf
server {
    listen 443 ssl;
    server_name example.com;
    root /home/app/example.com;
    location / {
        try_files /resource$uri @uwsgi;
    }
    location @uwsgi {
        include uwsgi_params;
        uwsgi_pass unix:///var/run/uwsgi/example.com.sock;
        uwsgi_connect_timeout 600s;
        uwsgi_read_timeout 600s;
    }
    ssl_certificate     /etc/nginx/ssl/self_signed.crt;
    ssl_certificate_key /etc/nginx/ssl/self_signed.key;
}

try_filesの行には、リクエストされたファイルが/home/app/example.com/resource内に存在すればこれを直接出力し、実在しなければ代わりにuwsgiのPythonへ処理を繋ぐようにする指示です。したがって例えば画像ファイルなどはresourceに配置しておきます。

uwsgi_connect_timeoutuwsgi_read_timeout/var/log/nginx/error.logに頻出していたupstream timed outを消すのに効果がありました。

オレオレ証明書

前述のexample.com.confに記述したSSLの証明書を作成します。本題から逸れる内容なので本稿ではオレオレ証明書で済ませますが、実際の運用に際してはLet's Encryptなどで証明書を得るのが良いでしょう。

# mkdir /etc/nginx/ssl
# chmod 700 /etc/nginx/ssl
# cd /etc/nginx/ssl
# openssl genrsa 2048 > self_signed.key
# openssl req -new -key self_signed.key > self_signed.csr
---- 必要情報を入力 ----

# openssl x509 -days 3650 -req -signkey self_signed.key < self_signed.csr > self_signed.crt
# chmod 600 /etc/nginx/ssl/*

Python配置

example.comのドキュメントルートとなるディレクトリを作成して、動作試験用のPythonスクリプトを配置します。

# su app
$ chmod 711 /home/app
$ mkdir -p /home/app/example.com/resource
$ mkdir -p /home/app/example.com/wsgi

上の例ではユーザーappのホームにバーチャルホスト名のディレクトリを作成していますが、/etc/nginx/conf.d/example.com.confrootと一致していれば、全く別のディレクトリでもかまいません。nginxとuwsgiからも参照できるようにパーミッションを変更します。

/home/app/example.com/wsgi/wsgi.py
# -*- coding: utf-8 -*-

def application(environ, start_response):
    try:
        body = {k + ': ' + str(v) for k, v in environ.items()}
        start_response('200 OK', [('Content-type', 'text/plain')])
        return ['\n'.join(body).encode()]
    except:
        import traceback
        start_response('500 Internal Server Error', [('Content-type', 'text/plain')])
        return [traceback.format_exc().encode()]

このスクリプトの保存ディレクトリ/home/app/example.com/wsgi/と、ファイル名のwsgi、および関数名のapplicationは、先のuwsgiの設定のvhosts.iniの中で指定した文字列と一致させています。言い換えると、これらに一貫性のある変更を加えれば別の文字列にも変更できます。

nginx 起動

startで起動したことをstatusで確認し、今後は自動的に起動されるようにenableを指定します。

# systemctl start nginx
# systemctl status nginx
# systemctl enable nginx

動作確認

確認前に念のため nginx と uswgi を再起動します。

# systemctl restart nginx
# systemctl restart uwsgi

ブラウザでhttps://example.com/へアクセスします。オレオレ証明書を使用しているためブラウザが警告を出しますが、ページの表示を強行します。wsgi.py が出力する環境変数の一覧がブラウザに表示されれば成功です。

結果が好ましくない場合は/var/log/nginx/error.logおよび/var/log/uwsgi/uwsgi.logに記されたエラーの内容から解決の糸口が得られるかもしれません。

終わりに

本稿は各項の詳細を深く理解して合理的に導いた結果ではなく、ネットに散在する情報の断片を元に試行錯誤を重ねて経験的に導いた現時点での解決策を紹介しています。誤りや改善点ご指摘いただくと助かります。

本稿と同様の内容の ラズパイ編 もご覧ください。

24
41
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
41