Python
python3
centos7
nginx-unit

【4/25更新】CentOS7上でNGINX Unitを用意してPython3のアプリを動かす

背景

PythonでFlaskやBottleを使って個人的に利用するWEBアプリケーションを作っているのですが、実行環境はuWSGIとか使わずに,FlaskやBottleの組み込みサーバーで動かしている事がほとんどです.十分それで満足しているのですが.すごく簡単にWEBアプリケーションを実行する環境が用意できるというNGINX Unitが気になっていました.

そして,最近NGINX Unitが2018年4月13日に正式バージョン(記事)になったので,いい機会だと思って軽い気持ちで触ってみたところ,いろいろ大変な思いをしたので,ここにできる限り誰でもできるように記しておきます.

もし,これからNGINX Unitをサクッと試したいと思っているのであれば,Ubuntuで試すと幸せになれます.なんと言っても公式の用意したリポジトリからPython3系が実行できるモジュールをaptでインストールすることができます.

CentOS7のために用意された公式リポジトリから簡単にNGINX UnitとPython実行モジュールを導入することはできるのですが・・・・用意されているモジュールはPython2.7用です.

なので,CentOS7でyum install pythonでインストールされるpython2.7で事足りるのであれば,この記事を読む必要はありません.公式ドキュメントやCentOS7でNGINX Unitを試している記事を読めばすべて解決します!

NGINX Unitはについては以下を参考にしてください.

やる事

  1. Python3のインストール
  2. venvの用意 ※必要であれば
  3. 最新のNGINX Unitを用意 ※この時にPython3系のvenv関係のバグ(2018/4/21時点)を修正 4/25 修正されました! (Support for PEP 405 virtual environments. )
  4. NGINX UnitとCentOS向けには用意されていないPython3系のモジュールをコンパイル
  5. Flaskを使ったサンプルプログラムの準備
  6. NGINX Unitの起動
  7. NGINX UnitへPython製Webアプリケーションの設定ファイルの準備
  8. NGINX Unitへ設定ファイルを設定
  9. 評価
  10. まとめ

環境

OS
$ cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

この記事を書くために用意した環境は,CentOS7をminimumインストールし,以下のコマンドが実行されています.

初期設定
$ sudo yum update  # 最新状態へ
$ sudo yum groupinstall "Development Tools" "Base" # NGINX Unitのコンパイル環境の準備
$ sudo yum install python python-devel # Python2.7も一応用意

公式ドキュメントではコンパイルに必要なパッケージはyum install make gccで揃うみたいです.

Python3系のインストール

通常のままではyumからPython3系をインストールすることができません.
なので,epel-releaseを入れます.

epel-releaseのインストール
$ sudo yum install epel-release

今回はpython3.6をインストールします.
また,この時にNGINX UnitでCentOS向けには用意されていないモジュールを後で生成する必要があるのでpython36-develも入れておきます.

Python3.6のインストール
$ sudo yum install python36 python36-devel

venvの用意

必要であればvenvを用意してください.

venvの用意
$ python36 -m venv forApp

# venvの有効化
$ source forApp/bin/activate

# pythonの利用
(forApp) $ python

# pipの利用
(forApp) $ pip install ****

# venvの無効化
(forApp) $ deactivate

最新のNGINX Unitの準備

NGINX UnitのGitHubからcloneします

nginx_unit_git_clone
$ git clone https://github.com/nginx/unit.git

ここに書いていたPython3.3以上のvenvのバグに対する修正は,4/25修正されたので消しました. (Support for PEP 405 virtual environments. )

NGINX UnitとPythonモジュール(2.7と3.6)のコンパイル

configureの実行

configure
$ cd unit
$ ./configure

