話題の記事

Amazon DNSはレートリミットを超えるとタイムアウトになる

2018.02.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

2018/02/07 当初本記事ではNXDOMAINレスポンスの注意喚起を主旨としていましたが、コンテンツサーバーでNXDOMAINを返す要因も考えられるため主旨を変更しました。

ども、大瀧です。

Amazon VPCには、同ネットワーク内から名前解決のためのDNSキャッシュサーバー(Amazon DNS)が提供されます。AWSのドキュメントに以下の記述を見つけたので、レートリミットに引っかかるときにどのような挙動になるのか検証してみたのが本ブログです。結果としては、タイトルの通りタイムアウトが観測されました。

DNS の制限 各 Amazon EC2 インスタンスは Amazon が提供する DNS サーバーへ送信できるパケット数をネットワークインターフェイスあたり最大 1024 パケット/秒に制限しています。この制限を増やすことはできません。Amazon が提供する DNS サーバーでサポートされる 1 秒あたりの DNS クエリの数は、クエリのタイプ、レスポンスのサイズ、および使用中のプロトコルにより異なります。スケーラブルな DNS アーキテクチャの詳細および推奨については、「Amazon VPC のハイブリッドクラウド DNS ソリューション」ホワイトペーパーを参照してください。

おことわり : 本検証は独自に行ったものであり、AWSの仕様を説明するものではありません。予告なく挙動が変わる可能性がある点をご注意ください。

検証環境

  • OS : Amazon Linux 2 RC
  • インスタンスタイプ : c5.9xlarge

同一ENIからのアクセスとするために、Dockerでコンテナを1000個実行しコンテナでdigを無限ループさせる作戦でやってみました。

  • hostloop.bash : コンテナの中で実行するシェルスクリプト
  • Dockerfile : Dockerイメージの作成で使用
  • docker-run.bash : 作成したDockerイメージで1000個コンテナを実行するシェルスクリプト

hostloop.bash

#!/bin/bash

while [ 1 ]
do
  host -t a example.com
done

Dockerfile

FROM amazonlinux:2

RUN yum install -y bind-utils
COPY hostloop.bash /hostloop.bash

CMD bash /hostloop.bash

docker-run.bash

#!/bin/bash

for i in {0..1000};do
  sudo docker run -d hostloop:latest
done

それぞれのファイルを作成し、以下のコマンドで動かしてみます。

実行するとDNSサーバーへの問い合わせがまともに動かなくなる&キャッシュサーバーに多大な負荷をかけるので、検証用EC2を用意して試すことを強くお奨めします。PCやMacで実行すると、Dockerの後始末含め多分大変です。

$ sudo yum install -y docker
$ sudo service docker start
$ docker build -t hostloop .
  :(略)
$ chmod +x docker-run.bash
$ ./docker-run.bash
597735e291bf
  :(略)
$

実行したコンテナのログを見てみると。。。。

$ sudo docker logs -f 597735e291bf
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
;; connection timed out; trying next origin
Host example.com not found: 3(NXDOMAIN)
;; connection timed out; trying next origin
;; connection timed out; no servers could be reached
socket.c:1937: internal_send: 172.31.0.2#53: Invalid argument
socket.c:1937: internal_send: 172.31.0.2#53: Invalid argument
;; connection timed out; trying next origin
;; connection timed out; no servers could be reached
;; connection timed out; trying next origin
;; connection timed out; no servers could be reached
socket.c:1937: internal_send: 172.31.0.2#53: Invalid argument
socket.c:1937: internal_send: 172.31.0.2#53: Invalid argument
;; connection timed out; trying next origin
socket.c:1937: internal_send: 172.31.0.2#53: Invalid argument
;; connection timed out; no servers could be reached
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

正常な結果が返ってくる中に、タイムアウトとNXDOMAIN(該当レコードなし)の結果が混じっていました。以下の結果はクライアント(Linux)の制約によるもののようなので、今回の制限とは直接の関連は無さそうです。

socket.c:1937: internal_send: 172.31.0.2#53: Invalid argument

また、記事冒頭のドキュメントで案内されている「Amazon VPC のハイブリッドクラウド DNS ソリューション」ホワイトペーパーに以下の記載があるため、タイムアウトがAmazon DNSとしての挙動と読めます。

When you’re designing a scalable solution for name resolution, you need to consider this limit because failure to do so can result in queries to the Amazon DNS server to go unanswered if the limit is breached.

考察とまとめ

Amazon DNSがレートリミットを超えるときの挙動を検証してみました。アプリケーション側では、タイムアウトに対応する例外処理としてリトライを実装するのが良いでしょう。とはいえ、まずはENIあたり秒間1024パケットのラインを越えないように設計するのが最も効果的な対策になるでしょう。

ちなみに、今回検証することになったきっかけは、Amazon ECS環境で同様のエラーになるという相談が社内からあったためです。最近ECSにはコンテナごとにENIを割り当てる構成がサポートされたので、そちらを利用することで回避できるのではと考えます。