概要
registrator というサービス検出・登録・削除専用コンテナを使って、CoreOS 上に起動する Docker サービス(rails や redis, elasticsearch などポートを外部にさらすもの)を etcd へ自動登録するという話
サービス登録の選択肢
Docker をマルチホストで使おうと思ったときに出てくるのがサービスディスカバリの問題。例えば Web アプリケーションコンテナをロードバランサに登録しようと思ったとき、何らかの方法でアプリケーションコンテナがどのホストのどのポートで起動しているのか知らせる必要がある。
サービスディスカバリ用ミドルウェアとして Consul や 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
この例では Web コンテナを起動する際、systemd を使って ELB へその Web コンテナが起動したことを伝えるサービス登録専用コンテナをバインドしている。
上に書いたとおり、web コンテナ用 unit ファイルに加えてサービス登録用 unit ファイルも用意し、それについて知っておく必要がある。さらに起動するサービスがいくつもある場合単純計算で管理するプロセスも倍になるので大変だよね..
Self-Registering と Coprocess の問題を踏まえ、サービスディスカバリについて気にせずサービスを起動できるようにしてくれるのが registrator
registrator で Docker サービス を etcd へ自動登録する
registrator は Docker サービス検出・登録・削除専用サービス。 Docker イメージが用意されており、起動して Consul や etcd にサービス情報を自動で登録してくれる。
registrator を使う事で
と同じくホスト側の設定を最小限に抑え、出来るだけ全てをコンテナでやる "docker way" アプローチが取れるのもうれしい。
registrator を起動する。その際 etcd を使うようにする。(consul も利用可能)
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 のログを見ると、サービス登録・削除のログが出ている
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
を使ってホスト側ポートは自動選択する。
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 に登録してくれる
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 から
core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/services/redis | jq .
redis サービスのノードが 2 つあることがわかり、それぞれの IP とポートが取得できるようになる
{
"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 に登録しているのでクラスタ内の他のマシンでもこの情報が取得出来る
core@core-02 ~ $ curl -L http://127.0.0.1:4001/v2/keys/services/redis
redis.1
を止めると
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 に減った
core@core-02 ~ $ curl -L http://127.0.0.1:4001/v2/keys/services/redis
{
"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 を起動する
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