Mac OSのGoをNGINX Unitで動かしてみる

NGINX Unit

NGINX UnitがアプリケーションサーバとしてOSSで登場しました。

www.nginx.com

9/8(金)の夜に Mac OSGo で試した作業ログと気付いた点などをまとめておきます。ぱっと調べたところ Mac OS で実際に動かした人の情報は無さそうですね。

ドキュメントは GitHub - nginx/unit: An official read-only mirror of http://hg.nginx.org/unit/ を参考にしてください。

Go から unit パッケージを利用するには、現状だとまだ go get で落としてくることは出来ないのでソースコードを落としてきてビルドする必要があります。 ちなみに余談ですが unit ではなく nginxunit など別のパッケージ名にした方が良かったのになぁと個人的には思いました。 それでは、実際に Go でアプリケーションを動作させるまでの手順を最低限記載しておきますので興味のある人は、本ブログのままコピペで動かすところまでやってみてください。

2017/09/09の時点では、 Mac OS 上で Go のアプリケーションを動作させることは出来ませんでした。但し Mac OS で行った同じ手順を ubuntu で行ったところ正常に動作したことを確認出来ました。以下の手順は Mac OSubuntu 共通です。特に問題なければビルドから実行まで10分あれば試せますので是非いろいろ触ってみては如何でしょうか。

install

インストールは、以下の通り。

$ git clone https://github.com/nginx/unit
$ cd unit
$ ./configure
$ ./configure go
$ make all
$ make go-install
$ make install

これで $GOPATH 配下に unit がインストールされ Gounit を利用する準備が整いました。

起動

Mac OS で起動するには make したカレンドディレクトリの sbin 配下の unitd を実行します。バージョンを確認してから起動しました。 ubuntu の場合も sbin 配下の unitd 起動でも確認とれましたし、 sudo service unitd start からでも起動から Go アプリの実行まで確認出来ています。

$ ./sbin/unitd --version
unit version: 0.1
configured as ./configure
$./sbin/unitd

起動するとdefaultでカレントディレクトリに control.unit.sock ファイルが生成されます。参照 https://github.com/nginx/unit#configuration

起動オプション

$ ./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"

  --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

登録確認

まず listenerapplication の確認を行います。まだ未登録だということがわかります。

$ curl --unix-socket /Users/yone098/nginx-unit-work/unit/control.unit.sock http://127.0.0.1
{
    "listeners": {},
    "applications": {}
}

Goアプリケーション作成

Go で簡単なアプリケーションを作成します。 http.ListenAndServeunit.ListenAndServe に変更するだけです。 公式ドキュメントの import{ } になっているのでそのままコピペすると動かないので ( ) に書き換えてください。

package main

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

func HelloServer(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "hello, unit!\n")
}

func main() {
    http.HandleFunc("/", HelloServer)
    // 以下を unit に変更する
    //http.ListenAndServe(":8000", nil)
    unit.ListenAndServe(":8000", nil)
}

go build してバイナリを作成します。 設定ファイルにて作成したバイナリを executable として指定します。

設定ファイル

{
  "listeners": {
    "*:8070": {
      "application": "golang"
    }
  },
  "applications": {
    "golang": {
      "type": "go",
      "workers": 1,
      "executable": "/Users/yone098/nginx-unit-work/gounit",
    }
  }
}

登録

$ curl -X PUT -d @/Users/yone098/nginx-unit-work/gounit.json --unix-socket /Users/yone098/nginx-unit-work/unit/control.unit.sock http://127.0.0.1/
{
    "success": "Reconfiguration done."
}

確認

$ curl --unix-socket /Users/yone098/nginx-unit-work/unit/control.unit.sock http://127.0.0.1/
{
    "listeners": {
        "*:8080": {
            "application": "golang"
        }
    },

    "applications": {
        "golang": {
            "type": "go",
            "workers": 1,
            "executable": "/home/ubuntu/gounit"
        }
    }
}

登録したあとに変更する場合は listener を一旦削除してから登録します。 以下のように削除したあとに確認すると listener が削除されていることが確認出来ます。

削除

$ curl -X DELETE --unix-socket /Users/yone098/nginx-unit-work/unit/control.unit.sock 'http://127.0.0.1/listeners/*:8080'
{
    "success": "Reconfiguration done."
}
$ curl --unix-socket /Users/yone098/nginx-unit-work/unit/control.unit.sock http://127.0.0.1/
{
    "listeners": {},
    "applications": {
        "golang": {
            "type": go",
          "workers": 1,
          "executable": "/tmp/gounit"
      }
  }
}

Mac OS でのエラー

Mac OSで確認

$ curl http://127.0.0.1:8080/
Read header timeout

エラーログ

2017/09/09 23:52:21 [info] 28656#1194422 "golang" application started
2017/09/09 23:52:21 [crit] 28329#1175827 process 28656 exited on signal 9
2017/09/09 23:52:26 [alert] 28335#1176038 *17 error 408: Read header timeout

ubuntu で実行

$ curl http://127.0.0.1:8080/
hello, unit!

Mac OS 上の Go で実行しようとした際に “Read header timeout” になる件は、 nxt_router.c3185 行あたり

static void
nxt_router_conn_timeout(nxt_task_t *task, void *obj, void *data)
{
    nxt_conn_t   *c;
    nxt_timer_t  *timer;

    timer = obj;

    nxt_debug(task, "router conn timeout");

    c = nxt_read_timer_conn(timer);

    if (c->read_state == &nxt_router_conn_read_header_state) {
        nxt_router_gen_error(task, c, 408, "Read header timeout");

    } else {
        nxt_router_gen_error(task, c, 408, "Read body timeout");
    }
}

ubuntu 上だと全く同じ手順(ビルド後の ./sbin/unitd 経由で起動した場合)で正常に Go のアプリケーションが稼働しているところをみると Mac OS 固有の問題なんだろうなと思う。しばらくしたら Mac OS でも正常に動作するようになるかなと期待します。

まとめ

まだ出たばかりなので、これからに期待といったところでしょうか。 ubuntu でアプリ書き換え後にホットリロードされるかと思ったのですが unitd の再起動後、再度設定し直す必要があり、このへんもこれからといったところだと思いました。 今後は、 https://github.com/nginx/unit#key-features Key Features を見るとこれから機能追加といったところでしょうか。

- Fully dynamic reconfiguration using RESTful JSON API
- Multiple application languages and versions can run simultaneously
- Dynamic application processes management (coming soon)
- TLS support (coming soon)
- TCP, HTTP, HTTPS, HTTP/2 routing and proxying (coming soon)

Node.js, Java, Ruby もサポートするようなので Java サポートされたらまた触ってみようと思います。