python2.7モジュールのconfigure(必要であれば

configure_python2
$ ./configure python
configuring Python module
checking for Python ... found
checking for Python version ... 2.7.5
 + Python module: python.unit.so

python3.6モジュールのconfigure

configure_python3
$ ./configure python --config=/usr/bin/python3.6-config
configuring Python module
checking for Python ... found
checking for Python version ... 3.6.3
 + Python module: python3.6.unit.so

※ 他のモジュールが必要であれば公式ドキュメントを読んで用意してください

コンパイル!

make_all
$ make all

インストール !

make_install
$ make install DESTDIR=./install/

※ DESTDIRを指定しないとbuildとbuildがダブってエラーします

Flaskを使ったプログラムの用意

Flaskの用意

yumを使ってインストール

python-flaskパッケージのインストール
$ sudo yum install python-flask

venvにpipを使ってインストール

venvにFlaskのインストール
$ source ~/forApp/bin/activate
(forApp) $ pip install Flask

適当なプログラムを作成

main.py
from flask import Flask

application = Flask(__name__)

@application.route("/")
def hello():
        return "Hello NGINX Unit!"

動作確認

main.pyの動作確認
(forApp) $ flask run FLASK_APP=main.py
 * Serving Flask app "main"
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
# or
(forApp) $ export FLASK_APP=main.py
(forApp) $ python -m flask run
 * Serving Flask app "main"
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

# curlで確認
$ curl 127.0.0.1:5000
Hello NGINX Unit!%

NGINX Unitの起動

上から順番にやってるのであれば,ソースコードフォルダにinstallというフォルダがあると思います.
以下の構成になっているはずです.

インストール先のファイル構成
$ tree unit/install/
unit/install/
|-- build
|   |-- python3.6.unit.so # Python3.6のモジュール
|   `-- python.unit.so    # Python2.7のモジュール
`-- sbin
    `-- unitd             # NGINX Unit本体

NGINX Unitを起動してみましょう

unitdの起動
$ cd unit/install
$ ./sbin/unitd
2018/04/21 20:45:00 [info] 15699#15699 unit started

起動に成功すると以下のファイルが現れます.

unitdが生成するファイル
unit.log          # ログファイル
unit.pid          # PID
control.unit.sock # NGINX Unitコントロール用Unixソケット

これらのファイルの名前や場所はunitdを起動するときの引数で変更できます.

unitd_help
/sbin/unitd --help

unit options:

  --version            print unit version and configure options

  --no-daemon          run unit in non-daemon mode

  --control ADDRESS    set address of control API socket
                       default: "unix:control.unit.sock"

  --pid FILE           set pid filename
                       default: "unit.pid"

  --log FILE           set log filename
                       default: "unit.log"

  --modules DIRECTORY  set modules directory name
                       default: "build"

  --state DIRECTORY    set state directory name
                       default: "build"

  --user USER          set non-privileged processes to run as specified user
                       default: "nobody"

  --group GROUP        set non-privileged processes to run as specified group
                       default: user's primary group

NGINX UnitへPython製Webアプリケーションの設定

今回使用する設定は以下の通りです.

python.json
{
    "listeners": {
        "*:8080": {
            "application": "python2"
        },
        "*:8081": {
            "application": "python3"
        },
        "*:8082": {
            "application": "python3_and_venv"
        }
    },
    "applications": {
        "python2": {
            "type": "python 2.7.5",
            "processes": 2,
            "path": "/home/user",
            "module": "main"
        },
        "python3": {
            "type": "python 3.6.3",
            "processes": 2,
            "path": "/home/user",
            "module": "main"
        },
        "python3_and_venv": {
            "type": "python 3.6.3",
            "processes": 2,
            "path": "/home/user",
            "home": "/home/user/forApp",
            "module": "main"
        }
    }
}

listeners

listeners*:8080applicationsに書かれたpython2が紐づいています.他の記述も同様です.

applications

python2typeでモジュールの名前を書きます.
※ modulesに各名前はunitdを実行時のunit.logに表示されます

unit.log
2018/04/21 20:45:00 [notice] 15701#15701 module: python 2.7.5 "build/python.unit.so"
2018/04/21 20:45:00 [notice] 15701#15701 module: python 3.6.3 "build/python3.6.unit.so"

今回は上記のpython 2.7.5を書いています.

processesはworker数です.

pathはPythonプログラムが置いてある場所を書きます.

moduleにはプログラムのファイル名から.pyを除いたものを書きます.

その他の設定

homevenvのディレクトリ

working_directoryはPythonアプリケーション内で相対的なファイル参照をしているときに便利です

NGINX Unitへ設定ファイルを設定

nginx unitが生成したソケットに対してjsonをPUTすると設定が反映されます.

設定の反映
$ curl -X PUT -d@./python2.json --unix-socket ./control.unit.sock 'http://localhost/'

GETすると設定を見ることができます

設定の確認
$ curl -X GET --unix-socket ./control.unit.sock 'http://localhost/'
{
        "listeners": {
                "*:8080": {
                        "application": "python2"
                },

                "*:8081": {
                        "application": "python3"
                },

                "*:8082": {
                        "application": "python3_and_venv"
                }
        },

        "applications": {
                "python2": {
                        "type": "python 2.7.5",
                        "processes": 1,
                        "path": "/home/user",
                        "module": "main"
                },

                "python3": {
                        "type": "python 3.6.3",
                        "processes": 1,
                        "path": "/home/user",
                        "module": "main"
                },

                "python3_and_venv": {
                        "type": "python 3.6.3",
                        "processes": 1,
                        "path": "/home/user",
                        "home": "/home/user/forApp",
                        "module": "main"
                }
        }
}

DELETEすると設定を消すことができます

設定の削除
# python2の削除
$ curl -X DELETE --unix-socket ./control.unit.sock 'http://localhost/applications/python2'
$ curl -X DELETE --unix-socket ./control.unit.sock 'http://localhost/listeners/*:8080'

評価

$ curl localhost:8080
Hello NGINX Unit!
$ curl localhost:8081
Hello NGINX Unit!
$ curl localhost:8082
Hello NGINX Unit!

動いているようです!

ApacheBench(abコマンド)を使って評価します

$ sudo yum install httpd-tools

processes:1

$ ab -n 10000 -c 10 http://localhost:8080/
Server Software:        Unit/1.1
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      10
Time taken for tests:   3.990 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1520000 bytes
HTML transferred:       180000 bytes
Requests per second:    2506.26 [#/sec] (mean)
Time per request:       3.990 [ms] (mean)
Time per request:       0.399 [ms] (mean, across all concurrent requests)
Transfer rate:          372.02 [Kbytes/sec] received
$ ab -n 10000 -c 10 http://localhost:8081/
Server Software:        Unit/1.1
Server Hostname:        localhost
Server Port:            8081

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      10
Time taken for tests:   3.301 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1520000 bytes
HTML transferred:       180000 bytes
Requests per second:    3029.16 [#/sec] (mean)
Time per request:       3.301 [ms] (mean)
Time per request:       0.330 [ms] (mean, across all concurrent requests)
Transfer rate:          449.64 [Kbytes/sec] received
$ ab -n 10000 -c 10 http://localhost:8082/
Server Software:        Unit/1.1
Server Hostname:        localhost
Server Port:            8082

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      10
Time taken for tests:   3.394 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1520000 bytes
HTML transferred:       180000 bytes
Requests per second:    2946.15 [#/sec] (mean)
Time per request:       3.394 [ms] (mean)
Time per request:       0.339 [ms] (mean, across all concurrent requests)
Transfer rate:          437.32 [Kbytes/sec] received

Python2.7Python3.6Requests per secondが意外と違う!


processes:2

$ ab -n 10000 -c 10 http://localhost:8080/
Server Software:        Unit/1.1
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      10
Time taken for tests:   2.596 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1520000 bytes
HTML transferred:       180000 bytes
Requests per second:    3852.43 [#/sec] (mean)
Time per request:       2.596 [ms] (mean)
Time per request:       0.260 [ms] (mean, across all concurrent requests)
Transfer rate:          571.84 [Kbytes/sec] received
$ ab -n 10000 -c 10 http://localhost:8081/
Server Software:        Unit/1.1
Server Hostname:        localhost
Server Port:            8081

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      10
Time taken for tests:   2.400 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1520000 bytes
HTML transferred:       180000 bytes
Requests per second:    4167.18 [#/sec] (mean)
Time per request:       2.400 [ms] (mean)
Time per request:       0.240 [ms] (mean, across all concurrent requests)
Transfer rate:          618.57 [Kbytes/sec] received
$ ab -n 10000 -c 10 http://localhost:8082/
Server Software:        Unit/1.1
Server Hostname:        localhost
Server Port:            8082

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      10
Time taken for tests:   2.335 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1520000 bytes
HTML transferred:       180000 bytes
Requests per second:    4283.24 [#/sec] (mean)
Time per request:       2.335 [ms] (mean)
Time per request:       0.233 [ms] (mean, across all concurrent requests)
Transfer rate:          635.79 [Kbytes/sec] received

Requests per secondが1000ほど上がりました.

負荷の様子は以下の通りです.
processes:2 私のショボイ環境ではこれが限界です.

top - 23:03:41 up 9 days,  9:17,  2 users,  load average: 1.52, 1.07, 0.59
Tasks:  22 total,   3 running,  19 sleeping,   0 stopped,   0 zombie
%Cpu(s): 55.0 us, 20.5 sy,  0.0 ni, 13.1 id,  0.1 wa,  0.0 hi, 11.2 si,  0.1 st
KiB Mem :  1883244 total,   231968 free,   679872 used,   971404 buff/cache
KiB Swap:  2097148 total,  1992180 free,   104968 used.   933116 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
16116 user      20   0  246640  16788   5424 R  81.0  0.9   0:33.15 unitd
16121 user      20   0  246640  16748   5384 R  79.3  0.9   0:33.05 unitd
16183 user      20   0   63712   3972   2408 S  54.7  0.2   0:03.46 ab
15703 user      20   0  288740   3300   1312 S  45.0  0.2   1:13.66 unitd

まとめ

  • すごく簡単にWebアプリケーションの実行環境がNGINX Unitで構築できます.
  • Webアプリケーションの実行環境の動作設定をAPIで簡単に設定変更できます.
  • 今回の手順を踏むことでCentOS7でPython3とvenvのPython製Webアプリケーションが利用できます.
  • ソースコードからNGINX unitを用意するので,CentOS以外でも自分の好きなバージョンのPythonを用意してモジュールを作ることができます

Flaskの組み込みサーバとおさらばだー!

Nginx Unitの今後のアップデートに期待しています.

以上です.