Consul 0.6でユーザのいるshardを探す(Prepared Queryを使う)
この記事は HashiCorp Advent Calendar 2015 - Qiita の第21日目です。(大人な事情により Hamee Advent Calendar 2015 - Qiita の第20日目でもあります。なので微妙な時間に公開)
概略
ユーザIDをベースにDBを分割(sharding)しているのですが、プログラムからユーザのいるshardを探すのにConsulを使えないかなぁと思って試している話。
Consul0.6のPrepared Queryを使って解決してみました。
目次
ユーザIDをベースにDBを分割(sharding)していると、プログラムからユーザのいるshardを探す必要があります。 これをどこかのメタDBサーバにするってのも良いんだけど、Consul使ってDNSで引っ張ってこれると使う側にとって扱いやすいかなと思って調べてます。 ・Node設定 各DBではconsulのServiceが下記JSONの定義のように登録されています。 なので以下のような感じでDNSでひける状態です。
ちなみに試している環境にはdnsmasqを入れています。 Consul0.6で導入されたPrepared Query(以下PQ)を使う方法です。 PQに下記のような情報を登録します。(user1.jsonとします) これを以下のように登録します。 IDが発行されるので、これは後で必要になります。もしくは /v1/query にGETでリクエストすれば一覧が取れるので、そこから必要ものを探せばOKです。 このようにすると、以下のようにDNSでひけて、ユーザが収容されているmaster DBのIPが取得できます。 ・slave昇格 db01-aが死んでdb01-bがmasterになるときは、db01-bで のようにmasterのタグをもたせて これで、同じようにDNSをひくと、昇格したノードのIPが取得できます。 なお、db01-aがmasterのタグを持って起動しないようにする必要はあります。今回は、マシンブート時にconsulを自動起動させないようにして逃げてます。 ・shard移動 user1のいるshardがdb02になれば、 というJSONを用意して、このPQのIDを指定してPUTします。 これで、同じようにDNSをひくと、今度はdb02のmasterのIPが取得できます。 Consul0.6より前だとPQがないので、外部サービス(ノード)を登録することで同じようにことできると思います。 この場合、外部サービスとして以下のようなJSONを登録します。 こうすると、以下のようにDNSがひけます。 外部サービス(ノード)なので、CNAMEで解決されて、その後Aレコードを得るような形となります。 shardの移動は、Nodeの定義を更新することで対応することとなります。 Consul0.6より前ではこの方法かなぁとは思うのですが、Consul0.6だとPQによりCNAMEを介さずにひけるので方法1を採用予定です。 ・タグだけで のようにタグだけで解決することも可能は可能かと。 ただ、shard移動するときとか、userの追加・削除時には必要なタグをして指定して更新するのでちょっと大変そう。 ・サービスだけで みたいなサービスをユーザ数分だけ登録することでもできそう。 この場合、slave昇格でユーザ数分だけServiceを更新・登録とかしないといけなのでこれも大変そう。 ユーザIDをベースにDBを分割(sharding)しているような場合に、それをDNSでひけるようにするには、Consul0.6のPrepared Queryを使うとなんとかなりそうでした。 ちなみに、PQを1万ほど突っ込んでみた範囲では、DNSをひくことにパフォーマンス的な影響は特に見られませんでした。 Consulでやる場合のもっと良い方法とか、そもそもConsul使うべきじゃないとかありましたら教えてください。背景
前提
{
"Service": {
"name": "db01",
"tags": [ "master" ]
}
}
# dig master.db01.service.consul.
; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.37.rc1.el6_7.4 <<>> master.db01.service.consul.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53317
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;master.db01.service.consul. IN A
;; ANSWER SECTION:
master.db01.service.consul. 30 IN A 192.168.0.11
;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Dec 18 14:11:33 2015
;; MSG SIZE rcvd: 94
方法1(今回採用予定)
{
"Name": "master-db-user1",
"Service": {
"Service": "db01",
"Tags": ["master"]
},
"DNS": {
"TTL": "30s"
}
}
$ curl -X POST http://localhost:8500/v1/query --data-binary @user1.json
{"ID":"66804426-8e5f-427b-caa7-d896c9328910"}
$ dig master-db-user1.query.consul.
; <<>> DiG 9.3.6-P1-RedHat-9.3.6-25.P1.el5_11.2 <<>> master-db-user1.query.consul.
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14375
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;master-db-user1.query.consul. IN A
;; ANSWER SECTION:
master-db-user1.query.consul. 30 IN A 192.168.0.11
;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Dec 18 14:44:21 2015
;; MSG SIZE rcvd: 94
{
"Service": {
"name": "db01",
"tags": [ "master" ]
}
}
consul reload
すればよいです。$ dig master-db-user1.query.consul. +short
192.168.0.12
{
"Name": "master-db-user1",
"Service": {
"Service": "db02",
"Tags": ["master"]
},
"DNS": {
"TTL": "30s"
}
}
$ curl -X PUT http://localhost:8500/v1/query/66804426-8e5f-427b-caa7-d896c9328910 --data-binary @user1.json
$ dig master-db-user1.query.consul. +short
192.168.0.21
方法2
{
"Node": "master-db-user1",
"Address": "master.db01.service.consul"
}
$ curl -X PUT http://localhost:8500/v1/catalog/register --data-binary @user1-node.json
$ dig master-db-user1.node.consul.
; <<>> DiG 9.3.6-P1-RedHat-9.3.6-25.P1.el5_11.2 <<>> master-db-user1.node.consul.
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29792
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;master-db-user1.node.consul. IN A
;; ANSWER SECTION:
master-db-user1.node.consul. 30 IN CNAME master.db01.service.consul.
master.db01.service.consul. 30 IN A 192.168.0.11
;; Query time: 5 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Dec 18 15:20:49 2015
;; MSG SIZE rcvd: 166
その他の方法
{
"Service": {
"name": "db01",
"tags": [ "master", "user1", "user2", ・・・ ]
}
}
{
"Service": {
"name": "user1",
"tags": [ "master"]
}
}
まとめ