Ubuntu 14.04でPythonアプリを提供するためにuWSGIとNginxを設定する方法

導入

このガイドでは、uWSGIによって提供されるシンプルなWSGIアプリケーションをセットアップします。より堅牢な接続処理を提供するために、Nginxウェブサーバーをアプリケーションサーバーへのリバースプロキシとして使用します。これらのコンポーネントをUbuntu 14.04サーバーにインストールして構成します。

定義と概念

いくつかの用語の明確化

まず最初に、取り組む概念に関連する混乱する用語について説明しておきましょう。これらの3つの別々の用語は、同じように見えますが実際には異なる意味を持っています:

  • WSGI: アプリケーションやフレームワークとアプリケーション/ウェブサーバーとの間の通信のための標準インターフェースを定義するPython仕様です。これは、これらのコンポーネント間の通信を一貫性を持たせ、交換可能にするために作成されました。これは基本的に、他のプロトコル上で使用できるAPIインターフェースを定義します。
  • uWSGI: Webアプリケーションやサービスの開発および展開のための完全なスタックを提供することを目指すアプリケーションサーバーコンテナ。主要なコンポーネントは、異なる言語のアプリケーションを処理できるアプリケーションサーバーです。これは、WSGI仕様で定義された方法を使用してアプリケーションと通信し、他のWebサーバーとさまざまなプロトコルで通信します。これは、従来のWebサーバーからのリクエストをアプリケーションが処理できる形式に変換する部分です。
  • uwsgi: より高機能なWebサーバーと通信するためにuWSGIサーバーによって実装された高速なバイナリプロトコル。これはワイヤープロトコルであり、輸送プロトコルではありません。これは、リクエストをuWSGIにプロキシするWebサーバーと通信するための推奨される方法です。

WSGIアプリケーションの要件

WSGI仕様は、スタックのWebサーバーとアプリケーション部分とのインターフェースを定義します。このコンテキストでは、「Webサーバー」とは、uWSGIサーバーのことを指し、これはクライアントのリクエストをアプリケーションにWSGI仕様を使用して変換する責任があります。これにより、通信が簡素化され、両側を簡単に交換できるようになり、緩やかに結合されたコンポーネントが作成されます。

Webサーバー(uWSGI)は、定義された”callable”をトリガーしてアプリケーションにリクエストを送信できる必要があります。callableは単純に、Webサーバーがいくつかのパラメータを持つ関数を呼び出すことができるアプリケーションへの入り口です。期待されるパラメータは、環境変数の辞書とWebサーバー(uWSGI)コンポーネントが提供するcallableです。

応答として、アプリケーションは、クライアント応答の本文を生成するために使用される反復可能なものを返します。また、それはパラメータとして受け取ったWebサーバーコンポーネントcallableを呼び出します。Webサーバーcallableをトリガーする際の最初のパラメータはHTTPステータスコードであり、2番目のパラメータは、クライアントに送信する応答ヘッダーと値を定義するタプルのリストです。

このインタラクションの「Webサーバー」コンポーネントがこの場合にuWSGIによって提供されるため、私たちのアプリケーションには上記の品質があることを確認するだけで済みます。また、実際のクライアントリクエストを処理し、それらをuWSGIサーバーにプロキシするためにNginxを設定します。

コンポーネントのインストール

まず、Ubuntu 14.04サーバーに必要なコンポーネントをインストールする必要があります。これは主にaptpipを使用して行います。

まず、aptパッケージインデックスを更新し、次にPython開発ライブラリとヘッダー、pip Pythonパッケージマネージャー、およびNginxウェブサーバーとリバースプロキシをインストールします。

sudo apt-get update
sudo apt-get install python-dev python-pip nginx

パッケージのインストールが完了すると、pip Pythonパッケージマネージャーにアクセスできるようになります。これを使用してvirtualenvパッケージをインストールし、アプリケーションのPython環境をシステム上に存在する他の環境から分離します。

sudo pip install virtualenv

これが完了したら、アプリケーションの一般的な構造を作成できます。上記で説明した仮想環境を作成し、その環境内にuWSGIアプリケーションサーバーをインストールします。

アプリディレクトリと仮想環境の設定

