/var/log/study

つまり雑記

PHP, Python, Golang を NGINX Unit で動かしてみた

NGINX Unit

ホームページは以下

www.nginx.com

もしくはミラーだけどGitHubが以下となる

github.com

RestAPIやJSONで設定できる、phpPHP-FPMやpythonwsgiサーバーなど言語ごとのアプリケーション・サーバーを集約したアプリケーションサーバーという感じ。なのでNginxの後ろで動くサーバーという認識で大丈夫なのかな?

まだversionは0.1なので、今後どんどん成長していくはず。

現状は以下に対応しているとのこと

Python 2.6, 2.7, 3

PHP 5, 7

Go 1.6 or later

ざっくりとした所感

  • プロダクトに関して
    • 言語ごとのミドルウェア運用がNGINX Unitに集約されて嬉しい可能性がある
    • Docker + NGINX Unit も嬉しいが、NGINX Unitだけでも十分に嬉しいかも
    • ベンチマークを取ってどれだけさばけるか試したい
  • 現状のドキュメントに関して
    • インストールはドキュメントどおり行えばOK
    • アプリケーション・サーバーとしての動作
      • PHPはさっくりと動かせた
      • Pythonもちょっとwsgiに関して少し調べれば動かせた。
      • Golangissue を見つけなかったら動かせなかったと思う
    • まだ細かいチューニング方法とかもドキュメントには記述されていなさそう?

と、言うことで、3つの言語で動かした際の動かし方を残しておく (最初にドキュメントを読んで、詰まった際の補助資料くらいに思うと良いはずです。)

動かし方

インストール後のサービスの動かし方

systemctl enable unitd.service
systemctl start unitd.service

気づき

  1. curl --unix-socket /run/control.unit.sock http://localhost/ で設定を確認できる.
  2. systemctl restart unitd.service をしたら設定が揮発する模様?

各言語ごとの動かし方をまとめ

要点だけ

PHP

設定で指定する rootindex で指定するファイルを置くだけ

Python

設定で指定するpathwsgi.py を設置, wsgi.py の中で applicationwsgiアプリケーションになっていればOK

例えばFlaskなら以下の様な感じ

#!/usr/bin/env python

from flask import Flask

application = Flask(__name__)

@application.route("/")
def index():
    return "<h1>Hello NGINX Unit</h1>"

Golang

こっちが一筋縄では行かなかった。

まず、unit をインストールしているだけではダメでCentOSでは unit-devel をインストールしておく必要があることと、

export GOPATH=/home/user/go_apps をしておいた後に、 unit パッケージをインポートして使う。

上記の状態で、以下のようなソースをビルドし、設定の executable で指定したパスに配置する。

package main

import (
    "fmt"
    "net/http"
    "unit"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello NGINX Unit</h1>")
    })
    unit.ListenAndServe(":8000", nil)
}

普通のgolangのアプリと違うのは、 ListenAndServe するのが、httpではなく unit な点だと思う

個人的感想

NGINXが新しくサーバーをリリースする!というのは個人的に驚き。

ApacheよりもNGINXに慣れ親しんでいる世代ということもあり、頑張ればソースコードを読み切る事ができそうな気がする規模なので読みたい。

以下は全部この記事を書いた際のメモ

とりあえず動かしてみる

CentOS7でやってみる

[root@mycentos7 go-app]# cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

↑のインストールにそって yum -y install unit で問題なくうまくいく

ドキュメント的にはインストールで終わっているので動かし方を探ると以下の感じで動き出す。定石どおり。

