【負荷分散】DNSラウンドロビンによるプロキシの冗長化

複数のサーバで負荷分散や可用性を確保するには、一般的にはサーバロードバランサを使います。ロードバランサがない環境で、少しでも負荷を分散し、可用性を高める方法の1つが DNS ラウンドロビンです。

この記事では、クライアントからプロキシサーバ2台への接続を DNS ラウンドロビンで分散し、片方のプロキシが止まったときにブラウザ(Microsoft Edge)がどう切り替わるかを、パケットキャプチャと net-export のログで確認します。

この記事でわかること
  1. DNS ラウンドロビンで、DNS レスポンスがどう変わるか
  2. プロキシ1号機の電源 OFF とサービス停止で、ブラウザの切り替わり方がどう違うか
  3. DNS ラウンドロビンの注意点と、ロードバランサとの違い
インターネットDNS の A レコードproxy-sv A 10.10.1.100proxy-sv A 10.10.1.101DNSサーバ10.10.1.1プロキシ1号機10.10.1.100プロキシ2号機10.10.1.101DNSクエリDNSレスポンスプロキシ接続障害時の切り替え先クライアント(Edge) 10.10.1.50
図1 検証構成

DNS ラウンドロビンの仕組み

DNS ラウンドロビンは、1つの名前に複数の IP アドレスを登録し、DNS レスポンスの中のアドレスの並び順を問い合わせのたびに入れ替えることで、接続先を分散する方法です。

今回の DNS サーバは Active Directory の DNS サーバ(ドメイン名 hirotanoblog.local)です。1つの名前 proxy-sv に、A レコードを2つ(10.10.1.100 と 10.10.1.101)登録します。

画面イメージについて

この記事の画面は、実際の画面ではなく、検証時の画面から見るポイントを抜き出して描いたイメージです。バージョンによって、項目の並びや表記は変わります。

DNS マネージャー(A レコード)画面イメージ
名前種類データ
1proxy-svHost (A)10.10.1.100
proxy-svHost (A)10.10.1.101
図2 proxy-sv の A レコード(実際の画面ではなく、登録内容のポイントを示したイメージです)
No.表示意味
1proxy-sv が2行同じ名前に、2つの A レコード(10.10.1.100 と 10.10.1.101)を登録している

DNS ラウンドロビンというと、問い合わせのたびにアドレスを1つずつ順番に返すと思われがちです。実際には、登録した A レコードがすべてレスポンスに入り、その並び順が問い合わせのたびに入れ替わります。

DNSサーバ① 10.10.1.100② 10.10.1.101① 10.10.1.101② 10.10.1.100① 10.10.1.100② 10.10.1.101DNS レスポンス(アドレスの並び)プロキシ1号機10.10.1.100プロキシ2号機10.10.1.101
図3 DNS ラウンドロビンで接続先が分かれるイメージ

クライアントから nslookup で proxy-sv を3回問い合わせた結果が次の画面です。どの回もアドレスは2つ返っていて、並び順だけが変わっています。

コマンドプロンプト(nslookup)画面イメージ
C:\Users\administrator>nslookup既定のサーバー:  UnKnownAddress:  10.10.1.1 > proxy-svサーバー:  UnKnownAddress:  10.10.1.1 名前:    proxy-sv.hirotanoblog.local1Addresses:  10.10.1.101          10.10.1.100 > proxy-svサーバー:  UnKnownAddress:  10.10.1.1 名前:    proxy-sv.hirotanoblog.local2Addresses:  10.10.1.100          10.10.1.101 > proxy-svサーバー:  UnKnownAddress:  10.10.1.1 名前:    proxy-sv.hirotanoblog.local3Addresses:  10.10.1.101          10.10.1.100
図4 proxy-sv を3回問い合わせた結果(実際の画面ではなく、表示内容のポイントを示したイメージです)
No.表示意味
11回目10.10.1.101 → 10.10.1.100 の順
22回目10.10.1.100 → 10.10.1.101 の順(入れ替わった)
33回目10.10.1.101 → 10.10.1.100 の順(1回目と同じ並びに戻った)

クライアントのアプリケーションは、受け取ったアドレスを先頭から順に使うのが一般的です。問い合わせごとに先頭のアドレスが変わるので、クライアントの接続先がプロキシ1号機と2号機に分かれます。

