Serfを使用したNatサーバの冗長化

Serfを使用してNatサーバの冗長化構成を構築したので、メモ。

Serfとは

Serfとは、オーケストレーションを実現できるツールで、Serf間でクラスタを組み、クラスタのメンバーの状態変化時にスクリプトを実行したりできる。
管理サーバを特に必要とせず導入できるため、比較的導入は簡単である。
監視単位としては、ノード単位の監視しかできず、サービスレベルでの監視をしたい場合はConsulで実現できる。

www.serfdom.io

Serfを利用するケースとしては、以下のようなものがある

・WEBサーバ増減に伴うロードバランサーへの登録、解除

・サーバ増減に伴うDNSの更新

Natサーバの冗長構成

今回はAWSにてNatサーバの冗長構成を構築した。

MultiAz環境にてそれぞれのAZにNatサーバを配置し、各WebサーバはそれぞれのAZのNatサーバに通信するように、NatサーバはActive/Active構成とした。

冗長構成の仕組みとしては、2台のNatサーバにインストールしたSerfでクラスタを組み、WebサーバのサブネットのDefaultGWをNatサーバになるようにRouterを設定。
以下構成図の場合、サブネットごとに紐づけるルートテーブルを分けている(WEB1⇒Nat1にルーティング、WEB2⇒Nat2にルーティングするルートテーブル)
障害時には、それぞれのルートテーブルをスクリプトで向きを切り替え、正常のNatサーバ側へ通信するような構成にした。

構成図は以下のとおり。

f:id:hangyoun:20151103005358p:plain

障害時の挙動は、serf.confにて設定する。
以下例では、メンバーのFail,Leave時にスクリプトを実行している。

{
  "tags" : {
    "role" : "nat"
  },
  "event_handlers" : [
    "member-failed=/etc/serf/route-change.sh",
    "member-leave=/etc/serf/route-change.sh"
  ],
  "reconnect_timeout" : "1h",
  "reconnect_interval" : "10s",
  "enable_syslog" : true
}


スクリプトの中で、1台のNatサーバが障害時にもう1台のNatサーバにリクエストがいくように、Routerの向き先を変更する。

Router_A="AAAA"
Router_B="BBBB"
dest_cidr_block="0.0.0.0/0"

instance_id=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
nic_id=`aws ec2 describe-instances --filters "Name=instance-id,Values=$instance_id" | jq '.Reservations[].Instances[].NetworkInterfaces[].NetworkInterfaceId' | sed -e 's/"//g'`


for routeid in $Router_A $Router_B
do
  aws ec2 replace-route --route-table-id $routeid \
                        --destination-cidr-block $dest_cidr_block \
                        --network-interface-id $nic_id
done

起動時のクラスタ組み込み

再起動などの場合に、毎回Natサーバを手動でクラスタに組み込むのは嫌なので、自動で組み込むように設定。

SerfはMulticastでのオートディスカバリによる自動組み込みをサポートしているが、AWS環境ではMulticastを使用できないとのことなので、
今回はinitスクリプトの中でタグを利用し、クラスタの自動組み込みをすることにした。

各NatサーバのTagに、Serf-joinタグを付与し、タグからNatのIPアドレスを検索する。
そして、検索したIPアドレスをSerf joinでメンバー登録する。
以下例でタグからサーバのIPアドレスを取得している。

aws ec2 describe-instances --filters "Name=tag-key,Values=serf-join" "Name=tag-value,Values=true" | jq '.Reservations[].Instances[].PrivateIpAddress' | sed -e 's/"//g'`