LinuxにおいてローカルDNSキャッシュが必要なケースとAmazon Linux 2023における設定例

記事タイトルとURLをコピーする

こんにちは、久保です。

AWSでEC2やコンテナを利用する際、OSとしてLinuxを利用される方も多いと思います。

LinuxではDNSによる名前解決結果がキャッシュされないことはご存知でしょうか。また、複数のDNSサーバの参照が必要なケースでは、耐障害性を確保するために注意すべき動作があります。

本記事では、EC2でAmazon Linux 2023を利用する際の、ローカルDNSキャッシュが必要なケースとAmazon Linux 2023における設定についてご紹介いたします。

ローカルDNSキャッシュとは

その名のとおり、Linuxサーバ上(ローカル)で、DNSによる名前解決結果をキャッシュする仕組みです。

Amazon Linux 2023においてはデフォルトでローカルDNSキャッシュは構成されていない状態となっています。(2025年4月執筆現在。厳密にはsystemd-resolvedが入っていますがDNSキャッシュとしては無効化されています。)

例えば、

$ curl https://s3.amazonaws.com

のようにしてs3.amazonaws.comの名前解決が発生する場合について考えてみます。

ローカルDNSキャッシュがない場合

ローカルDNSキャッシュがない、デフォルトの状態ではリクエストが発生する度に、DNSサーバへ名前解決リクエストを行います。 1万回アクセスすれば1万回の名前解決が必要となります。

※図は、VPCのDHCP Option Setにてデフォルトの設定(Route 53 Resolverを参照)を利用いただいている場合のイメージです。*1

ローカルDNSキャッシュがない場合

ローカルDNSキャッシュがある場合

ローカルDNSキャッシュがある場合は、初回の名前解決の際にs3.amazonaws.com.の名前解決結果がキャッシュされ、一定期間の間は同じ名前がリクエストされた場合に外部のDNSサーバへの問合せは行わず、キャッシュから返す動作となります。

ローカルDNSキャッシュがある場合

では、どのような場合にローカルDNSキャッシュを構成するのがよいのでしょうか?

ローカルDNSキャッシュが必要となるケース

参照先DNSサーバが複数あるケース

例えば企業内ネットワークの独自ドメイン用の権威サーバをオンプレミス側で運用されており、AWSとDirect Connect等で接続しハイブリッドなご利用をされている場合です。
独自ドメインの解決のために独自のDNSキャッシュサーバを用意し、各サーバにそれらDNSキャッシュサーバを参照させるケースが考えられます。

複数のDNSサーバを参照させる場合、Amazon Linux 2023では /etc/resolv.conf に以下のように複数のDNSサーバを参照する方法が取られるかもしれません。

※/etc/resolv.confは直接編集できません。/etc/systemd/resolved.confでの指定等が必要なためご注意ください。*2

$ cat /etc/resolv.conf
nameserver 192.168.1.100
nameserver 192.168.1.200

例を図にすると以下のような構成となります。

単一障害点(SPOF)とならないよう、DNSキャッシュサーバを2台用意し、各クライアントが2台参照するように設定しています。

しかし、この設定には問題があります。

例えば、DNSキャッシュサーバ#1(192.168.1.100)がダウンした場合、上記のような/etc/resolv.confの設定では以下のような挙動となります。

$ curl https://s3.amazonaws.com
  1. 192.168.1.100 に 名前解決リクエスト
  2. 5秒待ちタイムアウト
  3. 192.168.1.200 に 名前解決リクエスト
  4. 名前解決結果を取得

最終的にDNSキャッシュサーバ#2を利用して名前解決することができましたが、5秒ものタイムアウト待ちが発生しています。

最初に申し上げたとおり、Linuxでは名前解決結果をキャッシュしないため、この挙動が毎回繰り返されます。 つまり、上記の例ですとS3へのリクエストが毎回必ず5秒のオーバーヘッドがかかることとなり、システムとしては許容できないレベルの遅延が発生することとなります。

