2026年6月9日

2026年6月9日

CoreDNS のパフォーマンスチューニング|計測から cache・forward・ログ・台数の調整まで

CoreDNS の応答を速く安定させるには、設定を触る前にメトリクスと負荷試験で「遅いのは上流への転送か、CoreDNS 自身の CPU か、問い合わせの数そのものか」を見分け、そのうえで cache・forward・log の各プラグインと台数を順に調整します。この記事では、計測の方法、効果の大きい設定、Kubernetes 特有の問い合わせ増加への対策、変更を安全に試す手順を説明します。Corefile の基本的な書き方は前提として扱います。

まず計測する:どこで時間がかかっているか

チューニングで最初にするのは、現状の数字を取ることです。CoreDNS は prometheus プラグインを有効にすると、既定では 9153 番ポートの /metrics でメトリクスを公開します。

.:53 {
    errors
    prometheus 127.0.0.1:9153
    forward . 10.0.0.2 10.0.0.3
    cache 300
}

見るべき数字は次の3つです。

  • 応答時間の分布:coredns_dns_request_duration_seconds(ヒストグラム)。平均ではなく 99 パーセンタイルのような遅い側を見ます。
  • キャッシュのヒット率:coredns_cache_hits_total と coredns_cache_misses_total の比率。
  • 応答コードの内訳:coredns_dns_responses_total の rcode ラベル。SERVFAIL や REFUSED の増加は、上流の不調や同時接続の上限に当たっているサインです。

Prometheus で集計する場合の例です。

# 99パーセンタイルの応答時間(秒)
histogram_quantile(0.99, sum by (le) (rate(coredns_dns_request_duration_seconds_bucket[5m])))

# キャッシュのヒット率
sum(rate(coredns_cache_hits_total[5m]))
  / (sum(rate(coredns_cache_hits_total[5m])) + sum(rate(coredns_cache_misses_total[5m])))

上流への転送にかかる時間のメトリクスは、CoreDNS のバージョンによって名前が変わっています(coredns_forward_ や coredns_proxy_ で始まるものなど)。ここに書いた名前を前提にせず、curl -s http://127.0.0.1:9153/metrics の実際の出力で確認してください(2026年10月現在)。

負荷試験で上限を知る

本番のメトリクスだけでは、どこまで負荷に耐えられるかは分かりません。検証環境で DNS-OARC の dnsperf を使うと、一定の問い合わせ数をかけたときの応答時間と取りこぼしを測れます。

# 問い合わせ一覧(名前 種別)を用意する
cat > queries.txt <<'EOF'
www.example.com A
www.example.org AAAA
mail.example.net MX
EOF

# 30秒間、最大2000 qps で負荷をかける
dnsperf -s 127.0.0.1 -p 53 -d queries.txt -l 30 -c 10 -Q 2000

実運用に近い名前の一覧を使うことが大切です。同じ数件の名前を繰り返すと、すべてキャッシュから返るため実態より速い結果になります。本番の CoreDNS や、他人が運用する DNS サーバーに負荷試験をかけてはいけません。

結果別:どこを調整するか

計測の結果から、原因をおおむね次のように分けられます。

  • ヒット率が低く、遅い応答が上流待ち:cache の調整が最優先です。
  • 上流が遅い・不安定で SERVFAIL が出る:forward の転送先と並列度を見直します。
  • CPU が張り付いている:ログ出力の削減と、台数・CPU 割り当ての見直しです。
  • 問い合わせ数が想定より多い:Kubernetes では、クライアント側の検索ドメインによる問い合わせの増加を疑います。

cache プラグインの調整

cache は効果がもっとも大きい項目です。秒数だけの指定でも動きますが、詳細を書くと成功応答と否定応答を分けて制御できます。

cache {
    success 20000 3600 30
    denial 5000 300 5
    prefetch 10 1m 10%
    serve_stale 1h verify
}
  • success 容量 最大TTL 最小TTL:成功応答を何件、最長何秒までキャッシュするか。既定の容量は約1万件で、扱う名前が多い環境ではヒット率が頭打ちになることがあります。coredns_cache_entries が容量付近に張り付いていれば増やす目安です。
  • denial:「存在しない」という否定応答のキャッシュ。存在しない名前への問い合わせが多い環境で上流への負荷を減らせますが、長くすると新しく作ったレコードが見えるまでの時間も延びます。
  • prefetch 回数 期間 割合:よく引かれる名前を、TTL が切れる前に裏で取り直します。人気の名前で TTL 切れの瞬間に遅くなる現象を抑えられます。
  • serve_stale:上流が応答しないとき、期限切れの値を一時的に返します。可用性は上がりますが、古い値を返し得るという性質を理解したうえで使います。verify を付けると、まず上流に問い合わせ、それでも答えが得られなかったときだけ期限切れの値を返す動作になります。

