Edited at

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

More than 1 year has passed since last update.


背景

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の今後のアップデートに期待しています.

以上です.