補足
  • Windows Server の DNS サーバでは、ラウンドロビンが既定で有効です。DNS サーバのプロパティの[詳細設定]タブで無効にもできます。
  • Windows Server の DNS サーバには、ネットマスクの順序付け(netmask ordering)という機能もあり、既定で有効です。クライアントと同じサブネット(既定では /24 単位)のアドレスがあれば、それを先頭に並べます。今回はクライアントも2つのアドレスも 10.10.1.0/24 にあるので、2つのアドレスの間でラウンドロビンが働いています。
  • OS によっては、受け取ったアドレスを独自の規則(IPv6 と IPv4 のどちらを優先するか、など)で並べ替えてから使います。このため、どのクライアントでも DNS レスポンスの先頭に接続するとは限りません。

動作確認

今回は、プロキシサーバの負荷分散を DNS ラウンドロビンで実装します。クライアントのプロキシ設定には、IP アドレスではなく名前の proxy-sv(ポート 3128)を指定します。名前で指定するので、ブラウザは DNS で解決したアドレスに接続します。ブラウザは Microsoft Edge を使い、片方のプロキシが止まったときの切り替わりも確認します。

Windows の設定(プロキシ)画面イメージ
手動プロキシ セットアップ
1プロキシ サーバーを使う オン
2アドレスhttp://proxy-sv
ポート3128
図5 クライアントのプロキシ設定(実際の画面ではなく、設定項目のポイントを示したイメージです。実際の画面ではオン/オフはトグルスイッチです)
No.項目設定内容
1プロキシ サーバーを使うオン
2アドレス、ポートhttp://proxy-sv、3128。プロキシを名前で指定する

Edge は、この Windows のプロキシ設定を使って通信します。次のキャプチャは、https://www.bing.com へプロキシ1号機経由で接続したあと、プロキシ1号機の電源を OFF にしたときのものです(説明に不要なパケットは省いています)。

パケットキャプチャ(Wireshark)画面イメージ
No.TimeSourceDestinationProtocolInfo
110.00000010.10.1.5010.10.1.1DNSStandard query 0x3c6c A proxy-sv.hirotanoblog.local
20.00060110.10.1.110.10.1.50DNSStandard query response 0x3c6c A proxy-sv.hirotanoblog.local A 10.10.1.100 A 10.10.1.101
230.00761710.10.1.5010.10.1.100HTTPCONNECT www.bing.com:443 HTTP/1.1
3460.02058610.10.1.5010.10.1.100TCP49579 → 3128 [SYN] Seq=0 Win=64240 …
560.78735910.10.1.5010.10.1.100TCP51164 → 3128 [SYN] Seq=0 Win=64240 …
661.05189410.10.1.5010.10.1.100TCP[TCP Retransmission] … 49579 → 3128 [SYN] …
761.81470510.10.1.5010.10.1.100TCP[TCP Retransmission] … 51164 → 3128 [SYN] …
869.86870710.10.1.5010.10.1.100TCP56174 → 3128 [SYN] Seq=0 Win=64240 …
969.97080110.10.1.5010.10.1.100TCP55978 → 3128 [SYN] Seq=0 Win=64240 …
1070.64794710.10.1.5010.10.1.100TCP59694 → 3128 [SYN] Seq=0 Win=64240 …
1170.74283810.10.1.5010.10.1.100TCP60862 → 3128 [SYN] Seq=0 Win=64240 …
1271.31979510.10.1.5010.10.1.100TCP54738 → 3128 [SYN] Seq=0 Win=64240 …
1371.41288610.10.1.5010.10.1.100TCP[TCP Retransmission] … 56174 → 3128 [SYN] …
41471.41413510.10.1.5010.10.1.101HTTPCONNECT www.bing.com:443 HTTP/1.1
図6 プロキシ1号機の電源 OFF 前後のキャプチャ(実際の画面ではなく、表示内容のポイントを示したイメージです。Length 列と Info の後半は省いています)
No.表示意味
1No.1〜2 DNSproxy-sv の名前解決。レスポンスは 10.10.1.100、10.10.1.101 の順
2No.3 CONNECT先頭のプロキシ1号機(10.10.1.100)に接続
3No.4〜13 SYN電源 OFF のプロキシ1号機へ接続を試みるが、応答がない(再送が続く)
4No.14 CONNECTプロキシ2号機(10.10.1.101)に切り替わる
クライアント10.10.1.50DNSサーバ10.10.1.1プロキシ1号機10.10.1.100プロキシ2号機10.10.1.101No.1 DNSクエリ(proxy-sv)No.2 DNSレスポンス(A 10.10.1.100, A 10.10.1.101)No.3 CONNECT www.bing.com:443電源OFFNo.4〜13 SYN(再送)、応答なしNo.14 CONNECT www.bing.com:443(2号機へ)
図7 キャプチャのやり取りの流れ

