こんにちは!はんだです。
今日はDNSサーバの可用性向上についてのエントリです。

DNSサーバの障害時の影響を防ぐ方法として、
単純にDNSサーバを複数設置して/etc/resolv.confに記入するという方法がありますが、
その方法には、タイムアウトの待ち時間という落とし穴があります。

/etc/resolv.conf
nameserver 192.168.0.11  //DNS1のIPアドレス
nameserver 192.168.0.12  //DNS2のIPアドレス

のように設定したとして、
1行目のDNS1が落ちた状態の場合、2行目のDNS2に移行するまでに、設定次第ですが問い合わせの度に毎回1~数秒のタイムアウトが発生します。名前解決の頻度が高いサービスの場合、この僅かな待ち時間がサービス全体へのパフォーマンスに影響してきます(この問題については[24時間365日]サーバ/インフラを支える技術(技術評論社)の 「DNSサーバの冗長化」の項が詳しいです)。

この問題への対策として、LVS+Keepalivedなどを利用して冗長性を高める方法がありますが、それに加え

複数のDNSサーバをローカルのUnboundの参照先として指定することで冗長性を高める

という方法を検証してみました。

(なお、今回の記事ではLVS構成の設定及び検証については言及しませんので、その部分については他の解説記事などをご参照下さい)

検証環境

検証用の環境として以下を準備しました。
sv-web01       192.168.0.1       Webサーバ。Unboundを導入
sv-dns-lvs01     192.168.0.11       node01とnode02でLVS。Keepalivedで死活監視
  sv-dns-node01  192.168.0.101     BIND
  sv-dns-node02  192.168.0.102     BIND
sv-dns-lvs02     192.168.0.12       node03とnode04でLVS。Keepalivedで死活監視
  sv-dns-node03  192.168.0.103     BIND
  sv-dns-node04  192.168.0.104     BIND

Unbound



sv-web01にはUnboundをインストールし、/etc/resolv.confには以下のようにnameserverを設定します。
nameserver 127.0.0.1       //ローカルのUnbound
nameserver 192.168.0.11    //sv-dns-lvs01
nameserver 192.168.0.12    //sv-dns-lvs02

Unboundの設定

Unboundとは、
  • シンプルな設計になっており高速で動作する。BINDより処理性能が良い(2~3倍)
  • 設定ファイルは1つのみ(unbound.conf)で内容もシンプルなため、設定が容易
  • キャッシュ汚染に対する耐性を強化した設計&DNSSECをサポート
  • デフォルト設定で高いセキュリティが確保された状態で動く
という特徴を持った、新定番となっているDNSキャッシュサーバです。

デフォルトの設定でもキャッシュサーバとして問題なく動作しますが、今回は社内の権威DNSサーバへ問い合わせを行いたいため、stub-zoneの設定を行います。
stub-zoneは、特定のドメインの名前解決について、指定したDNS権威サーバに問い合わせを行うための設定です。

/etc/unbound/unbound.confで、参照先DNSサーバとしてsv-dns-lvs01とsv-dns-lvs02を指定します。
 stub-zone:
        name:"example.com"
        stub-addr:192.168.0.11
        stub-addr:192.168.0.12
stub-zone:
        name: "0.168.192.in-addr.arpa"
        stub-addr:192.168.0.11
        stub-addr:192.168.0.12
Unboundはデフォルトではプライベートアドレスの問い合わせを行わないので、以下の設定を追加します。
private-domain: "example.com"
(中略)
local-zone: "0.168.192.in-addr.arpa" transparent

また、特定のドメインについて、指定したDNSキャッシュサーバに再帰問い合わせを行わせるときにはforward-zoneを設定します。
forward-zoneは、nameオプションに "." を指定することで、全ての問い合わせを転送することもできます。
自社ドメイン以外は別のDNSサーバ(外部DNSなど)を指定して直接問い合わせを行わせるような場合は、こちらで設定します。
forward-zone:
  name: "."
  forward-addr: 203.0.113.111

検証したいこと 

1. stub-zoneに複数DNSサーバを記述した場合、どのような問い合わせ順序になるか。先に書かれているものが優先なのか、ラウンドロビンなのか。

2. stub-zoneに書いたDNSサーバのうち1つが利用不可になった場合に、どのような挙動になるか。

3. 利用不可になった理由が、サーバ自体がダウンした場合と、DNSサービスが利用不可になった場合では、挙動に違いはあるか。resolv.confでは、サーバ自体のダウンではなくDNSサービスが落ちていた場合にタイムアウトまでに長くかかることがあるが、Unboundではどうか。

検証手順

