HEARTBEATS

NSDとDNSラウンドロビン

   

権威ネームサーバのNSDがDNSラウンドロビンに対応したので、NSDとDNSラウンドロビンについて紹介します。

なお、この記事は『UnboundとDNSラウンドロビン』の姉妹編的な位置づけです。ほぼ同じようなDNSラウンドロビンについての説明があります。

NSDとは

DNSサーバのソフトウェアとしてはBINDがデファクトスタンダードのような位置づけになっていますが、BIND以外にも様々なDNSサーバのソフトウェアがあります。その中の一つとしてNSDがあります。

NSDはオランダのNLnet LabsのW.C.A. Wijngaards氏が中心となって開発が行われています。NSDは元々はルートサーバ用として開発されましたが、現在のバージョンでは普通の権威ネームサーバとしての機能はほぼ実装されており、代表的な権威ネームサーバのソフトウェアの一つとなっています

ちなみに、ルートサーバのHとKとLはNSDで運用されています。次のようにしてHのルートサーバのソフトウェアを調べると"NSD 4.1.0"が返ってきます。

$ dig @h.root-servers.net. version.server. CH TXT +norec
中略
;; ANSWER SECTION:
version.server.		0	CH	TXT	"NSD 4.1.0"

NSDについての詳細は以下のサイトをご覧ください。

DNSラウンドロビン

DNSラウンドロビンはRRset(リソース レコード セット)に複数のRR(リソース レコード)があるときにレスポンス毎にRRの順番を巡回させる機能です。

$ dig @ns3.google.com. www.l.google.com. +norec ←1回目
;; ANSWER SECTION:
www.l.google.com.	300	IN	A	173.194.117.240
www.l.google.com.	300	IN	A	173.194.117.242
www.l.google.com.	300	IN	A	173.194.117.244
www.l.google.com.	300	IN	A	173.194.117.243
www.l.google.com.	300	IN	A	173.194.117.241

$ dig @ns3.google.com. www.l.google.com. +norec ←2回目
;; ANSWER SECTION:
www.l.google.com.	300	IN	A	173.194.117.242
www.l.google.com.	300	IN	A	173.194.117.244
www.l.google.com.	300	IN	A	173.194.117.243
www.l.google.com.	300	IN	A	173.194.117.240
www.l.google.com.	300	IN	A	173.194.117.241

ウェブブラウザはRRset内の最初のRRで示されたIPアドレスへアクセスする傾向があるため、この機能を使うと、結果としてウェブサーバへのリクエストの分散ができるので、簡易的な負荷分散クラスターを構成するために使われることがあります。実際のところ、負荷の分散ではなくリクエストの分散です。

ただし、OSやライブラリによってはRFC 6724 "Default Address Selection for Internet Protocol Version 6 (IPv6)"の"longest matching prefix"に従うケースもあるため、簡易的な負荷分散クラスターの目的としては一概に期待通り動作するとは限りません。

NSDとDNSラウンドロビン

NSDでは当初より長らくDNSラウンドロビンの機能は実装されてきませんでした。実装されてこなかった理由としてはNSDの付属文書"REQUIREMENTS"に次のような説明があります。

B.2. Explicit NON-Requirements
4. No creeping featurism

NSD will not implement any functionality that is not strictly necessary for the task of authoritative name serving. Examples: round robin sequence of RRset members in consecutive answers, Also no dynamic plugins.

簡単に訳してみますと次のようになります。

B.2. 明確に不要な機能
4. 忍び寄る多機能性

NSDは権威ネームサーバを提供するタスクに厳密には必要でないどんな機能も実装しません。例えば、連続した回答におけるRRsetメンバーのラウンドロビンシーケンスや動的なプラグインなどもです。

恐らくUnboundのときと同じくDNSラウンドロビンを実装することによる複雑さの増加や処理のオーバーヘッドが気になっていた面もあるのではないかと思われます。

しかし、2014年9月4日にリリースされたNSD 4.1.0からDNSラウンドロビンの機能が実装されました。これにより、NSDでDNSラウンドロビンの機能が使えるようになりました。なお、デフォルトでは無効になっているので、利用するためには設定ファイルnsd.confに"round-robin: yes"を設定する必要があります。

round-robinの実装

ここからは、どのようにNSDのDNSラウンドロビンが実装されているかを見ていきましょう。

ソースコードのpacket.cに記述されている関数packet_encode_rrset()の中を見てみましょう。

まず、非常に重要である1行を見てみます。

        static int round_robin_off = 0;

round_robin_offという変数が定義されています。static宣言されていますので関数の実行が終了しても値を保持しています。後述しますがクエリーごとに値が1つずつ増加するため、結果としてはクエリーの通し番号のようになります。このことがここでは非常に重要です。