[root@mycentos7 unit]# systemctl status unitd
● unitd.service - NGINX Unit
   Loaded: loaded (/usr/lib/systemd/system/unitd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
systemctl enable unitd
systemctl start unitd
[root@mycentos7 unit]# systemctl status unitd
● unitd.service - NGINX Unit
   Loaded: loaded (/usr/lib/systemd/system/unitd.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2017-09-08 00:42:25 JST; 6s ago
  Process: 2502 ExecStart=/usr/sbin/unitd $UNITD_OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 2503 (unitd)
   CGroup: /system.slice/unitd.service
           ├─2503 unit: main [/usr/sbin/unitd --log /var/log/unitd.log --pid /run/unitd.pid]
           ├─2505 unit: controller
           └─2506 unit: router

プロセスとしては動いていそう。

[root@mycentos7 unit]# ps aux | grep unitd
root      2503  0.0  0.0  16168   632 ?        Ss   00:42   0:00 unit: main [/usr/sbin/unitd --log /var/log/unitd.log --pid /run/unitd.pid]
root      2516  0.0  0.0 112644   964 pts/0    R+   00:44   0:00 grep --color=auto unitd

lsofを叩いてもunitdが掴んでいるポートはなさそう?

[root@mycentos7 log]# lsof -i
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
chronyd   715 chrony    1u  IPv4  13918      0t0  UDP localhost:323
chronyd   715 chrony    2u  IPv6  13919      0t0  UDP localhost:323
sshd     1038   root    3u  IPv4  16617      0t0  TCP *:ssh (LISTEN)
sshd     1038   root    4u  IPv6  16626      0t0  TCP *:ssh (LISTEN)
master   1648   root   13u  IPv4  17586      0t0  TCP localhost:smtp (LISTEN)
master   1648   root   14u  IPv6  17587      0t0  TCP localhost:smtp (LISTEN)
dhclient 2260   root    6u  IPv4  18318      0t0  UDP *:bootpc
dhclient 2260   root   20u  IPv4  18307      0t0  UDP *:46788
dhclient 2260   root   21u  IPv6  18308      0t0  UDP *:gilatskysurfer

ドキュメントを読むと

By default, the Unit API is available in the control socket file unit.control.sock.

と書いてあるので、探すと /run/control.unit.sock があることがわかった。

[root@mycentos7 log]# find / -name 'control.unit.sock'
/run/control.unit.sock

ミニマルと呼ばれている設定を投げつけることにする

ドキュメントどおり以下の設定ファイルを

{
  "listeners": {
    "*:8300": {
      "application": "blogs"
    }
  },
  "applications": {
    "blogs": {
      "type": "php",
      "workers": 20,
      "root": "/www/blogs/scripts",
      "index": "index.php"
    }
  }
}

curl -X PUT -d @./start.json --unix-socket /run/control.unit.sock http://localhost/ で投げつける

レスポンスは以下の様

{
    "success": "Reconfiguration done."
}

ではphpを設置してみる。

[root@mycentos7 ~]# mkdir -p /www/blogs/scripts
[root@mycentos7 ~]# touch /www/blogs/scripts/index.php
[root@mycentos7 ~]# echo -e "<?php\n\nphpinfo();" > /www/blogs/scripts/index.php
[root@mycentos7 ~]# cat /www/blogs/scripts/index.php
<?php

phpinfo();

curl localhost:8300 できちんと反応する

自分のホストのIPだと反応がないと思ったが、firewalldを切ったらきちんと反応した.

ちなみにドキュメントの何処かに書いて有りそうだが、 curl --unix-socket /run/control.unit.sock http://localhost/ で設定を確認できる.

また systemctl restart unitd.service をしたら設定が揮発した。たぶんココらへんは今後改良される点なはず。

Python

こうなったらpythonもためしたい。

またしてもドキュメントどおりの設定を投げつけ、

{
  "applications": {
    "blogs": {
      "type": "php",
      "user": "nobody",
      "group": "nobody",
      "workers": 20,
      "root": "/www/blogs/scripts",
      "index": "index.php"
    },
    "wiki": {
      "type": "python",
      "user": "nobody",
      "group": "nobody",
      "workers": 10,
      "path": "/www/wiki",
      "module": "wsgi"
    }
  },
  "listeners": {
    "*:8300": {
      "application": "blogs"
    },
    "*:8400": {
      "application": "wiki"
    }
  }
}

/www/wiki/wsgi.py を以下のように作る

[root@mycentos7 wiki]# cat wsgi.py
#!/usr/bin/env python

from flask import Flask

application = Flask(__name__)

@application.route("/")
def index():
    return "<h1>Hello NGINX Unit</h1>"

こちらもそつなく動く

golang

ついでだからgolangも書いておく。今度はドキュメントからチョット変更して以下のような設定を投げつける

{
  "applications": {
    "blogs": {
      "type": "php",
      "user": "nobody",
      "group": "nobody",
      "workers": 20,
      "root": "/www/blogs/scripts",
      "index": "index.php"
    },
    "wiki": {
      "type": "python",
      "user": "nobody",
      "group": "nobody",
      "workers": 10,
      "path": "/www/wiki",
      "module": "wsgi"
    },
    "go_server": {
      "type": "go",
      "executable": "/www/server/bin/server",
      "user": "nobody",
      "group": "nobody"
     }
  },
  "listeners": {
    "*:8300": {
      "application": "blogs"
    },
    "*:8400": {
      "application": "wiki"
    },
    "*:8500": {
      "application": "go_server"
    }
  }
}

でもって以下のようなコードを作って... と思ったら、golangは普通にnet/httpを使うのではダメな模様

https://github.com/nginx/unit/issues/8/ に書いてある/usr/share/doc/unit/examples/go-app/let-my-people.goを覗くと unit というパッケージがある

package main

import (
    "fmt"
    "net/http"
    "unit"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("Content-Type", "text/plain");

    fmt.Fprintf(w, "Method : %s\n", r.Method)
    fmt.Fprintf(w, "URL    : %s\n", r.URL.Path)
    fmt.Fprintf(w, "Host   : %s\n", r.Host)
}

func main() {
    http.HandleFunc("/", handler)
    unit.ListenAndServe("8000", nil)
}

上記をissue真似て, sudo yum -y install unit-devel をしたあとexport GOPATH=/home/user/go_apps, 以下のファイルを go build して /www/server/bin/server としてファイルを設置したら curl localhost:8500が上手くいった.

package main

import (
    "fmt"
    "net/http"
    "unit"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello NGINX Unit</h1>")
    })
    unit.ListenAndServe(":8000", nil)
}

以上でPHP, Python, GolangでUnitを動かしてみる事自体は完了となる