このようなケースで、ローカルDNSキャッシュを構成して/etc/resolv.confではnameserver 127.0.0.1のようにローカルDNSキャッシュのみ参照する構成にしておくことで、ローカルDNSキャッシュ側で、障害が発生している参照先DNSサーバの迅速な切り離しや切り替えが実現可能です。

ローカルDNSキャッシュがある場合

※切り替え、切り離しにかかる速度や仕様は利用するソフトウェアにより変わります(dnsmasq, unbound, bind)

このように、Linuxサーバから複数のDNSサーバを参照する必要があるような構成において、ローカルDNSキャッシュが必要となる場合があります。


Route 53 アウトバウンドエンドポイントを利用することで独自ドメインのDNSクエリをオンプレミスなど独自のDNSサーバで行うことも可能です。*3 EC2やコンテナが参照するDNSのIPアドレスは1つにするが運用効率や耐障害性として最も推奨されます。ここでは構成上、DNSキャッシュサーバを複数参照する必要がある場合を想定した例をご紹介しています。


短時間に大量の名前解決が必要なケース

2点目は、短時間に大量の名前解決が必要なケースです。

Linuxから参照するDNSサーバがRoute 53 Resolver1つで済む場合であっても、AWSの名前解決のクォータを超過する場合には対応が必要となります。

Route 53 Resolverは、利用可能なリクエストが秒間1,024パケットまでという制限があります。

Amazon DNS について理解する - Amazon Virtual Private Cloud

秒間であるため、かなりの高パフォーマンスが必要なサーバでなければ本クォータに達することは少ないと考えられますが、制限以上のパフォーマンスが必要な場合には、対応が必要となります。

本制限は上限を変更することができないため、ローカルDNSキャッシュを構成することで、繰り返し発生する名前解決について外部へのリクエストを減らすなどの対応を行うこととなります。

ローカルDNSキャッシュの設定方法

以下のような仕組みを利用して構成することが可能です。

  • systemd-resolved
  • dnsmasq
  • unbound
  • bind

本記事では、AWSのre:Postでも紹介されている、dnsmasqを利用した方法をご紹介します。

repost.aws

dnsmasqは非常に軽量で、設定もシンプルなOSSのDNSソフトウェアです。

環境

  • 2025年4月21日時点の最新版のAmazon Linux 2023を利用します
  • EC2が存在するVPCのCIDRは172.31.0/16とします

構成前の所要時間

dnsmasqがない状態で、/etc/resolv.confが以下のようになっているものとします。

$ cat /etc/resolv.conf
nameserver 192.168.1.100  # 存在しないサーバです。タイムアウトします。
nameserver 172.31.0.2

この状態でcurlでアクセスすると、まず192.168.1.100を利用して名前解決を試行しますが、当該サーバは存在しません。タイムアウトまでに5秒を要するため、リクエストが処理完了するまで5秒強の時間がかかります。

$ time curl https://s3.amazonaws.com

real    0m5.477s
user    0m0.019s
sys 0m0.000s

dnsmasqを設定

前出のre:Postの記事を参考にdnsmasqをインストール、設定します。

dnsmasq パッケージをインストールします。

sudo dnf makecache --refresh
sudo dnf install -y dnsmasq

デフォルト設定のバックアップを行います。

sudo cp /etc/dnsmasq.conf{,.bak}

メイン設定を作成します。

cat <<'EOF' | sudo tee /etc/dnsmasq.conf
# https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
# https://thekelleys.org.uk/gitweb/?p=dnsmasq.git

### Server Configuration

# The alternative would be just 127.0.0.1 without ::1
listen-address=::1,127.0.0.1

# Listen on port 53
port=53

# Listen only on the specified interface(s).
# For a local only DNS resolver use interface=lo + bind-interfaces
# See for more details: https://serverfault.com/a/830737
interface=lo

# dnsmasq binds to the wildcard address, even if it is listening 
# on only some of the interfaces. It then discards requests that 
# it shouldn't reply to. This has the advantage of working even 
# when interfaces come and go and change address.
bind-interfaces
#bind-dynamic

