CentOS7でPythonアプリを提供するようにuWSGIとNginxを設定する方法
序章
このガイドでは、uWSGIが提供する簡単なWSGIアプリケーションをセットアップします。 Nginx Webサーバーをアプリケーションサーバーへのリバースプロキシとして使用して、より堅牢な接続処理を提供します。 これらのコンポーネントをCentOS7サーバーにインストールして構成します。
定義と概念
いくつかの用語を明確にする
始める前に、扱う相互に関連する概念に関連するいくつかの紛らわしい用語に対処する必要があります。 これらの3つの別個の用語は互換性があるように見えますが、実際には異なる意味を持っています。
- WSGI :アプリケーションまたはフレームワークとアプリケーション/Webサーバー間の通信用の標準インターフェースを定義するPython仕様。 これは、一貫性と互換性のためにこれらのコンポーネント間の通信を簡素化および標準化するために作成されました。 これは基本的に、他のプロトコルで使用できるAPIインターフェースを定義します。
- uWSGI :Webアプリケーションとサービスを開発および展開するためのフルスタックを提供することを目的としたアプリケーションサーバーコンテナー。 主要なコンポーネントは、さまざまな言語のアプリを処理できるアプリケーションサーバーです。 WSGI仕様で定義された方法を使用してアプリケーションと通信し、他のさまざまなプロトコルを介して他のWebサーバーと通信します。 これは、従来のWebサーバーからの要求をアプリケーションが処理できる形式に変換する部分です。
- uwsgi :よりフル機能のWebサーバーと通信するためにuWSGIサーバーによって実装される高速のバイナリプロトコル。 これはワイヤープロトコルであり、トランスポートプロトコルではありません。 これは、uWSGIへの要求をプロキシしているWebサーバーと通信するための推奨される方法です。
WSGIアプリケーションの要件
WSGI仕様は、Webサーバーとスタックのアプリケーション部分の間のインターフェイスを定義します。 このコンテキストでは、「Webサーバー」とは、WSGI仕様を使用してクライアント要求をアプリケーションに変換するuWSGIサーバーを指します。 これにより、通信が簡素化され、緩く結合されたコンポーネントが作成されるため、どちらの側も問題なく簡単に交換できます。
Webサーバー(uWSGI)は、定義された「呼び出し可能」をトリガーすることにより、アプリケーションに要求を送信する機能を備えている必要があります。 呼び出し可能オブジェクトは、Webサーバーがいくつかのパラメーターを使用して関数を呼び出すことができるアプリケーションへの単なるエントリーポイントです。 必要なパラメーターは、環境変数のディクショナリと、Webサーバー(uWSGI)コンポーネントによって提供される呼び出し可能パラメーターです。
応答として、アプリケーションは、クライアント応答の本文を生成するために使用される反復可能オブジェクトを返します。 また、パラメーターとして受け取った呼び出し可能なWebサーバーコンポーネントを呼び出します。 呼び出し可能なWebサーバーをトリガーするときの最初のパラメーターはHTTPステータス・コードであり、2番目はタプルのリストであり、それぞれがクライアントに送り返す応答ヘッダーと値を定義します。
この場合、uWSGIによって提供されるこのインタラクションの「Webサーバー」コンポーネントを使用すると、アプリケーションが上記の品質を備えていることを確認するだけで済みます。 また、実際のクライアントリクエストを処理し、それらをuWSGIサーバーにプロキシするようにNginxを設定します。
コンポーネントをインストールします
開始するには、CentOS7サーバーに必要なコンポーネントをインストールする必要があります。 これは主にyum
とpip
を使用して実行できます。
まず、より広範囲のパッケージにアクセスできるように、EPELリポジトリをインストールする必要があります。 これは、次のように入力することで、1つのyum
コマンドで簡単に実行できます。
sudo yum install epel-release
これで、コンポーネントをインストールできます。 Python開発ライブラリとヘッダー、pip
Pythonパッケージマネージャー、およびNginxWebサーバーとリバースプロキシを取得する必要があります。 また、uWSGIバイナリを一時的にビルドするためのコンパイラも必要になります。
sudo yum install python-pip python-devel nginx gcc
パッケージのインストールが完了すると、pip
Pythonパッケージマネージャーにアクセスできるようになります。 これを使用してvirtualenv
パッケージをインストールできます。これを使用して、アプリケーションのPython環境をシステム上に存在する可能性のある他の環境から分離します。
sudo pip install virtualenv
これが完了すると、アプリケーションの一般的な構造の作成を開始できます。 上記で説明した仮想環境を作成し、この環境内にuWSGIアプリケーションサーバーをインストールします。
AppDirectoryとVirtualenvを設定します
まず、アプリ用のフォルダーを作成します。 これにより、より完全なアプリケーションで実際のアプリケーションコードを含むネストされたフォルダーを保持できます。 私たちの目的のために、このディレクトリは単に仮想環境とWSGIエントリポイントを保持します。
mkdir ~/myapp/
次に、ディレクトリに移動して、アプリケーションの環境を設定できるようにします。
cd ~/myapp
virtualenv
コマンドを使用して仮想環境を作成します。 簡単にするために、これをmyappenv
と呼びます。
virtualenv myappenv
myappenv
というディレクトリの下に新しいPython環境がセットアップされます。 次のように入力して、この環境をアクティブ化できます。
source myappenv/bin/activate
プロンプトが変化して、仮想環境内で操作していることを示します。 次のようになります。
(myappenv)username@host:~/my_app$
いつでもこの環境を離れたい場合は、次のように入力するだけです。
deactivate
環境を非アクティブ化した場合は、再度アクティブ化してガイドを続行してください。
この環境がアクティブな場合、インストールされているPythonパッケージはすべてこのディレクトリ階層に含まれます。 システムのPython環境に干渉することはありません。 これを念頭に置いて、pip
を使用してuWSGIサーバーを環境にインストールできるようになりました。 このためのパッケージはuwsgi
と呼ばれます(これはまだuWSGIサーバーであり、uwsgi
プロトコルではありません):
pip install uwsgi
次のように入力して、使用可能になったことを確認できます。
uwsgi --version
バージョン番号が返される場合は、uWSGIサーバーを使用できます。
WSGIアプリケーションを作成する
次に、前に説明したWSGI仕様の要件を使用して、非常に単純なWSGIアプリケーションを作成します。 繰り返しになりますが、提供する必要のあるアプリケーションコンポーネントには、次のプロパティが必要です。
- 呼び出し可能オブジェクト(呼び出すことができる関数または他の言語構造)を介してインターフェースを提供する必要があります
- 呼び出し可能オブジェクトは、環境変数のようなキーと値のペアを含むディクショナリと、サーバー(uWSGI)でアクセス可能な呼び出し可能オブジェクトをパラメーターとして受け取る必要があります。
- アプリケーションの呼び出し可能オブジェクトは、クライアントに送信する本文を生成する反復可能オブジェクトを返す必要があります。
- アプリケーションは、HTTPステータスとリクエストヘッダーを使用してWebサーバーの呼び出し可能オブジェクトを呼び出す必要があります。
アプリケーションディレクトリのwsgi.py
というファイルにアプリケーションを書き込みます。
nano ~/myapp/wsgi.py
このファイル内に、可能な限り最も単純なWSGI準拠のアプリケーションを作成します。 すべてのPythonコードと同様に、インデントに注意を払うようにしてください。
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return ["<h1 style='color:blue'>Hello There!</h1>"]
上記のコードは、完全なWSGIアプリケーションを構成します。 デフォルトでは、uWSGIはapplication
という呼び出し可能オブジェクトを検索します。そのため、関数application
を呼び出しました。 ご覧のとおり、2つのパラメーターが必要です。
最初にenviron
と呼んだのは、キー値辞書のような環境変数になるからです。 2つ目はstart_response
と呼ばれ、送信されるWebサーバー(uWSGI)呼び出し可能オブジェクトを参照するためにアプリが内部で使用する名前です。 これらのパラメーター名は両方とも、WSGIの相互作用を定義する PEP 333 仕様の例で使用されているため、単純に選択されています。
アプリケーションはこの情報を取得し、2つのことを実行する必要があります。 まず、HTTPステータスコードと送り返したいヘッダーを使用して、受信した呼び出し可能オブジェクトを呼び出す必要があります。 この場合、「200 OK」応答を送信し、Content-Type
ヘッダーをtext/html
に設定しています。
第二に、それは応答本体として使用するために反復可能で戻る必要があります。 ここでは、HTMLの単一の文字列を含むリストを使用しました。 文字列も反復可能ですが、リスト内では、uWSGIは1回の反復で文字列全体を処理できます。
実際のシナリオでは、このファイルは残りのアプリケーションコードへのリンクとして使用される可能性があります。 たとえば、Djangoプロジェクトには、デフォルトでwsgi.py
ファイルが含まれており、Webサーバー(uWSGI)からアプリケーション(Django)にリクエストを変換します。 簡略化されたWSGIインターフェイスは、実際のアプリケーションコードがどれほど複雑であっても同じです。 これは、インターフェイスの長所の1つです。
終了したら、ファイルを保存して閉じます。
コードをテストするために、uWSGIを起動できます。 とりあえずHTTPを使用し、ポート8080
でリッスンするように指示します。 スクリプトの名前を渡します(サフィックスは削除されています)。
uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi
ここで、WebブラウザでサーバーのIPアドレスまたはドメイン名にアクセスし、続いて:8080
にアクセスすると、wsgi.py
ファイルに本文として渡された第1レベルのヘッダーテキストが表示されます。
これが機能することを確認したら、CTRL-Cでサーバーを停止します。
この時点で、実際のアプリケーションの設計は完了です。 必要に応じて、仮想環境を非アクティブ化できます。
deactivate
uWSGI構成ファイルを構成する
上記の例では、uWSGIサーバーを手動で起動し、コマンドラインでいくつかのパラメーターを渡しました。 これを回避するには、構成ファイルを作成します。 uWSGIサーバーはさまざまな形式の構成を読み取ることができますが、簡単にするために.ini
形式を使用します。
これまで使用してきた命名を続けるために、ファイルmyapp.ini
を呼び出して、アプリケーションフォルダーに配置します。
nano ~/myapp/myapp.ini
内部に、[uwsgi]
というセクションを作成する必要があります。 このセクションには、すべての構成アイテムがあります。 まず、アプリケーションを特定します。 uWSGIサーバーは、アプリケーションの呼び出し可能ファイルがどこにあるかを知る必要があります。 次の範囲内でファイルと関数を指定できます。
[uwsgi] module = wsgi:application
最初のuwsgi
プロセスをマスターとしてマークしてから、いくつかのワーカープロセスを生成します。 5人の労働者から始めます:
[uwsgi] module = wsgi:application master = true processes = 5
実際には、uWSGIが外界と話すために使用するプロトコルを変更します。 アプリケーションをテストするときに、--protocol=http
を指定して、Webブラウザーから表示できるようにしました。 nginxをuWSGIの前でリバースプロキシとして構成するため、これを変更できます。 Nginxは、uwsgi
プロキシメカニズムを実装しています。これは、uWSGIが他のサーバーと通信するために使用できる高速バイナリプロトコルです。 uwsgi
プロトコルは実際にはuWSGIのデフォルトプロトコルであるため、プロトコル仕様を省略するだけで、uwsgi
にフォールバックします。
この構成はNginxで使用するように設計しているため、ネットワークポートの使用から変更し、代わりにUnixソケットを使用します。 これはより安全で高速です。
uwsgi
サーバーを実行し、ソケットファイルを所有するために、独自のユーザー名を指定します。 /run
の下にディレクトリを作成して、uWSGIとNginxの両方がアクセスできるようにソケットファイルを配置します。 ソケット自体をmyapp.sock
と呼びます。 Nginxが書き込みできるように、権限を「664」に変更します(Nginxが使用するwww-data
グループでuWSGIを起動します。 vacuum
オプションも追加します。これにより、プロセスが停止したときにソケットが削除されます。
[uwsgi] module = wsgi:application master = true processes = 5 uid = user socket = /run/uwsgi/myapp.sock chown-socket = user:nginx chmod-socket = 660 vacuum = true
起動時にアプリケーションを開始するためのsystemdファイルを作成するため、最後のオプションが1つ必要です。 SystemdとuWSGIは、SIGTERMシグナルがアプリケーションに対して何をすべきかについて異なる考えを持っています。 Systemdでプロセスを期待どおりに処理できるようにこの不一致を整理するには、die-on-term
というオプションを追加するだけで、uWSGIがプロセスをリロードする代わりに強制終了します。
[uwsgi] module = wsgi:application master = true processes = 5 uid = user socket = /run/uwsgi/myapp.sock chown-socket = user:nginx chmod-socket = 660 vacuum = true die-on-term = true
終了したら、ファイルを保存して閉じます。 この構成ファイルは、Upstartスクリプトで使用するように設定されています。
アプリを管理するためのSystemdユニットファイルを作成する
起動時にuWSGIインスタンスを起動して、アプリケーションを常に利用できるようにすることができます。 これを行うために、systemdユニットファイルを作成できます。 これを/etc/systemd/system
ディレクトリに配置します。これは、ユーザーが作成したユニットファイルに最適な場所です。 ユニットファイルをuwsgi.service
と呼びます。
sudo nano /etc/systemd/system/uwsgi.service
まず、[Unit]
セクションから始めます。ここで、メタデータのペースを調整できます。 ここに記載するのは、サービスの説明だけです。
[Unit] Description=uWSGI instance to serve myapp
次に、[Service]
セクションを開きます。 仮想環境を使用しているため、サービス開始コマンドは従来よりも複雑になります。 ExecStartPre
コマンドを使用して、ソケットディレクトリが作成され、正しい関係者によって所有されていることを確認します。 すでに設定されている場合、これは失敗することが許可されます(等号の後に-
を置くことによって)。 これは、bash
への単一の呼び出しに渡されます。
uWSGIを起動する実際のExecStart
コマンドについては、実際のコマンドもbash
に渡します。 このディレクティブで実行できるコマンドは1つ(この場合はbash
)のみであるため、これにより、いくつかの異なるコマンドを実行できます。 これを使用して、アプリケーションディレクトリに移動し、仮想環境をアクティブにして、作成した.ini
ファイルでuWSGIを起動します。
[Unit] Description=uWSGI instance to serve myapp [Service] ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown user:nginx /run/uwsgi' ExecStart=/usr/bin/bash -c 'cd /home/user/myapp; source myappenv/bin/activate; uwsgi --ini myapp.ini'
あとは、[Install]
セクションを作成するだけです。 これにより、ユニットをenable
したときに何が起こるかが決まります。 基本的に、ユニットが自動起動する状態を指定します。 有効にすると、サーバーがマルチユーザーモードのときにこのユニットが起動するように指定します。
[Unit] Description=uWSGI instance to serve myapp [Service] ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown user:nginx /run/uwsgi' ExecStart=/usr/bin/bash -c 'cd /home/user/myapp; source myappenv/bin/activate; uwsgi --ini myapp.ini' [Install] WantedBy=multi-user.target
上記の設定を書き留めたら、ファイルを保存して閉じます。
これで、次のように入力してサービスを開始できます。
sudo systemctl start uwsgi
次のように入力して、問題なく起動したことを確認します。
systemctl status uwsgi
エラーがなかった場合は、次のように入力して、起動時にサービスが開始されるようにサービスを有効にします。
sudo systemctl enable uwsgi
次のように入力すると、いつでもサービスを停止できます。
sudo systemctl stop uwsgi
nginxをuWSGIにプロキシするように構成する
この時点で、WSGIアプリがあり、uWSGIがそれを読み取って提供できることを確認しました。 設定ファイルとSystemdユニットファイルを作成しました。 uWSGIプロセスはソケットをリッスンし、uwsgi
プロトコルを使用して通信します。
これで、Nginxをリバースプロキシとして構成できるようになりました。 Nginxには、uWSGIと通信するためのuwsgi
プロトコルを使用してプロキシする機能があります。 これはHTTPよりも高速なプロトコルであり、パフォーマンスが向上します。
セットアップするNginxの構成は非常に簡単です。 既存のnginx.conf
ファイルを変更し、新しいサーバーブロックを追加します。 sudo
でファイルを開いて編集します。
sudo nano /etc/nginx/nginx.conf
デフォルトのサーバーブロックの前に、独自のサーバーブロックを追加します。
http { . . . include /etc/nginx/conf.d/*.conf; server { } server { listen 80 default_server; server_name localhost; . . .
作成したブロックは、uWSGIプロキシの構成を保持します。 以下の残りの構成アイテムは、このブロック内に配置されます。 サーバーブロックはポート80でリッスンし、サーバーのドメイン名またはIPアドレスに応答する必要があります。
server { listen 80; server_name server_domain_or_IP; }
その後、すべてのリクエストを処理する単一のロケーションブロックを開くことができます。 このブロック内に、/etc/nginx/uwsgi_params
ファイルにあるuwsgi
パラメーターを含め、uWSGIがリッスンしているソケットにトラフィックを渡します。
server { listen 80; server_name server_domain_or_IP; location / { include uwsgi_params; uwsgi_pass unix:/run/uwsgi/myapp.sock; } }
実際、単純なアプリケーションに必要なのはこれだけです。 より完全なアプリケーションのために行うことができるいくつかの改善があります。 たとえば、このブロックの外側にいくつかのアップストリームuWSGIサーバーを定義し、それらをそこに渡すことができます。 さらにいくつかのuWSGIパラメータを含めることができます。 また、Nginxからの静的ファイルを直接処理し、動的リクエストのみをuWSGIインスタンスに渡す場合もあります。
ただし、3行のアプリではこれらの機能は必要ないため、ファイルを保存して閉じることができます。
次のように入力して、Nginx構成が有効であることを確認するためにテストできます。
sudo nginx -t
これがエラーなしで返される場合は、次のように入力してサービスを開始します。
sudo systemctl start nginx
サービスを有効にして、起動時にNginxを起動します。
sudo systemctl enable nginx
サーバーのドメイン名またはIPアドレス(ポート番号なし)に移動して、構成したアプリケーションを確認できるはずです。
結論
ここまで進んだら、単純なWSGIアプリケーションを作成し、より複雑なアプリケーションをどのように設計する必要があるかについての洞察を得ることができます。 uWSGIアプリケーションコンテナ/サーバーを専用の仮想環境にインストールして、アプリケーションを提供します。 このプロセスを自動化するために、構成ファイルとSystemdユニットファイルを作成しました。 uWSGIサーバーの前に、uwsgi
ワイヤープロトコルを使用してuWSGIプロセスと通信できるNginxリバースプロキシをセットアップしました。
実際の実稼働環境をセットアップするときに、これをどのように拡張できるかを簡単に確認できます。 たとえば、uWSGIには、「エンペラーモード」と呼ばれるものを使用して複数のアプリケーションを管理する機能があります。 Nginx構成を拡張して、uWSGIインスタンス間の負荷分散を行ったり、アプリケーションの静的ファイルを処理したりできます。 複数のアプリケーションにサービスを提供する場合、ニーズによっては、仮想環境ではなくグローバルにuWSGIをインストールすることが最も重要な場合があります。 コンポーネントはすべてかなり柔軟なので、さまざまなシナリオに対応するように構成を微調整できるはずです。