まず、アプリのためのフォルダを作成します。これには、より完全なアプリケーションで実際のアプリケーションコードが含まれるネストされたフォルダが含まれます。今回は、このディレクトリには単に仮想環境とWSGIエントリポイントが含まれます。

mkdir ~/myapp/

次に、アプリケーションの環境を設定するためにディレクトリに移動します。

cd ~/myapp

virtualenvコマンドで仮想環境を作成します。ここでは、単純化のためにmyappenvと呼びます。

virtualenv myappenv

A new Python environment will be set up under a directory called myappenv. We can activate this environment by typing:

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ステータスとリクエストヘッダーを持つウェブサーバーの呼び出し可能なものを呼び出す必要があります。

アプリケーションを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

これで、ウェブブラウザーでサーバーのIPアドレスまたはドメイン名にアクセスし、:8080を続けて入力すると、wsgi.pyファイル内のボディとして渡した最初のレベルの見出しテキストが表示されます:

これが機能することを確認したら、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が外部と通信するために使用するプロトコルを変更します。 アプリケーションをテストする際、Webブラウザからアクセスできるように--protocol=httpを指定しました。 しかし、uWSGIの前にNginxをリバースプロキシとして設定するため、これを変更する必要があります。 Nginxはuwsgiプロキシ機構を実装しており、これはuWSGIが他のサーバーと通信するために使用できる高速バイナリプロトコルです。 実際、uwsgiプロトコルはuWSGIのデフォルトのプロトコルであり、プロトコルの指定を省略することで、自動的にuwsgiにフォールバックします。

Nginxでこの構成を設計しているため、ネットワークポートの代わりにUnixソケットを使用することにします。これはより安全で高速です。相対パスを使用する場合、ソケットは現在のディレクトリに作成されます。それをmyapp.sockと呼びます。許可を「664」に変更して、Nginxが書き込みできるようにします(Nginxが使用するwww-dataグループでuWSGIを起動します)。また、プロセスが停止したときにソケットを削除するvacuumオプションを追加します。

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

アプリケーションを起動するUpstartファイルを作成するので、最後のオプションが必要です。UpstartとuWSGIは、アプリケーションに対してSIGTERMシグナルが何をするかについて異なる考えを持っています。これらのプロセスをUpstartで期待どおりに処理できるようにするために、単にdie-on-termというオプションを追加すれば問題ありません。これにより、uWSGIがプロセスを終了させるようになります。

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

編集が完了したら、ファイルを保存して閉じてください。この構成ファイルは、Upstartスクリプトとして使用するように設定されています。

アプリケーションを管理するUpstartファイルを作成する

アプリケーションが常に利用可能になるように、ブート時にuWSGIインスタンスを起動します。これをUpstartがチェックする/etc/initディレクトリに配置します。これをmyapp.confと呼びます。

sudo nano /etc/init/myapp.conf

最初に、サービスの説明を開始し、自動的に実行されるシステムランレベルを選択します。標準のユーザー実行レベルは、2から5です。Upstartに、このグループ以外のランレベル(システムが再起動中である場合やシングルユーザーモードの場合など)でサービスを停止するように指示します。

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

次に、Upstartにプロセスを実行するユーザーとグループについて伝えます。このガイドでは、独自のユーザーを使用していますが(ここではdemoを使用しています)、アプリケーションを自分のアカウントで実行したいと考えています。ただし、Nginxが使用するwww-dataユーザーにグループを設定したいと思います。これは、Webサーバーが.iniファイルが作成するソケットに読み書きできる必要があるためです。

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

次に、実際のuWSGIを起動するコマンドを実行します。uWSGIを仮想環境にインストールしたため、追加の作業が必要です。uWSGI実行可能ファイルへの完全なパスを提供することもできますが、代わりに仮想環境をアクティブにします。これにより、環境にインストールされた追加のソフトウェアに依存している場合に便利になります。

これを行うには、scriptブロックを使用します。その中で、アプリケーションディレクトリに移動し、仮想環境をアクティブにし(スクリプトではsourceの代わりに.を使用する必要があります)、.iniファイルを指してuWSGIインスタンスを起動します。

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

script
    cd /home/demo/myapp
    . myappenv/bin/activate
    uwsgi --ini myapp.ini
end script

これで、Upstartスクリプトが完成しました。完了したら、ファイルを保存して閉じてください。

