consul-template!

consul-template!

consul kvと連携したconfigurationができる!

弊社はconsul/serfでGoogle Cloud Platform化において
ほぼ完全に自動化されたclusteringをしてますが
実際のところ以下のような役割分担でやってます。

  • serfはオーケストレーション
  • consulは監視系及びインスタンスの正常性を管理

consul/serf被ってと言われると被ってる範囲もあるけど
そもそもserfはconsulより前から使ってたので
たぶんそのうちserfが要らなくなるかな・・・
今は移行期間として並列で稼働させてたりする。

consul-template

hashicorp/consul-template

consul-template — Generic template rendering and notifications with Consul

github.com

Install

buildしても以下のbinary落としてきてもどっちでも
※自分はbuildしてるけどbuildする場合goの環境整えるのがまずめんどいかも・・・

Plain text
mkdir -p /usr/local/src/consul-template
cd /usr/local/src/consul-template
wget https://github.com/hashicorp/consul-template/releases/download/v0.7.0/consul-template_0.7.0_linux_amd64.tar.gz
tar -xvzf consul-template_0.7.0_linux_amd64.tar.gz
cd consul-template_0.7.0_linux_amd64/
cp -p consul-template /usr/local/bin/
which consul-template

Setup

/usr/local/etc配下に構成

Plain text
mkdir -p /usr/local/etc/consul-template/{conf.d,param.d,template.d}
  • conf.d: consul-templateのconfigを置く
  • param.d: consul kvに入れるJSONの元データYAMLを置く
  • template.d: consul-templateのtemplateを置く

ちょっと今回はredisあたりのsample configを利用
前半何行かだけで

今回consul-templateのparseJSON使います!

  • consul kvにtree構造でデータ入れてくのもミス増えそうなので
  • どっかに元データとなるyaml配置して そいつの構造からいちいちtreeでconsul kvに入れるのめんどい

※JSON全体を受けてtemplate内部でその他のKeyも使えるから一旦これでいいかな

/usr/local/etc/consul-template/conf.d/redis.conf

  • 生成先は/tmp/redis.conf(ちゃんとしたconfigじゃないけど)
  • 生成後のcommandはとりあえず使わないけどなんか寂しいのでLog吐くようにw
Plain text
consul = "127.0.0.1:8500"
retry = "10s"
max_stale = "10m"
syslog {
 enabled = true
 facility = "LOCAL5"
}
template {
 source = "/usr/local/etc/consul-template/template.d/redis.conf.tmpl"
 destination = "/tmp/redis.conf"
 command = "echo `date` regenerate /tmp/redis.conf >> /tmp/consul-template.log"
}

/usr/local/etc/consul-template/template.d/redis.conf.tmpl

consul-templateのTemplate Engineはなんかこれらしい

template — The Go Programming Language

Package template implements data-driven templates for generating textual output. To generate HTML output, see package…

golang.org

Plain text
# Hostname: {{env "HOSTNAME"}}
{{$hostname := env "HOSTNAME"}}
{{$key := printf "%s%s" "/service/redis/" $hostname}}
{{with $redis := key $key | parseJSON}}
{{if $redis}}
{{if $redis.daemonize}}
daemonize {{$redis.daemonize}}
{{else}}
daemonize no
{{end}}
pidfile /var/run/redis.pid
{{if $redis.port}}
port {{$redis.port}}
{{else}}
port 6379
{{end}}
tcp-backlog 511
timeout 0
tcp-keepalive 0
databases 16
dbfilename dump.rdb
dir ./
save 900 1
save 300 10
save 60 10000
{{end}}
{{end}}

