Happy Elements 株式会社 カカリアスタジオ Advent Calendar 2016 初日の記事となります.本日の担当はインフラグループの @nagizero です.よろしくお願いします.
背景
トラフィックを分散させるため,ひとつのドメイン名に対して複数のホストを割り当てる必要がありました.
対応方針
ヘルスチェックありの DNS ラウンドロビンを設定することにしました.
単純な DNS ラウンドロビン1 では,異常なホストも名前解決の結果に含まれてしまいます.正常なホストだけがラウンドロビンの対象となるようにするため,異常なホストを検知して切り離すための仕組みが必要となります.
ホスト
HTTP サーバとして 52.68.43.35 と 54.199.248.145 が稼働しているものとします.待ち受けポートはデフォルトの 80 番です.
ヘルスチェック
Route53 でヘルスチェックを作成します.各ホストに対して HTTP で 80 番ポートをチェックします. HTTP ステータスコード 200 が返ってくれば当該ホストは健全とみなされます.ヘルスチェックの Name はそれぞれ test-01 (52.68.43.35), test-02 (54.199.248.145) とします.
DNS ラウンドロビン
設定
ラウンドロビンの対象とするホストを,それぞれ異なるレコードセットとして登録する必要があります.ただし, A レコードの値はどちらも等しい値 (今回は health-check.foo.bar) とします.
下図は 52.68.43.35 の登録例です.注意すべき点は Routing Policy です.等しい A レコードを別々のレコードセットとして登録するために Routing Policy を Weighted としています.また,今回は重み付けが不要のため (両ホストに送信されるリクエストの数を等しくしたいため) 両ホストとも Weight は 1 としています.このあたりの設定は Route 53 のルーティング系の設定 (simple/weighted/latency) が参考になりました.
動作確認
両ホストをレコードセットに登録したところで DNS ラウンドロビンの動作確認を行います. health-check.foo.bar を名前解決して IP アドレスを確認します.下記コマンドにより約 1 秒おきに名前解決し,約 1 時間分 (3,600 回分) のログを確認しました.
% while true; do nslookup health-check.foo.bar 8.8.8.8 | tail -n 2 | head -n 1 >> dns-round-robin.log; sleep 1; done
% wc -l dns-round-robin.log
3600 dns-round-robin.log
% grep '52.68.43.35' dns-round-robin.log | wc -l
1821
% grep '54.199.248.145' dns-round-robin.log | wc -l
1779
両ホストに対して均等にリクエストが割り振られていることが確認できました.
DNS とヘルスチェックの連携
DNS ラウンドロビンの動作を確認できたので,次にヘルスチェック機能を組み込みます.
レコードセット設定
レコードセットを編集します. Associate with Health Check を Yes に変更し, Health Check to Associate は先ほど設定したヘルスチェックを選択します.下図は 52.68.43.35 のレコードセットですので,ヘルスチェックに test-01 を指定しています. 54.199.248.145 のレコードセットではヘルスチェックに test-02 を指定します.
動作確認
異常なホストがラウンドロビンの対象から切り離されることと,正常な (復旧した) ホストがラウンドロビンの対象に追加されることを確認します.
今回のヘルスチェック間隔は 30 秒であり, 3 回連続で HTTP ステータスコードが 200 でない場合にホストを異常とみなす設定となっています.よって,異常なホストは約 90 秒で切り離される想定です.同様に,復旧したホストは約 90 秒でラウンドロビンに追加される想定です.
ホストの切り離し
54.199.248.145 の HTTP サーバを停止してしばらくした後に health-check.foo.bar の名前解決結果として 52.68.43.35 のみが返されるようになることを確認します.
% while true; do date '+%Y-%m-%d %H:%M:%S'; nslookup health-check.foo.bar 8.8.8.8 | tail -n 2 | head -n 1; sleep 1; done
2016-06-02 18:39:37
Address: 54.199.248.145
2016-06-02 18:39:38
Address: 52.68.43.35
2016-06-02 18:39:39
Address: 54.199.248.145
.
.
.
2016-06-02 18:41:26
Address: 54.199.248.145
2016-06-02 18:41:27
Address: 52.68.43.35
.
.
.
HTTP server を停止してから 109 秒に 54.199.248.145 が返されたのを最後に,以降は 52.68.43.35 のみが返されるようになりました.
ホストの復旧
54.199.248.145 の HTTP サーバを起動してしばらくした後に health-check.foo.bar の名前解決結果として 54.199.248.145 が返されるようになることを確認します.ホストの復旧までの想定時間は,ホストの切り離しと同様に 90 秒前後です.
% while true; do date '+%Y-%m-%d %H:%M:%S'; nslookup health-check.foo.bar 8.8.8.8 | tail -n 2 | head -n 1; sleep 1; done
2016-06-02 19:14:17
Address: 52.68.43.35
.
.
.
2016-06-02 19:15:38
Address: 52.68.43.35
2016-06-02 19:15:39
Address: 54.199.248.145
.
.
.
HTTP サーバを起動してから 82 秒後に 54.199.248.145 が返されるようになりました.
ホストが全滅した場合
正常な状態と同様に両ホストの IP アドレスが返されました
おわりに
Route 53 でヘルスチェックありの DNS ラウンドロビンを設定して動作確認を行いました.今回は Routing Policy に Weighted を利用しながらも重み付けは行いませんでしたが, Weight の数値によって各ホストに割り振るリクエスト数の比率を自由に設定できます.他にも Routing Policy には Latency, Failover, Geolocation があり,様々な設定が可能です.必要に応じて適切な手段を選択していきたいと思います.
-
例えばひとつの A レコードに複数の IP アドレスを登録する方法があります ↩