GORB で Docker クラスタのロードバランシング

GORB って?
==========

[GORB](https://github.com/kobolog/gorb/) は，**ほぼメンテナンスフリーで**クラスタ上にある Docker コンテナのロードバランスをしてくれるツールです．
Go言語で記述されており，それ自身も Docker コンテナとして動作することが想定されています．

仕組み
------------

GORB は，gorb と gorb-link という 2つのコマンド(を内在する2つのDockerコンテナ)から成り立っています．

gorb は，LVS を用いたロードバランシングを行います．
また，ロードバランス先を設定や死活状況の確認を行うための WebAPI を持っています．
gorbを含むコンテナが動作するホストのカーネルは，LVS が有効になっている必要があります．

gorb-link は，`docker.sock` を監視し，コンテナの状態変化がある都度 WebAPI 経由で gorb に通知します． 
gorb-link を含むコンテナが動作するホストのカーネルは，LVS が有効になっていなくても構いません．

つまり，下記列挙するようなフローで，ロードバランシングの設定が自動的に行われます．

0. LVS が有効になっているカーネルで，gorb を立ち上げる．
0. ロードバランス先の各ノードで，gorb-link を立ち上げる．
 0. gorb-link は起動時に，自らのノードのコンテナ情報を取得し，gorb に通知する．
 0. gorb が LVS の設定を書き換える．
0. 誰かがどこかのクラスタノードで Docker コンテナを操作する(立ち上げる or 終了する)．
0. gorb-link が，そのクラスタノードで，操作を検知し，gorb に通知する．
0. gorb が LVS の設定を書き換える．

加えて，gorb は，ロードバランス先の死活監視も行います．

このような仕組みにより，管理者は，ロードバランサの設定から開放されるわけです．


CoreOS への導入
=============

GORB 自身は，LVS さえ有効になっていれば，ホストのディストリビューションを選びません．
しかしここでは CoreOS に限定して解説します．
他のディストリビューションでも参考にはなると思います．

gorb と gorb-link は一台のホストに置いても動作しますが，クラスタを構成したほうがリアリティが出ると思いますので，IPアドレス `10.0.0.[1-4]` の 4 台構成とします．

Docker イメージは，筆者の所属会社のDockerHubリポジトリに登録したものを用います．[^2]

[^2]: 原作者も DockerHub にイメージを登録しているのですが，なぜか gorb-link のイメージが落とせなかったので．

LVS を有効にする
---------------

CoreOS は，デフォルトの状態で LVS が有効になっていません．
[この記事](http://qiita.com/monamour555/items/16581ec18f85a637320e)を参考にして有効にしてください．
ちなみに，他のディストリビューションでの LVS を有効にする記事には，ip_forwarding を 1 にすることが推奨されていますが，CoreOS の場合はデフォルトで 1 なので，気にしなくて構いません．

gorb コンテナをデプロイする
------------------------

ここでは `10.0.0.1` をロードバランサとします．

```shell-session
10.0.0.1$ docker run -d --restart=always --name gorb --net=host --privileged monami0ya/gorb -f -i eth0
c1ab4376b2a35e2d8d39b23364ab02012a74f9ece00abe71d81e1aeac54bf3bd
$
```

gorb-link コンテナをデプロイする
-----------------------------

`10.0.0.2`, `10.0.0.3`, `10.0.0.4` に gorb-link コンテナをデプロイします．

ふつう，ロードバランサの上でアプリケーションを走らせないとは思いますが，
`10.0.0.1` にも gorb-link コンテナをデプロイしても，問題はありません．

```shell-session
10.0.0.2$ docker run --restart=always -d --net=host -v /var/run/docker.sock:/var/run/docker.sock --name gorb-link monami0ya/gorb-docker-link -r 10.0.0.1:4672 -i eth0
````

`10.0.0.1` は，先に gorb を動作させたコンテナが動作しているホストの IP アドレスです．
`4672` は，GORB の WebAPI のデフォルトポートです．このポートをロードバランシングで使いたい場合は，gorb の起動時にオプション `-l:{ポート番号}` を加えることで変更できます．

試す
----

作業はここまでです．もうロードバランサが動作しています．
この記事に興味を持たれる方は，既にロードバランスが必要なコンテナ群をお持ちでしょうから，いろいろ試してみてください．

発展: 死活状況を確認する
---------------

死活状況の確認には，WebAPI を使います．

まず，ログから，vsID を確認します．

```shell-session
10.0.0.1$ docker logs gorb
(中略)
ime="2015-12-19T22:42:26Z" level=info msg="starting pulse for [deis-router-v1.12.2-2222-tcp/deis-router-2222-tcp]" 
time="2015-12-19T22:42:26Z" level=info msg="creating virtual service [deis-router-v1.12.2-443-tcp] on 10.0.0.1:443" 
time="2015-12-19T22:42:26Z" level=info msg="creating backend [deis-router-443-tcp] on 10.0.0.2:443 for virtual service [deis-router-v1.12.2-443-tcp]" 
time="2015-12-19T22:42:26Z" level=info msg="starting pulse for [deis-router-v1.12.2-443-tcp/deis-router-443-tcp]" 
time="2015-12-19T22:42:26Z" level=info msg="creating virtual service [deis-router-v1.12.2-80-tcp] on 10.0.0.1:80" 
time="2015-12-19T22:42:26Z" level=info msg="creating backend [deis-router-80-tcp] on 10.0.0.2:80 for virtual service [deis-router-v1.12.2-80-tcp]" 
time="2015-12-19T22:42:26Z" level=info msg="starting pulse for [deis-router-v1.12.2-80-tcp/deis-router-80-tcp]" 
time="2015-12-19T22:43:11Z" level=error msg="unable to connect to 10.0.0.2:443"
(後略)
```

`deis-router-80-tcp` や `deis-router-443-tcp` などが vsID です．

これを使って下記のようにします．

```shell-session
$ curl http://10.0.0.7:4672/service/deis-router-v1.12.2-80-tcp
{
        "options": {
                "host": "",
                "port": 80,
                "protocol": "tcp",
                "method": "wrr",
                "persistent": false
        },
        "health": 1,
        "backends": [
                "deis-router-80-tcp"
        ]
}
```

port 80 は `"health": 1` なので，生きています．一方 443 は下記のように死んでいます．
上記ログにも `unable to connect to 10.0.0.2:443` とあります．

```
$ curl http://10.0.0.7:4672/service/deis-router-v1.12.2-443-tcp
{
        "options": {
                "host": "",
                "port": 443,
                "protocol": "tcp",
                "method": "wrr",
                "persistent": false
        },
        "health": 0,
        "backends": [
                "deis-router-443-tcp"
        ]
}
```

情報の寿命と Consul との連携
==========================
gorb は gorb-link から得た情報を，オンメモリに保持するほかに，Consul が提供する KVS に保存できます．
オンメモリに保持した場合は，当然のことがら， gorb プロセスが落ちると消えます．
その際には gorb-link の再起動が必要になります．

GORBにできないこと
================

GORB は L4 バランサです．http(s) の場合，L7 バランサにしてアクセス制御したい要求があると思いますが，それはできません．

入ってきたのと別の番号のポートに転送することはできません．

その他，細かいところで足りないところはあります．しかし，それらの多くはメンテナンスフリーとのトレードオフなので悩ましいところではあります．

まとめ
=====

* GORB を用いると，クラスタ上にある Docker コンテナ群のロードバランシングを実現できます．
* GORB は，Go言語で記述され，軽量です．
* L4 バランサです．
* 複雑なことはできませんし，こなれていない部分もありますが，メンテナンスフリーです．