DNS クエリーとレスポンス(No.1〜2)

クライアントが DNS サーバに proxy-sv を問い合わせます。DNS レスポンスには、10.10.1.100、10.10.1.101 の順でアドレスが2つ入っています。次の画面は、DNS レスポンスの Answers 部分です。

パケットキャプチャ(Wireshark) No.2 DNS レスポンス画面イメージ
▾ Answers1  ▸ proxy-sv.hirotanoblog.local: type A, class IN, addr 10.10.1.100  ▸ proxy-sv.hirotanoblog.local: type A, class IN, addr 10.10.1.101
図8 DNS レスポンスの Answers(実際の画面ではなく、表示内容のポイントを示したイメージです)
No.表示意味
1type A が2つA レコードが2つ返っている。先頭は 10.10.1.100

プロキシ1号機に接続(No.3)

Edge は、先頭のアドレス 10.10.1.100(プロキシ1号機)の 3128 番ポートに接続します。HTTPS のサイトにプロキシ経由でアクセスするときは、ブラウザがプロキシに CONNECT www.bing.com:443 を送り、プロキシに宛先までのトンネルを作ってもらいます。暗号化された通信は、そのトンネルの中を流れます。

プロキシ1号機の電源 OFF(No.4〜13)

プロキシ1号機の電源を OFF にします。Edge はプロキシ1号機の 3128 番ポートに TCP の接続(SYN)を試みますが、電源が切れているので応答が返らず、SYN の再送が続きます。キャプチャでは大量に出るため、一部を省いています。

プロキシ2号機に接続(No.14)

接続先が 10.10.1.101(プロキシ2号機)に切り替わり、Web 通信が復旧します。このキャプチャでは、プロキシ1号機への最初の SYN(No.4)から約11秒後に、プロキシ2号機への CONNECT(No.14)が出ています。

障害時のブラウザの挙動

プロキシサーバに障害が起きたときの Edge の動きを、net-export で取得したログで確認しました。net-export の使い方は、net-export の使い方 を参照してください。

プロキシ1号機の電源を OFF にした場合

プロキシ1号機(10.10.1.100)経由で通信しているところで、電源を OFF にします。Edge は 10.10.1.100:3128 へ TCP 接続を試みますが、応答が返らない状態が続きます。

net-export ログ(5087: SOCKET)画面イメージ
5087: SOCKEThttps://www.bing.comStart Time: 2022-08-01 19:44:29.303 t=123989 [st=   0] +SOCKET_ALIVE  [dt=8015]                    --> source_dependency = 5086 (SSL_CONNECT_JOB)t=123989 [st=   0]   +TCP_CONNECT  [dt=8015]1                      --> address_list = ["10.10.1.100:3128","10.10.1.101:3128"]                      --> aliases = []                      --> name_resolution_handle = 02t=123989 [st=   0]     +TCP_CONNECT_ATTEMPT  [dt=8015]                        --> address = "10.10.1.100:3128"3t=132004 [st=8015]        SOCKET_CLOSEDt=132004 [st=8015] -SOCKET_ALIVE
図9 プロキシ1号機への接続試行(実際の画面ではなく、表示内容のポイントを示したイメージです)
No.表示意味
1address_list接続先の候補。DNS レスポンスの順に 10.10.1.100、10.10.1.101 が並んでいる
2TCP_CONNECT_ATTEMPT先頭の 10.10.1.100:3128 に接続を試みる。応答がないまま約8秒(dt=8015)続く
3SOCKET_CLOSED接続できないまま、このソケットを閉じる

