はじめに
Amazon EKS クラスターで NodeLocal DNSCache を導入し、DNS クエリのレイテンシがどの程度改善されるかを検証しました。
NodeLocal DNSCache は、各ノード上で DNS キャッシュエージェントを DaemonSet として動作させる Kubernetes の機能です。Pod からの DNS クエリをノードローカルで処理することで、CoreDNS への負荷軽減とレイテンシ低減を実現します。Kubernetes v1.18 で stable になっています。
この記事で学べること
- NodeLocal DNSCache の仕組みと導入方法
- EKS クラスターへの具体的なデプロイ手順
- 導入前後の DNS レイテンシの比較結果
- メトリクスによるキャッシュ効果の確認方法
想定読者
- EKS を使い始めた方
- Kubernetes の DNS の仕組みに興味がある方
- クラスター内の DNS パフォーマンスを改善したい方
背景・課題
Kubernetes クラスターでは、Pod 間の通信やサービスディスカバリに DNS が多用されます。デフォルトでは、すべての DNS クエリが CoreDNS Pod に送られます。
この構成には以下の課題があります。
- Pod と CoreDNS が別ノードにある場合、ネットワークホップが発生しレイテンシが増加する
- クラスター規模が大きくなると CoreDNS への負荷が集中する
- iptables の DNAT とコネクション追跡(conntrack)による conntrack テーブルの枯渇リスク
NodeLocal DNSCache はこれらの課題を解決するために、各ノードに DNS キャッシュを配置します。
検証環境
| 項目 | 内容 |
|---|---|
| EKS バージョン | 1.34 |
| ノード | t3.medium × 2(Managed Node Group) |
| リージョン | ap-northeast-1 |
| kube-proxy モード | iptables |
| NodeLocal DNSCache | CoreDNS 1.13.1 ベース |
| DNS テスト用イメージ | gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 |
検証内容
1. EKS クラスターの作成
eksctl でクラスターを作成しました。
cat <<'EOF' > cluster-config.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: nodelocaldns-test
region: ap-northeast-1
version: "1.31"
managedNodeGroups:
- name: ng-default
instanceType: t3.medium
desiredCapacity: 2
minSize: 2
maxSize: 3
EOF
eksctl create cluster -f cluster-config.yaml
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-xx-xx.ap-northeast-1.compute.internal Ready <none> 57s v1.34.4-eks-f69f56f
ip-192-168-xx-xx.ap-northeast-1.compute.internal Ready <none> 56s v1.34.4-eks-f69f56f
2. 導入前の DNS レイテンシ計測
dig コマンドが使える dnsutils イメージでテスト Pod を作成し、ベースラインを計測しました。
kubectl run dnstest --image=gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 --restart=Never --command -- sleep 3600
クラスター内部 DNS(kubernetes.default.svc.cluster.local):
;; Query time: 2 msec
;; Query time: 2 msec
;; Query time: 1 msec
;; Query time: 2 msec
;; Query time: 1 msec
;; Query time: 7 msec
;; Query time: 17 msec
;; Query time: 1 msec
;; Query time: 3 msec
;; Query time: 2 msec
外部 DNS(amazon.com):
;; Query time: 3 msec
;; Query time: 2 msec
;; Query time: 1 msec
;; Query time: 1 msec
;; Query time: 2 msec
;; Query time: 1 msec
;; Query time: 2 msec
;; Query time: 2 msec
;; Query time: 2 msec
;; Query time: 2 msec
導入前の時点で、クラスター内部 DNS は 1〜17 msec、外部 DNS は 1〜3 msec でした。
また、resolv.conf を確認すると CoreDNS の ClusterIP が設定されています。
nameserver 10.100.0.10
search default.svc.cluster.local svc.cluster.local cluster.local ap-northeast-1.compute.internal
options ndots:5
3. NodeLocal DNSCache のデプロイ
Kubernetes 公式ドキュメントの手順に従い、マニフェストをダウンロードして変数を置換します。
curl -o nodelocaldns.yaml https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml
localdns=169.254.20.10
domain=cluster.local
kubedns=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
# Linux の場合
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
# macOS の場合
sed -i '' "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
kubectl apply -f nodelocaldns.yaml
実行結果:
serviceaccount/node-local-dns created
service/kube-dns-upstream created
configmap/node-local-dns created
daemonset.apps/node-local-dns created
service/node-local-dns created
4. 動作確認
node-local-dns のログを確認すると、169.254.20.10 と 10.100.0.10(kube-dns の ClusterIP)の両方でリッスンしていることがわかります。
.:53 on 169.254.20.10
cluster.local.:53 on 169.254.20.10
.:53 on 10.100.0.10
cluster.local.:53 on 10.100.0.10
CoreDNS-1.13.1
iptables モードでは、NodeLocal DNSCache が kube-dns の ClusterIP でもリッスンするため、Pod の resolv.conf を変更する必要がありません。既存の Pod もそのまま恩恵を受けられます。
5. 導入後の DNS レイテンシ計測
テスト Pod を再作成して計測しました。
クラスター内部 DNS(kubernetes.default.svc.cluster.local):
;; Query time: 4 msec ← 初回(キャッシュミス)
;; Query time: 0 msec ← 2回目以降(キャッシュヒット)
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
外部 DNS(amazon.com):
;; Query time: 2 msec ← 初回(キャッシュミス)
;; Query time: 0 msec ← 2回目以降(キャッシュヒット)
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
初回クエリのみ数ミリ秒かかりますが、2回目以降はキャッシュヒットにより 0 msec になりました。
6. メトリクスによるキャッシュ効果の確認
node-local-dns コンテナには curl/wget が入っていないため、kubectl port-forward でメトリクスを取得しました。
NODE_LOCAL_POD=$(kubectl get pods -n kube-system -l k8s-app=node-local-dns -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward -n kube-system ${NODE_LOCAL_POD} 9253:9253 &
sleep 2
curl -s http://localhost:9253/metrics | grep -E "^coredns_(cache_hits|cache_misses|dns_requests_total)"
kill %1
主要なメトリクス:
coredns_cache_hits_total{...,zones="."} 15
coredns_cache_hits_total{...,zones="cluster.local."} 9
coredns_cache_misses_total{...,zones="."} 1
coredns_cache_misses_total{...,zones="cluster.local."} 1
キャッシュヒットが 24 回に対してキャッシュミスは 2 回のみ。キャッシュヒット率は約 92% でした。
検証結果のまとめ
| 検証項目 | 導入前 | 導入後(初回) | 導入後(キャッシュヒット) |
|---|---|---|---|
| クラスター内部 DNS | 1〜17 msec | 4 msec | 0 msec |
| 外部 DNS | 1〜3 msec | 2 msec | 0 msec |
| キャッシュヒット率 | - | - | 約 92% |
わかったこと・学び
-
導入は簡単: マニフェストをダウンロードして変数を置換し
kubectl applyするだけで導入できます -
iptables モードなら設定変更不要: kube-dns の ClusterIP でもリッスンするため、Pod の
resolv.confを変更する必要がありません。既存の Pod もそのまま恩恵を受けられます - キャッシュ効果は明確: 2回目以降のクエリは 0 msec になり、キャッシュの効果がはっきり確認できました
- メトリクスで可視化できる: Prometheus 形式のメトリクスが公開されており、キャッシュヒット率やリクエスト数を監視できます
- EKS Auto Mode なら自動: EKS Auto Mode を使用している場合、NodeLocal DNSCache は自動的に含まれます
ハマりポイント・トラブルシューティング
busybox の time + nslookup では精度が足りない
当初 busybox イメージの time nslookup で計測しましたが、すべて 0m00.00s と表示され差分が見えませんでした。dig コマンド(dnsutils イメージ)を使うことで、ミリ秒単位の Query time が取得できます。
macOS の sed -i はオプションが異なる
Kubernetes 公式ドキュメントの sed -i コマンドは Linux 前提です。macOS の BSD sed では -i '' のように空文字列の拡張子指定が必要です。
# Linux
sed -i "s/..." nodelocaldns.yaml
# macOS
sed -i '' "s/..." nodelocaldns.yaml
node-local-dns コンテナに curl/wget がない
メトリクス取得時、コンテナ内に HTTP クライアントがないため kubectl exec では取得できません。kubectl port-forward を使ってローカルからアクセスする方法で解決しました。
dnsutils イメージの起動に時間がかかる
gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 はイメージの pull に時間がかかる場合があります。kubectl wait のタイムアウトを 120 秒程度に設定しておくと安心です。
まとめ
NodeLocal DNSCache を EKS クラスターに導入し、DNS クエリのレイテンシが大幅に改善されることを確認しました。特にキャッシュヒット時は 0 msec となり、CoreDNS への不要なネットワークホップを完全に排除できます。
導入手順もシンプルで、既存の Pod に影響を与えずに適用できるため、EKS クラスターの DNS パフォーマンス改善の第一歩としておすすめです。
クリーンアップ
検証後は忘れずにリソースを削除してください。
kubectl delete pod dnstest --force
kubectl delete -f nodelocaldns.yaml
eksctl delete cluster --name nodelocaldns-test --region ap-northeast-1