# DO NOT listen on the specified interface(s). 
#except-interface=eth0
#except-interface=eth1

# Turn off DHCP and TFTP Server features.
#no-dhcp-interface=eth0

# The user to which dnsmasq will change to after startup.
user=dnsmasq

# The group which dnsmasq will run as on startup.
group=dnsmasq

# File path to the PID file
pid-file=/var/run/dnsmasq.pid

# Clears the cache whenever /etc/resolv.conf is re-read or upstream servers are set via DBus, 
# This is useful when new nameservers may have different data than those held in local cache. 
#clear-on-reload

### Name resolution options

# Specify the upstream resolver within another file
resolv-file=/etc/resolv.dnsmasq

# Specify the upstream AWS VPC Resolver within this config file
# https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#AmazonDNS
# Setting this does not suppress reading of /etc/resolv.conf, use --no-resolv to do that.
#server=169.254.169.253
#server=fd00:ec2::253

# Specify upstream servers directly
#server=/ec2.internal/169.254.169.253
#server=/compute.internal/169.254.169.253

# IPv6 addresses may include an %interface scope-id
#server=/ec2.internal/fd00:ec2::253%eth0
#server=/compute.internal/fd00:ec2::253%eth0

# To query all upstream servers simultaneously
#all-servers

# Query servers in order
#strict-order

# Uncomment if you specify the upstream server in here so you don't read 
# /etc/resolv.conf. Get upstream servers only from cli or dnsmasq conf.
#no-resolv

# Uncomment if specify the upstream server in here so you no longer poll 
# the /etc/resolv.conf file for changes.
#no-poll

# Additional hosts files to include
#addn-hosts=/etc/dnsmasq-blocklist

# Send queries for internal domain to another internal resolver
#address=/int.example.com/10.10.10.10

# Examples of blocking TLDs or subdomains
#address=/.local/0.0.0.0
#address=/.example.com/0.0.0.0

# Return answers to DNS queries from /etc/hosts and --interface-name and
# --dynamic-host which depend on the interface over which the query was received.
#localise-queries

# Later versions of windows make periodic DNS requests which don't get sensible answers 
# from the public DNS and can cause problems by triggering dial-on-demand links.
# This flag turns on an option to filter such requests. 
#filterwin2k

# Never forward addresses in the non-routed address spaces
bogus-priv

# Never forward plain names
domain-needed

# Reject private addresses from upstream nameservers
stop-dns-rebind

# Disable the above entirely by commenting out the option OR allow RFC1918 responses 
# from specific domains by commenting out and/or adding additional internal domains.
#rebind-domain-ok=/int.example.com/
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-naming.html
rebind-domain-ok=/ec2.internal/compute.internal/local/

# Exempt 127.0.0.0/8 and ::1 from rebinding checks
rebind-localhost-ok

# Set the maximum number of concurrent DNS queries.
# The default value is 150. Adjust to your needs.
#dns-forward-max=150

# Set the size of dnsmasq's cache, default is 150 names
cache-size=1000

# Without this option being set, the cache statistics are also available in 
# the DNS as answers to queries of class CHAOS and type TXT in domain bind. 
no-ident

# The following directive controls whether negative caching 
# should be enabled or not. Negative caching allows dnsmasq 
# to remember “no such domain” answers from the parent 
# nameservers, so it does not query for the same non-existent 
# hostnames again and again.
#no-negcache

# Negative replies from upstream servers normally contain 
# time-to-live information in SOA records which dnsmasq uses 
# for caching. If the replies from upstream servers omit this 
# information, dnsmasq does not cache the reply. This option 
# gives a default value for time-to-live (in seconds) which 
# dnsmasq uses to cache negative replies even in the absence 
# of an SOA record.  
neg-ttl=60

# Uncomment to enable validation of DNS replies and cache DNSSEC data.

# Validate DNS replies and cache DNSSEC data.
#dnssec