0.事前準備
テスト用のスクリプトを作成します。sv-web01から他のサーバにnslookupを行い、最後にunboundのキャッシュを削除するというだけの単純なものです。
SERVER_LIST="sv-web02 sv-web03 sv-db01 ・・・・・ sv-db20"
while : ; do

    for SERVER in $SERVER_LIST; do
        echo $(date "+%F %H:%M:%S.%N") $(nslookup $SERVER | head -n 1)
    done

    unbound-control -q flush_zone example.com
    sleep 1

done | tee test.log
このスクリプトを実行しながらサーバやサービスを落として、どのサーバで名前解決が行われているかを確認します。
DNSサーバに何の問題も起こっていない時点では以下のようなログになります。
127.0.0.1、つまりローカルのUnboundで名前解決が行われているのがわかります。
test.log

2016-03-23 10:01:47.710318602 Server: 127.0.0.1
2016-03-23 10:01:47.720166753 Server: 127.0.0.1
2016-03-23 10:01:47.730168114 Server: 127.0.0.1
2016-03-23 10:01:47.739930041 Server: 127.0.0.1
2016-03-23 10:01:47.750394444 Server: 127.0.0.1
2016-03-23 10:01:47.760969879 Server: 127.0.0.1
2016-03-23 10:01:47.770984202 Server: 127.0.0.1
2016-03-23 10:01:47.780918869 Server: 127.0.0.1
2016-03-23 10:01:47.791561248 Server: 127.0.0.1
2016-03-23 10:01:47.802000414 Server: 127.0.0.1
2016-03-23 10:01:47.812778410 Server: 127.0.0.1
2016-03-23 10:01:47.823340386 Server: 127.0.0.1
2016-03-23 10:01:47.833941263 Server: 127.0.0.1
2016-03-23 10:01:47.844795349 Server: 127.0.0.1



検証1. 
問い合わせ順序の確認

各LVSサーバでDNSサービスへの接続状態を確認しながら検証用スクリプトを実行してみます。
[root@i-dns-lvs01 ~]# watch ipvsadm -Ln

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
UDP 192.168.0.11:53 rr
-> 192.168.0.101    Route     0 0 271 
-> 192.168.0.102     Route    0 0 272
[root@i-dns-lvs02 ~]# watch ipvsadm -Ln

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
UDP 192.168.0.12:53 rr
-> 192.168.0.103    Route     0 0 269 
-> 192.168.0.104     Route    0 0 271 

InActConn(一定時間以内の接続数)がほぼおなじ数字で推移しています。
結論:stub-zoneに複数のDNSサーバを記入した場合、順番に均等に問い合わせが割り振られる 。


検証2.ローカルのUnboundのstub-addrに登録してあるsv-dns-lvs0xのどちらかが落ちた場合


上記スクリプトを実行した状態で、片方のLVS(sv-dns-lvs01)を落としてみます。
[root@sv-dns-lvs01 ~]# service network stop
2016-03-23 11:02:39.654407380 Server: 127.0.0.1
2016-03-23 11:02:39.666387084 Server: 127.0.0.1
2016-03-23 11:02:39.678305469 Server: 127.0.0.1
2016-03-23 11:02:39.690497052 Server: 127.0.0.1
2016-03-23 11:02:39.702769957 Server: 127.0.0.1
2016-03-23 11:02:39.714040967 Server: 127.0.0.1
2016-03-23 11:02:39.725401558 Server: 127.0.0.1
2016-03-23 11:02:39.736123310 Server: 127.0.0.1
2016-03-23 11:02:39.746930229 Server: 127.0.0.1
2016-03-23 11:02:39.758402234 Server: 127.0.0.1
2016-03-23 11:02:39.769120682 Server: 127.0.0.1
2016-03-23 11:02:39.781072673 Server: 127.0.0.1
2016-03-23 11:02:41.296289719 Server: 127.0.0.1
2016-03-23 11:02:41.308759832 Server: 127.0.0.1
2016-03-23 11:02:41.422097623 Server: 127.0.0.1
2016-03-23 11:02:41.434119357 Server: 127.0.0.1
2016-03-23 11:02:42.849200158 Server: 127.0.0.1
2016-03-23 11:02:42.861381999 Server: 127.0.0.1
2016-03-23 11:02:42.873155039 Server: 127.0.0.1
2016-03-23 11:02:42.884901850 Server: 127.0.0.1
2016-03-23 11:02:42.896203445 Server: 127.0.0.1
2016-03-23 11:02:42.906861984 Server: 127.0.0.1
2016-03-23 11:02:42.917331042 Server: 127.0.0.1
2016-03-23 11:02:42.927999125 Server: 127.0.0.1
2016-03-23 11:02:42.938445488 Server: 127.0.0.1
2016-03-23 11:02:42.948876159 Server: 127.0.0.1
2016-03-23 11:02:42.959507049 Server: 127.0.0.1
2016-03-23 11:02:42.970042488 Server: 127.0.0.1

