ども、cloudpackかっぱ (@inokara) です。ご無沙汰してます。

追記(お詫び)

  • consul と serf を混同していた為、「consul はマルチキャストでノードを探索して勝手にクラスタを組んでくれる」というのは誤りを記載しておりました
  • consul は serf とは異なりマルチキャストによるメンバージョインは出来ないようです
  • 実際には -join オプションが必要になります
  • 尚、consul 0.5 からは atlas-join オプションが加わり Atlas を利用したクラスタ構成が可能です

はじめに

AWS 環境で Consul を起動する際に -join オプションを使ってジョイン先を指定していたけど、そもそもジョイン先を確認しなければいけないのはカッタルイなあと思ったので、ジョイン先を AWS のタグで管理して consul 起動の際にタグからジョイン先を判断出来たら面白そうだなと思って試してみた。そもそもタグを利用しなくても出来る方法があるかもしれないのであくまでもアイデアの一つとして…。

参考

consul がクラスタを構成する際には…

ざっくり言うと…

  • consul はマルチキャストでノードを探索して勝手にクラスタを組んでくれる
  • マルチキャストが利用出来ない場合には -join オプションでジョイン先を指定する必要がある

ということなので…

AWS 環境の場合には…

残念ながら

Multicast が利用出来ないので consul を起動すると漏れ無くスプリットブレイン状態となる。
以下のようなイメージ。
consul で EC2 Tag 使って Join 先を動的に管理する: 何も対処しない場合は スプリットブレイン状態

今までは…

以下のように実行してクラスタを構成している。

/usr/local/bin/consul agent -config-dir=/etc/consul.d -join ${ジョイン先}

以下のようなイメージ。
consul で EC2 Tag 使って Join 先を動的に管理する: 今までは明示的にコマンドで指定してクラスタを構成

思いついたアイデア

  • AWS のタグに consul-join というタグを作る(タグ名はあくまでも例)
  • consul-jointrue が入っているノード(インスタンス)にジョインする
  • リーダーノード(curl -s -X GET http://localhost:8500/v1/status/leader)の場合には consul-jointrue にする
  • consul watch オプションを使ってクラスタ内に変化があれば consul-join タグをチェックしてリーダーが変わっていればタグを更新する

以下のようなイメージ。
consul で EC2 Tag 使って Join 先を動的に管理する: アイディアの構成図
※図では consul-leader というタグ名になっているが適宜読み替える

実際にやってみる

consul の設定

consul の設定。

{
  "bootstrap": false,
  "bootstrap_expect": 3,
  "server": true,
  "data_dir": "/opt/consul",
  "log_level": "INFO",
  "node_name": "${HOSTNAME}",
  "enable_syslog": true
}

consul watch の設定。

{
  "watches": [
    {
      "type": "nodes",
      "handler": "/opt/bin/consul-check.sh"
    }
  ]
}

IAM ロール

EC2 タグを操作する必要があるので、最低でも以下の権限を付与しておく必要がある。

  • ec2:DescribeInstances
  • ec2:DescribeTags
  • ec2:CreateTags
  • ec2:DeleteTags

consul を起動する init スクリプト

consul を init スクリプト経由で起動する際のスクリプトは以下のとおり。

なんの変哲も無い init スクリプトだけど、以下のようにジョイン先を EC2 タグから取得している。

(sinp)

JOIN_TO=`aws --region ap-northeast-1 ec2 describe-instances --query 'Reservations[].Instances[].[PrivateIpAddress]' --output text --filter 'Name=tag:consul-leader,Values=true'`

(snip)

consul watch で利用する監視スクリプト

スクリプトは以下のとおり。

動作確認

現状

マネジメントコンソールの状態。
consul で EC2 Tag 使って Join 先を動的に管理する: マネジメントコンソールの状態

各ノードの状態。
consul で EC2 Tag 使って Join 先を動的に管理する: 各ノードの状態。

リーダーノードを確認。

$ curl -s -X GET http://localhost:8500/v1/status/leader
"xxx.xx.xx.37:8300"

リーダーノードの確認も上記のように API にて取得することが可能です。

適当に consul leave

リーダーとなっている consul ノードで consul leave を実行する。
consul で EC2 Tag 使って Join 先を動的に管理する: consul leave した状態

従来リーダーだったノードにて consul leave を実行後に他のノードにてリーダーノードを確認すると先ほどと変わっている。更に、マネジメントコントールを見ると…
consul で EC2 Tag 使って Join 先を動的に管理する: consul leave してマネジメントコンソールを確認
おお。

再度ジョインする場合には…

先ほど consul leave したノードを再度ジョインさせる場合には…

/etc/init.d/consul start

で consul を起動するとジョイン先を EC2 タグから取得して正常にジョインが完了する。
consul で EC2 Tag 使って Join 先を動的に管理する: 再度ジョイン

ということで…

本日の知見

  • マルチキャストでは無い環境では consul のクラスタを構成する際には -join オプションは必要(だと思う)
  • consul watch を利用すればクラスタノードに変化が発生した際に何らかの処理を行うことが出来る
  • 今回はタグの書き換えを行ったがもちろん通知等も可能

注意

  • そもそも consul をまだ理解しきれていないので他にやり方があるかもしれない

お疲れ様でした。

元記事はこちらです。
consul を EC2 で使う時のちょっとしたアイデア(EC2 Tag 使って Join 先を動的に管理する)