次にラウンドロビンを行うかを判断する箇所を見てみます。

        int do_robin = (round_robin && section == ANSWER_SECTION &&
                query->qtype != TYPE_AXFR && query->qtype != TYPE_IXFR);

do_robinという変数にラウンドロビンを行うかをセットします。ここで、round_robinは設定ファイルにおける設定round-robinがyesであれば1が、noであれば0がセットされます。round_robinが1で、かつANSWERセクションであり、ゾーン転送(クエリータイプがAXFRあるいはIXFR)でなければ、ラウンドロビンを実施するということになります。

ラウンドロビンのオフセット値は次の式で求められます。

        if(do_robin && rrset->rr_count)
                start = (uint16_t)(round_robin_off++ % rrset->rr_count);
        else    start = 0;

round_robin_offを回答のレコード数(rrset->rr_count)で割った余りがオフセット値startとなります。その後、round_robin_offの値は1だけ増やされます。round_robin_offはstatic宣言されているため、値を保持しており、次回のクエリーでは先ほど増えたround_robin_offの値を使うため、オフセット値も変わり、ラウンドロビンとなるわけです。

ラウンドロビンの実装コードとしてはこれだけです。非常にシンプルです。

今までの説明でわかるとおり、オフセット値はRRsetごとに保持しているのではなく、共通で保持している変数round_robin_offから算出されます。そのため、よく知られているRRsetごとにオフセット値が順番に巡回するラウンドロビンとは動作が異なることになります。

それでは実際にクエリーを行った結果を見てみましょう。

まず、次のような2つのRRsetを定義しましょう。それぞれ4つのRRを持っています。

test1.example.jp.	3600	IN	TXT	"1"
test1.example.jp.	3600	IN	TXT	"2"
test1.example.jp.	3600	IN	TXT	"3"
test1.example.jp.	3600	IN	TXT	"4"

test2.example.jp.	3600	IN	TXT	"1"
test2.example.jp.	3600	IN	TXT	"2"
test2.example.jp.	3600	IN	TXT	"3"
test2.example.jp.	3600	IN	TXT	"4"

まず、1回目を引いてみましょう。

$ dig @ns1.example.jp. test1.example.jp. TXT +norec
;; ANSWER SECTION:
test1.example.jp.	3600	IN	TXT	"1"
test1.example.jp.	3600	IN	TXT	"2"
test1.example.jp.	3600	IN	TXT	"3"
test1.example.jp.	3600	IN	TXT	"4"

1つ目のレコードの値が"1"になりました。

2回目を引いてみます。

$ dig @ns1.example.jp. test1.example.jp. TXT +norec
;; ANSWER SECTION:
test1.example.jp.	3600	IN	TXT	"2"
test1.example.jp.	3600	IN	TXT	"3"
test1.example.jp.	3600	IN	TXT	"4"
test1.example.jp.	3600	IN	TXT	"1"

1つ目のレコードの値が"2"になりました。これは期待通りにラウンドロビンしていますね。

3回目はtest2.example.jpの方を引いてみます。

$ dig @ns1.example.jp. test1.example.jp. TXT +norec
;; ANSWER SECTION:
test2.example.jp.	3600	IN	TXT	"3"
test2.example.jp.	3600	IN	TXT	"4"
test2.example.jp.	3600	IN	TXT	"1"
test2.example.jp.	3600	IN	TXT	"2"

このレコードは初めて引きましたが、1つ目のレコードの値は"3"になりました。

再びtest1.example.jpを引いてみます。

$ dig @ns1.example.jp. test1.example.jp. TXT +norec
;; ANSWER SECTION:
test1.example.jp.	3600	IN	TXT	"4"
test1.example.jp.	3600	IN	TXT	"1"
test1.example.jp.	3600	IN	TXT	"2"
test1.example.jp.	3600	IN	TXT	"3"

1つ目のレコードの値は"4"になりました。

このことからもわかるように、ラウンドロビン用の共通の値を持っていて、それがクエリーごとに増加し、その値を元にオフセット値が求められます。NSDではこのような実装を行うことで非常にシンプルなラウンドロビンを行っています。

なお、実際にこの機能を使ってみるとわかりますが、BINDのようにオフセットが順番に巡回するわけではなく、共通の変数round_robin_offからオフセット値を決定しているため、クエリーのタイミングによっては同じ順番が連続して返されることがあります。サイコロを振った回数が増えるほど出目の回数は均等になるのと同じように、それほど大きな問題はないでしょう。

ということで、今回は今月リリースされたNSD 4.1.0の新機能のDNSラウンドロビンについて紹介してみました。

株式会社ハートビーツの技術情報やイベント情報などをお届けする公式ブログです。