※検証目的でconsul-templateのREADME.mdに無いことやってます。

  • 環境変数からhostname取得
  • keyにhostname含める(将来的にいろんな事態を予測してユニークなkeyに分岐可能か調べたかったの例
  • parseJSON
  • if elseも

/usr/local/etc/consul-template/param.d/redis.yaml

まずは簡単に

Plain text
---
daemonize: "yes"
port: 6379

consul kvに詰める!

前はperlで自前で書いてたけどyaml2json便利!

peter-edge/go-yaml2json

go-yaml2json — Convert YAML to JSON for Go

github.com

※なんて爽やかなw

Plain text
curl -X PUT \
-d `cat /usr/local/etc/consul-template/param.d/redis.yaml | yaml2json` \
http://localhost:8500/v1/kv/service/redis/`hostname`

consul kvの確認!

Plain text
curl -s http://localhost:8500/v1/kv/service/redis/`hostname` \
| jq ‘.[].Value’ -r | base64 -d | jq .
{
  "daemonize": "yes",
  "port": 6379
}

よっしゃconsul-template -onceで実行!

Plain text
consul-template \
-config=/usr/local/etc/consul-template/conf.d/redis.conf \
-once \
-log-level debug
2015/03/20 05:36:21 [INFO] (runner) creating new runner (dry: false, once: true)
2015/03/20 05:36:21 [DEBUG] merging with config at /usr/local/etc/consul-template/conf.d/redis.conf
2015/03/20 05:36:21 [INFO] (runner) creating consul/api client
2015/03/20 05:36:21 [DEBUG] (runner) setting address to 127.0.0.1:8500
2015/03/20 05:36:21 [DEBUG] (runner) setting basic auth
2015/03/20 05:36:21 [INFO] (runner) creating Watcher
2015/03/20 05:36:21 [INFO] (runner) starting
2015/03/20 05:36:21 [DEBUG] (runner) running initial templates
2015/03/20 05:36:21 [INFO] (runner) running
2015/03/20 05:36:21 [DEBUG] (runner) checking template /usr/local/etc/consul-template/template.d/redis.conf.tmpl
2015/03/20 05:36:21 [DEBUG] (brain) does not have data for "key(/service/redis/yh-src)"
2015/03/20 05:36:21 [INFO] (runner) was not watching 1 dependencies
2015/03/20 05:36:21 [INFO] (watcher) adding "key(/service/redis/yh-src)"
2015/03/20 05:36:21 [DEBUG] (watcher) "key(/service/redis/yh-src)" starting
2015/03/20 05:36:21 [INFO] (runner) diffing and updating dependencies
2015/03/20 05:36:21 [DEBUG] (view) "key(/service/redis/yh-src)" starting fetch
2015/03/20 05:36:21 [DEBUG] ("key(/service/redis/yh-src)") querying consul with &{Datacenter: AllowStale:true RequireConsistent:false WaitIndex:0 WaitTime:1m0s Token:}
2015/03/20 05:36:21 [DEBUG] ("key(/service/redis/yh-src)") Consul returned {"daemonize":"yes","port":6379}
2015/03/20 05:36:21 [INFO] (view) "key(/service/redis/yh-src)" received data from consul
2015/03/20 05:36:21 [DEBUG] (runner) receiving dependency "key(/service/redis/yh-src)"
2015/03/20 05:36:21 [INFO] (brain) remembering "key(/service/redis/yh-src)"
2015/03/20 05:36:21 [INFO] (runner) running
2015/03/20 05:36:21 [DEBUG] (runner) checking template /usr/local/etc/consul-template/template.d/redis.conf.tmpl
2015/03/20 05:36:21 [INFO] (brain) recalling data for "key(/service/redis/yh-src)"
2015/03/20 05:36:21 [INFO] (brain) recalling data for "key(/service/redis/yh-src)"
2015/03/20 05:36:21 [DEBUG] (runner) checking ctemplate &{Source:/usr/local/etc/consul-template/template.d/redis.conf.tmpl Destination:/tmp/redis.conf Command:echo `date` regenerate /tmp/redis.conf > /tmp/consul-template.log}
2015/03/20 05:36:21 [DEBUG] (runner) wouldRender: true, didRender: true
2015/03/20 05:36:21 [DEBUG] (runner) appending command: echo `date` regenerate /tmp/redis.conf > /tmp/consul-template.log
2015/03/20 05:36:21 [INFO] (runner) diffing and updating dependencies
2015/03/20 05:36:21 [DEBUG] (runner) "key(/service/redis/yh-src)" is still needed
2015/03/20 05:36:21 [DEBUG] (runner) running command: `echo `date` regenerate /tmp/redis.conf > /tmp/consul-template.log`
2015/03/20 05:36:21 [INFO] (runner) once mode and all templates rendered
2015/03/20 05:36:21 [INFO] (runner) stopping
2015/03/20 05:36:21 [INFO] (watcher) stopping all views
2015/03/20 05:36:21 [DEBUG] (watcher) stopping "key(/service/redis/yh-src)"

/tmp/redis.confできたか確認!

Plain text
# Hostname: yh-src
daemonize yes
pidfile /var/run/redis.pid
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
databases 16
dbfilename dump.rdb
dir ./
save 900 1
save 300 10
save 60 10000

ちょっと隙間が気になるけど・・・w

/tmp/consul-template.log

Plain text
Fri Mar 20 05:54:10 UTC 2015 regenerate /tmp/redis.conf

daemonizeとportしかparameter化されてないからちょっと追加

/usr/local/etc/consul-template/param.d/redis.yaml

Plain text
---
daemonize: “yes”
port: 6379
timeout: 60
save:
 - seconds: 900
   changes: 1
 - seconds: 300
   changes: 10
 - seconds: 60
   changes: 10000

/usr/local/etc/consul-template/template.d/redis.conf.tmpl編集

Plain text
aaa
# Hostname: {{env "HOSTNAME"}}
{{$hostname := env "HOSTNAME"}}
{{$key := printf "%s%s" "/service/redis/" $hostname}}
{{with $redis := key $key | parseJSON}}
{{if $redis}}
{{if $redis.daemonize}}
daemonize {{$redis.daemonize}}
{{else}}
daemonize no
{{end}}
pidfile /var/run/redis.pid
{{if $redis.port}}
port {{$redis.port}}
{{else}}
port 6379
{{end}}
tcp-backlog 511
{{if $redis.timeout}}
timeout {{$redis.timeout}}
{{else}}
timeout 0
{{end}}
tcp-keepalive 0
databases 16
dbfilename dump.rdb
dir ./
{{range $redis.save}}
save {{.seconds}} {{.changes}}{{end}}
{{end}}
{{end}}
  • redisのsaveをListのHashでやれるか?

consul kvに詰めて確認してconsul-template実行!

Plain text
curl -X PUT \
-d `cat /usr/local/etc/consul-template/param.d/redis.yaml | yaml2json` \
http://localhost:8500/v1/kv/service/redis/`hostname`
curl -s http://localhost:8500/v1/kv/service/redis/`hostname` | jq '.[].Value' -r | base64 -d | jq .
consul-template \
-config=/usr/local/etc/consul-template/conf.d/redis.conf \
-once \
-log-level debug

確認!

Plain text
# Hostname: yh-src
daemonize yes
pidfile /var/run/redis.pid
port 6379
tcp-backlog 511
timeout 60
tcp-keepalive 0
databases 16
dbfilename dump.rdb
dir ./
save 900 1
save 300 10
save 60 10000

うえ〜〜い!

いけるかも・・・いける気がしてきた・・・

起動も以下の2種類ができそう

  • -onceでマニュアルかつオーケストレーションでキック
  • watchモードでdaemon化してイベントドリブンにやる

補足

まだいろいろ考えないといけないけど単純に-onceで起動するならもう使えるかな

  • 実はconfdも検証してみたけど、consul-templateの方がいい感じ ※ただしconfdはtomlに設定したkeyがない場合、templateにあるkeyがない場合コケてfile出力されないから安心かも
  • 両方ともgoのtemplate使ってるからSyntaxは似てるかも
  • template側を修正したらdaemonも再起動が必要
  • consul側にトラブルが起きた際の制御どうする

転載:consul-template!

この記事は Medium(@yusuke_h) からの転載です。