これで、次のように入力してサービスを開始できます:

sudo start myapp

それが開始されたことを確認するには、次のように入力できます:

ps aux | grep myapp
demo   14618  0.0  0.5  35868  5996 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14619  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14620  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14621  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14622  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14623  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   15520  0.0  0.0  11740   936 pts/0    S+   15:53   0:00 grep --color=auto myapp

これは起動時に自動的に開始されます。いつでもサービスを停止することができます。:

sudo stop myapp

NginxをuWSGIにプロキシするように構成します

この時点で、WSGIアプリがあり、uWSGIがそれを読み取って提供できることを確認しました。構成ファイルとUpstartスクリプトを作成しました。uWSGIプロセスはソケットでリスンし、uwsgiプロトコルを使用して通信します。

これで、Nginxをリバースプロキシとして構成できる段階に来ました。Nginxは、uWSGIと通信するためにuwsgiプロトコルを使用してプロキシする能力を持っています。これはHTTPよりも速いプロトコルであり、パフォーマンスが向上します。

設定するNginxの構成は非常にシンプルです。Nginxの構成階層内のsites-availableディレクトリに新しいファイルを作成します。ファイル名は、使用しているアプリ名に合わせてmyappとします。

sudo nano /etc/nginx/sites-available/myapp

このファイルでは、このサーバーブロックが応答するポート番号とドメイン名を指定できます。この場合、デフォルトのポート80を使用します。

server {
    listen 80;
    server_name server_domain_or_IP;
}

このドメインまたはIPアドレスのすべてのリクエストをWSGIアプリケーションに送信するため、/で始まるリクエストに対応する単一の場所ブロックを作成します。その内部では、Nginxの設定ディレクトリ内のファイルから合理的なデフォルトのパラメータを含むためにincludeディレクティブを使用します。これらを含むファイルはuwsgi_paramsと呼ばれます。その後、トラフィックを以前に設定したunixソケットを介してuWSGIインスタンスに渡します:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

実際には、これだけでシンプルなアプリケーションに必要なすべてです。より完全なアプリケーションのためにいくつかの改善ができます。たとえば、このブロックの外部で複数の上流uWSGIサーバーを定義し、それらを渡すことができます。さらにuWSGIパラメータを含めることもできます。また、Nginxから直接静的ファイルを処理し、動的なリクエストのみをuWSGIインスタンスに渡すこともできます。

しかし、私たちの3行のアプリではこれらの機能は必要ありませんので、ファイルを保存して閉じることができます。

作成したサーバーの設定をsites-enabledディレクトリにリンクして有効にします:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

構成ファイルの構文エラーを確認します:

sudo service nginx configtest

問題が検出されない場合は、サーバーを再起動して変更を実装します:

sudo service nginx restart

Nginxが再起動すると、構成したアプリケーションを表示するために、サーバーのドメイン名またはIPアドレス(ポート番号なし)にアクセスできるはずです。

結論

これまでに、シンプルなWSGIアプリケーションを作成し、より複雑なアプリケーションの設計方法についていくつかの洞察を得ました。uWSGIアプリケーションコンテナ/サーバーを専用の仮想環境にインストールして、アプリケーションを提供しました。このプロセスを自動化するために、設定ファイルとUpstartスクリプトを作成しました。uWSGIサーバーの前に、uwsgiワイヤープロトコルを使用してuWSGIプロセスと通信できるNginxリバースプロキシを設定しました。

実際の本番環境をセットアップする際に、これを拡張できることがわかります。たとえば、uWSGIは「emperor mode」と呼ばれるものを使用して複数のアプリケーションを管理する機能があります。Nginxの構成を拡張して、uWSGIインスタンス間で負荷分散を行ったり、アプリケーションの静的ファイルを処理したりできます。複数のアプリケーションを提供する場合は、必要に応じて仮想環境ではなくグローバルにuWSGIをインストールすることが最善の選択肢になる場合があります。これらのコンポーネントはすべてかなり柔軟であり、さまざまなシナリオに対応するために構成を調整できるはずです。

Source:
https://www.digitalocean.com/community/tutorials/how-to-set-up-uwsgi-and-nginx-to-serve-python-apps-on-ubuntu-14-04