この接続試行が終わる前(t=130408、試行開始から約6.4秒後)に、Edge は proxy-sv の名前解決をやり直しています。今度は、アドレスの並びが 10.10.1.101、10.10.1.100 の順に入れ替わっています。

net-export ログ(5109: HOST_RESOLVER_IMPL_JOB)画面イメージ
5109: HOST_RESOLVER_IMPL_JOBproxy-svStart Time: 2022-08-01 19:44:35.722 t=130408 [st=0] +HOST_RESOLVER_MANAGER_JOB  [dt=1]                --> dns_query_types = ["A"]1                --> host = "proxy-sv"                --> network_isolation_key = "null null"                --> secure_dns_mode = 0                --> source_dependency = 5108 (SSL_CONNECT_JOB) ⋮ (中略)t=130408 [st=0]   -HOST_RESOLVER_MANAGER_PROC_TASK2                   --> address_list = ["10.10.1.101:0","10.10.1.100:0"]                   --> aliases = []                   --> name_resolution_handle = 0t=130408 [st=0]    HOST_RESOLVER_MANAGER_ATTEMPT_FINISHED                   --> attempt_number = 1t=130409 [st=1] -HOST_RESOLVER_MANAGER_JOB
図10 proxy-sv の名前解決のやり直し(実際の画面ではなく、表示内容のポイントを示したイメージです)
No.表示意味
1host名前解決する名前。プロキシの名前 proxy-sv
2address_list結果は 10.10.1.101、10.10.1.100 の順。DNS ラウンドロビンで並びが入れ替わっている

Edge は新しい並びの先頭 10.10.1.101:3128(プロキシ2号機)へ接続し、CONNECT を送ります。これで Web 通信が復旧します。

net-export ログ(5110: SOCKET)画面イメージ
5110: SOCKEThttps://www.bing.comStart Time: 2022-08-01 19:44:35.723 t=130409 [st=   0] +SOCKET_ALIVE  [dt=14516+]                    --> source_dependency = 5108 (SSL_CONNECT_JOB)t=130409 [st=   0]   +TCP_CONNECT  [dt=0]1                      --> address_list = ["10.10.1.101:3128","10.10.1.100:3128"]                      --> aliases = []                      --> name_resolution_handle = 02t=130409 [st=   0]     TCP_CONNECT_ATTEMPT  [dt=0]                        --> address = "10.10.1.101:3128"t=130409 [st=   0]   -TCP_CONNECT                      --> local_address = "10.10.1.50:62785"                      --> remote_address = "10.10.1.101:3128"t=130409 [st=   0]   +HTTP_TRANSACTION_TUNNEL_SEND_REQUEST  [dt=0]t=130409 [st=   0]      HTTP_TRANSACTION_SEND_TUNNEL_HEADERS3                         --> CONNECT www.bing.com:443 HTTP/1.1                             Host: www.bing.com:443                             Proxy-Connection: keep-alive
図11 プロキシ2号機への接続(実際の画面ではなく、表示内容のポイントを示したイメージです)
No.表示意味
1address_listやり直した名前解決の結果。先頭が 10.10.1.101 になっている
2TCP_CONNECT_ATTEMPT10.10.1.101:3128 にすぐ接続できた(dt=0)
3CONNECT www.bing.com:443プロキシ2号機にトンネルの作成を依頼する

プロキシ1号機のサービスを停止した場合

次は、電源 OFF ではなく、プロキシ1号機のプロキシサービスだけを停止します。サーバ自体は動いているので、3128 番ポートへの接続はサーバから拒否(RST)されます。Edge は Windows のソケットのエラーで接続の失敗を知り、同じ候補リストの次のアドレス 10.10.1.101 へ接続します。