注意したいのは、cache の最大 TTL を大きくすると、レコードを変更したときに CoreDNS 経由の利用者へ反映されるまでの時間も長くなる点です。Kubernetes のクラスター DNS では、Service の追加や削除を素早く反映させるため、既定の短い値(30秒など)のままにしておくのが一般的です。

forward プラグインの調整

上流への転送は、転送先の選び方と並列度で応答時間が変わります。

forward . 10.0.0.2 10.0.0.3 {
    policy sequential
    max_concurrent 1000
    health_check 5s
}
  • policy:既定は random です。sequential は先頭の転送先を優先し、失敗したときだけ次へ回します。近くて速いリゾルバーが1台あるなら sequential、同等の転送先を均等に使いたいなら round_robin が向いています。
  • max_concurrent:上流へ同時に投げる問い合わせ数の上限です。上限を超えた分はエラー応答になるため、無制限による過負荷を防ぐ一方、小さすぎると REFUSED が増えます。応答コードの内訳を見ながら決めます。
  • health_check:転送先の死活確認の間隔です。落ちた転送先を早く外せるほど、タイムアウト待ちによる遅延が減ります。

転送先そのものが遅い場合、CoreDNS 側の設定では限界があります。手元から dig @10.0.0.2 www.example.com を繰り返し、上流単体の応答時間を確かめてください。地理的に遠い公開 DNS より、同じネットワーク内のリゾルバーのほうが速いことがよくあります。

ログ出力を減らす

log プラグインはすべての問い合わせを1行ずつ出力するため、問い合わせが多い環境では CPU とディスクを大きく消費します。調査が終わったら外すか、記録する種類を絞ります。

log . {
    class denial error
}

この指定では成功した問い合わせは記録せず、否定応答とエラーだけを残します。障害の把握には errors プラグインと、前述のメトリクスで足りる場面が多いはずです。

台数と CPU 割り当て

CoreDNS は複数の CPU コアを使えますが、1台で受けきれない場合は台数を増やして負荷を分けます。Kubernetes ではレプリカ数を増やすか、ノード数に応じてレプリカを自動で増減させる仕組み(cluster-proportional-autoscaler を使った DNS 用のオートスケーラーなど)を使います。マネージド Kubernetes では、既定で用意されている仕組みや推奨の方法がサービスごとに異なるため、公式ドキュメントを確認してください。

コンテナに CPU の上限(limits)を小さく設定していると、負荷が集中した瞬間に CPU が絞られ(スロットリング)、応答時間が跳ね上がることがあります。cAdvisor の container_cpu_cfs_throttled_periods_total などで絞られていないかを確かめ、必要なら上限を見直します。

Kubernetes で問い合わせ数そのものを減らす

Kubernetes の Pod は既定で ndots:5 の resolv.conf を持つため、api.example.com のようにドットが少ない名前を引くと、api.example.com.default.svc.cluster.local などの検索ドメインを付けた問い合わせが先に何回も行われ、CoreDNS への問い合わせ数が数倍に膨らみます。対策は次のとおりです。

  • アプリ側で外部の名前を末尾にドットを付けた完全修飾名(api.example.com.)で指定する
  • Pod の dnsConfig で ndots を小さくする
  • CoreDNS の autopath プラグインで、検索ドメインの試行をサーバー側でまとめて処理する(kubernetes プラグインで pods verified が必要になり、メモリ使用量が増えます)
  • 各ノードにキャッシュを置く NodeLocal DNSCache を導入し、CoreDNS 本体への問い合わせと UDP の接続追跡にまつわる遅延を減らす

WordPress を Kubernetes で動かしている場合、外部 API やプラグインの更新確認などの名前解決が増えると、この影響を受けやすくなります。

変更を安全に試す手順

DNS が止まると、ほぼすべての通信に影響します。変更は次の順で進めます。

  1. 現在の Corefile(Kubernetes なら ConfigMap)をバックアップする
  2. 検証環境で設定を変え、dnsperf と dig で効果と副作用を確かめる
  3. 本番では1項目ずつ変更し、メトリクスの変化を見てから次へ進む
  4. 悪化したらバックアップに戻す

reload プラグインを使っていれば Corefile の変更は自動で読み込まれますが、構文の誤りがあると反映されずにログにエラーが出るため、変更後は errors の出力を確認してください。

まとめ

  • 最初に応答時間の分布、キャッシュのヒット率、応答コードの内訳を計測し、原因を見分けます。
  • cache は容量・TTL・prefetch・serve_stale を調整し、反映の遅れとの釣り合いを考えます。
  • forward は転送先の選び方、同時問い合わせの上限、死活確認の間隔を見直します。
  • 全件のログ出力は負荷が大きいため、調査後は外すか種類を絞ります。
  • Kubernetes では台数と CPU 上限に加え、ndots による問い合わせの増加を抑えます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