LVSサーバを落としたタイミングで若干処理スピードの低減が発生しましたが、ほぼタイムラグなくsv-dns-lvs01を切り離して引き続きUnboundで名前解決を行ってくれるようです。

結論:stub-addrとして指定した複数のDNSのうち一つが落ちた場合でも、Unbound内で切り離しをして名前解決が続行される。



検証3. sv-dns-lvs0x自体が落ちている場合と、sv-dns-lvs0xにぶらさがっている2つのnodeの両方のDNSサービスが利用不可の場合とで挙動は変わるか


LVS用のサーバが落ちた場合、Unboundがうまく切り離してくれることがわかりました。
では、LVSサーバではなく、node側のDNSサービスが両方落ちた場合はどうでしょうか。

sv-dns-lvs01で利用している、sv-dns-node01とsv-dns-node02の両方で、namedサービスを停止してみます。
[root@sv-dns-node01 ~]# service named stop 
[root@sv-dns-lvs01 ~]# ipvsadm -Ln 

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
UDP 192.168.0.11:53 rr
-> 192.168.0.101    Route     0 0 0 
-> 192.168.0.102     Route    0 0 0 
2016-03-23 11:26:07.253176732 Server: 127.0.0.1
2016-03-23 11:26:07.263412675 Server: 127.0.0.1
2016-03-23 11:26:07.375477267 Server: 127.0.0.1
2016-03-23 11:26:07.385581955 Server: 127.0.0.1
2016-03-23 11:26:07.395923909 Server: 127.0.0.1
2016-03-23 11:26:07.507452435 Server: 127.0.0.1
2016-03-23 11:26:09.022468808 Server: 127.0.0.1
2016-03-23 11:26:09.635581100 Server: 127.0.0.1
2016-03-23 11:26:09.646748991 Server: 127.0.0.1
2016-03-23 11:26:10.712805036 Server: 127.0.0.1
2016-03-23 11:26:10.725051359 Server: 127.0.0.1
2016-03-23 11:26:10.734853913 Server: 127.0.0.1
2016-03-23 11:26:10.744873105 Server: 127.0.0.1
2016-03-23 11:26:10.754697333 Server: 127.0.0.1
2016-03-23 11:26:10.764829874 Server: 127.0.0.1
2016-03-23 11:26:10.877325395 Server: 127.0.0.1
2016-03-23 11:26:10.887888348 Server: 127.0.0.1
2016-03-23 11:26:10.898024415 Server: 127.0.0.1
2016-03-23 11:26:10.907979399 Server: 127.0.0.1
検証2と同様、若干の処理速度低下はありますが、その後は引き続きUnboundでの名前解決が継続されました。

結論:LVS自体が落ちている場合とサービスが使用不可の場合で挙動は変わらない

まとめ 

検証により、DNSサーバ障害時のパフォーマンスへの影響をローカルUnboundを導入することによりより減らすことができるだろうという結論が得られました。
ここで一度、Unboundを導入することのメリット・デメリットを整理したいと思います。

ローカルにUnboundを導入するメリット
・サーバ障害時も、キャッシュに保存されている分はTTL内(社内サーバなら1時間)であればローカルで名前解決ができる。
・DNSサーバを冗長化しておいた場合に、1台のみの障害であればresolv.confに列記するだけの場合よりもパフォーマンスへの影響を小さくできる。
・ローカルのネットワーク流量やDNSサーバの負荷を抑制できる。
・ローカルで名前解決ができることによるパフォーマンス向上が期待できる。

ローカルにUnboundを導入するデメリット(懸念事項)
・導入コスト(各サーバへの設定)
・運用コスト(local-dataを使う場合はサーバ追加時のlocal-dataの再配布、ドメイン&セグメント追加時のunbound.conf設定修正)
・キャッシュが新しいトラブルの原因になったり、トラブル時の切り分けがしづらくなる可能性
・サーバ負荷軽減やパフォーマンス向上については、名前解決の問い合わせが少ないサーバや、新しい名前解決が多いサーバの場合には、効果が小さい。

これを踏まえ、弊社では、
・TTL内での名前解決が多く発生し、キャッシュにより問い合わせが大きく減らせる
・DNS障害時のタイムラグがサービスのパフォーマンスに影響を与えることが想定できる
など、Unbound導入によるメリットが大きいサーバから順に導入していくことになりました。
また、導入・運用のコストについては、AnsibleやFabricといった自動化ツールを導入することである程度カバーできると考えています。

参考URL

・5分でわかるUnbound http://gihyo.jp/admin/feature/01/unbound/0001
・Unboundサーバ運用Tips http://gihyo.jp/admin/feature/01/unbound/0004
・キャッシュサーバの設定 http://dnsops.jp/event/20140626/cache-config.pdf