net-export ログ(1970: SOCKET)画面イメージ
1970: SOCKEThttps://www.bing.comStart Time: 2022-08-01 20:00:54.201 t= 88745 [st=   0] +SOCKET_ALIVE  [dt=29946+]                   --> source_dependency = 1969 (SSL_CONNECT_JOB)t= 88745 [st=   0]   +TCP_CONNECT  [dt=2063]1                     --> address_list = ["10.10.1.100:3128","10.10.1.101:3128"]                     --> aliases = []                     --> name_resolution_handle = 02t= 88745 [st=   0]     +TCP_CONNECT_ATTEMPT  [dt=2063]                       --> address = "10.10.1.100:3128"3t= 90808 [st=2063]     -TCP_CONNECT_ATTEMPT                       --> os_error = 10061t= 90808 [st=2063]      SOCKET_CLOSED4t= 90808 [st=2063]      TCP_CONNECT_ATTEMPT  [dt=0]                       --> address = "10.10.1.101:3128"t= 90808 [st=2063]   -TCP_CONNECT                     --> local_address = "10.10.1.50:62954"                     --> remote_address = "10.10.1.101:3128"t= 90809 [st=2064]   +HTTP_TRANSACTION_TUNNEL_SEND_REQUEST  [dt=0]t= 90809 [st=2064]      HTTP_TRANSACTION_SEND_TUNNEL_HEADERS                        --> CONNECT www.bing.com:443 HTTP/1.1                            Host: www.bing.com:443                            Proxy-Connection: keep-alive
図12 サービス停止時の切り替わり(実際の画面ではなく、表示内容のポイントを示したイメージです)
No.表示意味
1address_list接続先の候補。10.10.1.100、10.10.1.101 の順
2TCP_CONNECT_ATTEMPT先頭の 10.10.1.100:3128 に接続を試みる
3os_error = 10061Windows ソケットのエラー WSAECONNREFUSED(接続拒否)。このログでは、拒否と判定されるまで約2秒(dt=2063)かかっている
4TCP_CONNECT_ATTEMPT同じ候補リストの次のアドレス 10.10.1.101:3128 にすぐ接続できた

電源 OFF とサービス停止で、切り替わり方は次のように違いました。Edge と同じく、Chrome でも同様の動作になりました。

障害の種類プロキシ1号機の反応Edge の動き
電源 OFF応答なし(SYN の再送が続く)1号機への接続を試み続ける間に名前解決をやり直し、入れ替わった並びの先頭(2号機)に接続した
サービス停止接続を拒否(RST)エラー 10061 を受け取り、同じ候補リストの次のアドレス(2号機)にすぐ接続した

DNS ラウンドロビンの注意点

DNS ラウンドロビンは、DNS サーバの設定だけで始められる手軽な方法ですが、次の点に注意が必要です。

  • サーバの生死を見ていない。DNS サーバはプロキシの状態を監視していないので、障害中のサーバのアドレスも DNS レスポンスに含め続けます。そのため、クライアントは障害中のサーバへの接続を試み、切り替わるまで待たされることがあります。
  • 切り替え方はアプリケーション次第。複数のアドレスを受け取ったあと、次のアドレスを試すか、どれくらい待つかはアプリケーションの実装で決まります。今回の Edge と Chrome の動きが、ほかのアプリケーションでも同じとは限りません。
  • DNS のキャッシュが効く。クライアントやキャッシュ DNS サーバは、TTL の間は DNS レスポンスを保持します。そのため、分散は名前解決の単位になり、接続数やサーバの負荷が均等になるとは限りません。
比較項目DNS ラウンドロビンサーバロードバランサ
必要なものDNS サーバの設定だけロードバランサ(専用機器や仮想アプライアンスなど)
サーバの監視しないヘルスチェックで監視し、障害中のサーバには振り分けない
障害時の切り替えクライアント(アプリケーション)任せロードバランサが残りのサーバに振り分ける
分散の単位名前解決の単位(キャッシュの影響を受ける)接続(セッション)の単位

負荷分散と可用性の安定度は、やはりロードバランサのほうが上です。DNS ラウンドロビンを本番環境に適用する場合は、実際に使うアプリケーションで、障害時の動きを十分に検証してください。

まとめ

項目内容
DNS ラウンドロビン1つの名前に複数の A レコードを登録し、DNS レスポンスの並び順を問い合わせのたびに入れ替える
接続先の決まり方クライアントは受け取ったアドレスを先頭から使うのが一般的。並び順が変わるので、接続先が分かれる
電源 OFF の場合応答がなく、SYN の再送が続く。今回の Edge は名前解決をやり直し、プロキシ2号機に切り替わった
サービス停止の場合RST で接続を拒否され(エラー 10061)、同じ候補リストの次のアドレスにすぐ切り替わった
注意点サーバの監視をしない、切り替えはアプリケーション次第、DNS キャッシュの影響を受ける

コメント