NGINX unit v0.1 と所感

せっかくなので動作までの作業ログと所感をまとめて書いておく。最新のインターフェースや環境構築については公式README.mdのほうが詳しいので参考程度に。記事内リンクについてはv0.1時点のものを引用しているためmasterとは差分があるので注意。

www.nginx.com

環境

Vagrantubuntu-xenialを用意してgolangの受け答えができるところまで作った

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"

構築

unitの本体を入れる

configurationの受け答えしたり、(将来的に)アプリケーションのリロードや監視などのAPIが生えるであろうパッケージ。yum だと面倒な手順いらないらしいけどaptだとkeyの登録が必要だったりする。インストール手順はREADME.md#ubuntu-packagesを見てほしい。

goのプロジェクト作成

テスト環境なのでディレクトリはGOPATH以下に作ってしまう

$ mkdir -p $GOPATH/src/github.com/your-name/nginx-unit
$ cd $GOPATH/src/github.com/your-name/nginx-unit
$ touch nginx-unit.go

nginx-unit.go

このunitパッケージはgolangだけ必要らしい。PHP, pythonはピュアな応答をするモジュールを作成するだけで済むが、Golangはおそらくバイナリ上でポート設定等をgracefulにやりたかったのでパッケージを別にする必要があったのだと思ってる*1。それはそうと、unitって名前どうにかならないのかな。リネーム前のnginext よりはいいんだけど。

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, "Proto  : %s\n", r.Proto)
    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)
    // port はダミーなので適当で良い
    unit.ListenAndServe("8080", nil)
}

nginx/unitをコンパイル

各種言語用のパッケージをコードからビルドする必要があるのでcloneしてきて必要なものをビルドしていく。依存パッケージのインストールからパッケージのインストールまでをやると動く。特にv0.1に関しては入れるだけで済んだ。

起動

goのプロジェクトをビルドしてパスを控える

$ cd $GOPATH/src/github.com/your-name/nginx-unit
$ go build

unit本体の起動

$ sudo service unitd start

一応socket探す(手元では /run/control.unit.sockにあった)

$ find / -name 'control.unit.sock'

config.json

configuration用のjsonexecutableへは先程のビルドしたバイナリファイルへのパスを書く。また、ポート・ワーカー数などは適当な数に設定する

{
  "listeners": {
    "*:8040": {
      "application": "golang"
    }
  },
  "applications": {
    "golang": {
      "type": "go",
      "workers": 1,
      "executable": "full/path/to/nginx-unit/binary/file",
    }
  }
}

jsonの内容はAPI、もしくはserviceに生えてるインターフェースから適用する

$ sudo curl -X PUT -d @config.json --unix-socket /run/control.unit.sock http://127.0.0.1:8040/
# または
$ sudo service unitd restoreconfig /full/path/to/json

ここまでするとconfigに書いた内容でgolangのプロセスが立ち上がっているのでcurlで確認

$ curl curl http://localhost:8080/foo/bar
Proto  : HTTP/1.1
Method : GET
URL    : /foo/bar
Host   : localhost:8040

さらにconfig.json書き換えることで複数のパスを指定し、複数のプロセスを管理することもできる。

おお、簡単に動くじゃんと思ったが、機能としては現状ここまでらしく、hot reload がされなかったり(インターフェースがまだない?)unitをrestartするとconfigurationは全てpurgeしたり*2するなど全く実用段階ではないことがわかった。

所感

そもそもNGINX unitで何が嬉しいのかっていうと、今のところ言語間のインターフェースを吸収することでUnicornとかuwsgiを必要とせず、さらにアプリケーション内にデプロイ用のコードを個別に実装する必要がなくなるっていうのが強みにかなと思う。あとはHTTPで橋渡しができるのでそれがとにかく楽であった。 公式のKey Featuresを見る限りは動的なプロセス管理の対応や、サービスメッシュになっていくぞという宣言があるのでそこにも期待していきたい。所詮NGINX Application Platformの一機能としてのNGINX unitだが、unitのKey Featuresだけでも高機能なので、完動すればだいぶ心地の良いものになるんじゃないかと思っている。