# As a default, dnsmasq checks that unsigned DNS replies are legitimate: this entails 
# possible extra queries even for the majority of DNS zones which are not, at the moment,
# signed. 
#dnssec-check-unsigned

# Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients.
#proxy-dnssec

# https://data.iana.org/root-anchors/root-anchors.xml
#conf-file=/usr/share/dnsmasq/trust-anchors.conf

# The root DNSSEC trust anchor
#
# Note that this is a DS record (ie a hash of the root Zone Signing Key)
# If was downloaded from https://data.iana.org/root-anchors/root-anchors.xml
#trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5

## Logging directives

#log-async
#log-dhcp

# Uncomment to log all queries
#log-queries

# Uncomment to log to stdout
#log-facility=-

# Uncomment to log to /var/log/dnsmasq.log
log-facility=/var/log/dnsmasq.log
EOF

ポイントとして、re:Postのエントリではstrict-orderが有効にされていますが、上記例ではコメントアウトしています。

〜略
#strict-order
〜略

本設定をコメントアウトしておかないと、/etc/resolv.dnsmasqに記載された参照先DNSサーバについて必ず上から順に試行してしまうため、dnsmasqを追加する意味がなくなってしまうためです。

参照先DNSサーバの設定を作成します。(192.168.1.100は存在しないサーバ)

cat <<'EOF' | sudo tee /etc/resolv.dnsmasq
nameserver 192.168.1.100
nameserver 172.31.0.2
EOF

設定ファイルのシンタックスチェックを行います。

$ sudo dnsmasq --test
dnsmasq: syntax check OK.

OSがdnsmasq(127.0.0.1でリスン)を参照するように設定します。

$ sudo vim /etc/systemd/resolved.conf

↓以下のようにDNSを指定します。

〜略〜
[Resolve]
DNS=127.0.0.1
〜略〜

systemd-resolvedを再起動し反映します。

$ sudo systemctl restart systemd-resolved
$ cat /etc/resolv.conf

設定が反映されていることを確認します。

$ cat /etc/resolv.conf
nameserver 127.0.0.1

dnsmasqを起動し、自動起動も設定しておきます。

$ sudo systemctl enable --now dnsmasq.service
$ sudo systemctl start dnsmasq.service

構成後の所要時間

dnsmasqを利用する構成で、同じリクエストを実行してみます。

$ time curl https://s3.amazonaws.com

real    0m0.472s
user    0m0.018s
sys 0m0.000s

dnsmasqの構成前には5秒を要していたDNSのタイムアウト部分がなくなり、高速に処理できるようになりました。

注意点

構成要素の増加

ローカルDNSキャッシュを利用することで複数のDNSサーバを参照する際の耐障害性の向上およびクォータに達する可能性の緩和が可能ですが、dnsmasqなどの構成要素が増えるため、設計や運用の対象が増加いたします。

Route 53 アウトバウンドエンドポイントを活用することにより、EC2やコンテナが参照するDNSはRoute 53 Resolver1つにしつつ、その先で参照する独自ドメインのDNSサーバは複数設定することも可能です。 構成・コスト上採用が可能であれば、こちらの構成もご検討ください。

blog.serverworks.co.jp

Fargateで利用される場合の注意点

ECS on Fargateなどの構成においてもローカルDNSキャッシュが必要な場合、サイドカーとしてローカルDNSキャッシュを構成することが考えられます。 dnsmasqは実行に特権が必要なため、Fargateでは利用できません。 Fargateで利用されたい場合は、unboundの利用をご検討ください。

さいごに

  • 参照先DNSサーバが複数あるケース
  • 短時間に大量の名前解決が必要なケース

上記のケースにおいて、ローカルDNSキャッシュを利用することで耐障害性向上やクォータ到達の緩和を行う例をご紹介いたしました。

AWSではRoute 53 ResolverやRoute 53 アウトバウンドエンドポイントといったDNSの仕組みが充実しているためローカルDNSキャッシュを必要とするケースは多くないと思いますが、どなたかのお役に立てば幸いです。

久保 賢二(執筆記事の一覧)

猫とAWSが好きです。