CoreOS 上の Docker コンテナのサービス情報を registrator を使って etcd へ自動登録する


## 概要
registrator というサービス検出・登録・削除専用コンテナを使って、CoreOS 上に起動する Docker サービス（rails や redis, elasticsearch などポートを外部にさらすもの）を etcd へ自動登録するという話

## サービス登録の選択肢
Docker をマルチホストで使おうと思ったときに出てくるのがサービスディスカバリの問題。例えば Web アプリケーションコンテナをロードバランサに登録しようと思ったとき、何らかの方法でアプリケーションコンテナがどのホストのどのポートで起動しているのか知らせる必要がある。
サービスディスカバリ用ミドルウェアとして [Consul](http://www.consul.io/) や [etcd](https://github.com/coreos/etcd) がある。そういったものに Docker コンテナとして起動したサービスを登録するには選択肢は 3 種類ある（他にもあるかもだけど）

* **起動するサービス自身が登録する**: Webhook やサービスディスカバリへの登録機能をもったサービス自身が起動時に自分を etcd や consul に登録する (self-registering という）
* **起動するサービスに並行して起動する登録専用プロセスが登録する**: 起動するサービスと別にサービス登録用のプロセスを走らせ etcd や consul に登録する（coprocess や sidekick という）
* **起動するサービスと切り離されたプロセスが自動登録する**: サービスを起動すると予め起動していた別のプロセスがそれを検知しサービス登録する

self-registering と coprocess アプローチにはそれぞれ問題がある

### Self-Registering の問題

* Webhook やサービス登録といったことをサポートしていないアプリケーションやミドルウェアでは利用出来ない
* 利用するサービスディスカバリの仕組みを理解していないとサービスを起動できない

=> サービスを起動する際にサービスディスカバリーのことを気にしないといけない

### Coprocess の問題

* 設定が複雑になること（利用するサービスディスカバリの仕組みを理解していないとサービスを起動できない）
 * あるサービスを起動する際に必ずサービス登録用プロセスを走らせる設定を予めしておかないといけない

=> サービスを起動する際にサービスディスカバリーのことを気にしないといけない

Coprocess の例: [Deploying a Service Using fleet](https://coreos.com/docs/launching-containers/launching/fleet-example-deployment/)

この例では Web コンテナを起動する際、systemd を使って ELB へその Web コンテナが起動したことを伝えるサービス登録専用コンテナをバインドしている。
上に書いたとおり、web コンテナ用 unit ファイルに加えてサービス登録用 unit ファイルも用意し、それについて知っておく必要がある。さらに起動するサービスがいくつもある場合単純計算で管理するプロセスも倍になるので大変だよね..

Self-Registering と Coprocess の問題を踏まえ、サービスディスカバリについて気にせずサービスを起動できるようにしてくれるのが [registrator](https://github.com/progrium/registrator) 

## registrator で Docker サービス を etcd へ自動登録する
registrator は Docker サービス検出・登録・削除専用サービス。 Docker イメージが用意されており、起動して [Consul](http://www.consul.io/) や [etcd](https://github.com/coreos/etcd) にサービス情報を自動で登録してくれる。

registrator を使う事で 

* [logspout で CoreOS 上の Docker コンテナのログを集約・ルーティングする](http://qiita.com/spesnova/items/e05413eafadb9ff60056)
* [Datadog で CoreOS をモニタリングする](http://qiita.com/spesnova/items/882e0a61ddbe6c356394)

と同じくホスト側の設定を最小限に抑え、出来るだけ全てをコンテナでやる "docker way" アプローチが取れるのもうれしい。

registrator を起動する。その際 etcd を使うようにする。（consul も利用可能）

```bash
core@core-01 ~ $ docker run -d --name registrator -h $HOSTNAME -v /var/run/docker.sock:/tmp/docker.sock progrium/registrator -ip=172.17.8.101 etcd://172.17.8.101:4001/services
```

registrator のログを見ると、サービス登録・削除のログが出ている

```bash
core@core-01 ~ $ docker logs -f registrator
2014/09/12 14:08:10 registrator: Forcing host IP to 172.17.8.101
2014/09/12 14:08:10 registrator: Using etcd registry backend at etcd://172.17.8.101:4001/services
2014/09/12 14:08:10 registrator: ignored: f47e0e26cfbc no published ports
2014/09/12 14:08:10 registrator: ignored: 2a0f5ea6785b no published ports
2014/09/12 14:08:10 registrator: added: 06aedc6bf57d core-01:logspout:8000
2014/09/12 14:08:10 registrator: added: f5745a0d97e5 core-01:registry:5000
2014/09/12 14:08:10 registrator: Listening for Docker events...
```

redis コンテナを 2 個立てる。 etcd にサービス情報が登録されるので固定のポートを利用する必要はなくなる。 `-P` を使ってホスト側ポートは自動選択する。

```bash
core@core-01 ~ $ docker run -d --name redis.1 -h $HOSTNAME -P dockerfile/redis
core@core-01 ~ $ docker run -d --name redis.2 -h $HOSTNAME -P dockerfile/redis
```

すると registrator で 2 個サービスが検出され、etcd に登録してくれる

```bash
core@core-01 ~ $ docker logs -f registrator
...
2014/09/12 14:12:40 registrator: added: cab889269f0e core-01:redis.1:6379
2014/09/12 14:13:31 registrator: added: eac456c594c0 core-01:redis.2:6379
```

etcd から

```bash
core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/services/redis | jq .
```

redis サービスのノードが 2 つあることがわかり、それぞれの IP とポートが取得できるようになる

```json
{
  "node": {
    "createdIndex": 556,
    "modifiedIndex": 556,
    "nodes": [
      {
        "createdIndex": 556,
        "modifiedIndex": 556,
        "value": "172.17.8.101:49153",
        "key": "/services/redis/core-01:redis.1:6379"
      },
      {
        "createdIndex": 593,
        "modifiedIndex": 593,
        "value": "172.17.8.101:49154",
        "key": "/services/redis/core-01:redis.2:6379"
      }
    ],
    "dir": true,
    "key": "/services/redis"
  },
  "action": "get"
}
```

etcd に登録しているのでクラスタ内の他のマシンでもこの情報が取得出来る

```bash
core@core-02 ~ $ curl -L http://127.0.0.1:4001/v2/keys/services/redis
```

`redis.1` を止めると

```bash
core@core-01 ~ $ docker stop redis.1
redis.1
```

```
2014/09/12 14:18:05 registrator: removed: cab889269f0e core-01:redis.1:6379
```

`registrator` でというログが出て、redis サービスのノードが 1 に減った

```bash
core@core-02 ~ $ curl -L http://127.0.0.1:4001/v2/keys/services/redis
```

```json
{
  "node": {
    "createdIndex": 556,
    "modifiedIndex": 556,
    "nodes": [
      {
        "createdIndex": 593,
        "modifiedIndex": 593,
        "value": "172.17.8.101:49154",
        "key": "/services/redis/core-01:redis.2:6379"
      }
    ],
    "dir": true,
    "key": "/services/redis"
  },
  "action": "get"
}
```

## Cloud-Config で registrator を起動する


```yaml
coreos:
  units:
    - name: registrator.service
      command: start
      content: |     
        [Unit]
        Description=Registrator
        After=docker.service
        Requires=docker.service

        [Service]
        TimeoutStartSec=0
        ExecStartPre=-/usr/bin/docker kill registrator
        ExecStartPre=-/usr/bin/docker rm registrator
        ExecStartPre=/usr/bin/docker pull progrium/registrator:latest
        ExecStart=/usr/bin/docker run --name registrator -h %H -v /var/run/docker.sock:/tmp/docker.sock progrium/registrator:latest -ip=$private_ipv4 etcd://$private_ipv4:4001/services
        ExecStop=/usr/bin/docker stop registrator

        [Install]
        WantedBy=multi-user.target
```

## メモと感想
### TODO: etcd に登録されたサービス情報を ELB に伝える部分を考える・仕込む

## REF

* [progrium/registrator](https://github.com/progrium/registrator)
* [Automatic Docker Service Announcement with Registrator](http://progrium.com/blog/2014/09/10/automatic-docker-service-announcement-with-registrator/)
* [Deploying a Service Using fleet](https://coreos.com/docs/launching-containers/launching/fleet-example-deployment/)
* [Automated docker ambassadors with CoreOS + registrator + ambassadord](http://www.virtualroadside.com/blog/index.php/2014/07/28/automated-docker-ambassadors-with-coreos-registrator-ambassadord/)
* [CoreOS 上の Docker コンテナのサービス情報を registrator を使って SkyDNS2 に自動登録する](http://qiita.com/spesnova/items/1d384f80a76d8fa5c23f)