2026年6月13日
2026年6月13日
CoreDNS から Unbound へ移行する手順|Corefile の forward・hosts・file・acl を unbound.conf に置き換え、並行稼働で比べて切り替える
CoreDNS から Unbound への移行は、Corefile のプラグインを1つずつ unbound.conf の項目に対応させ、Unbound を別のポートで並行稼働させて応答を比べてから、同じアドレスのまま入れ替える流れで進めると、利用者への影響を小さくできます。注意したいのは、Unbound が既定で DNSSEC の検証を行うことと、社内向けのゾーンや逆引きの扱いが CoreDNS と異なることです。この記事では、移行の対象を見極める方法、設定の対応、DNSSEC と社内ゾーンの注意点、並行稼働での比較、切り替えと切り戻しを説明します。
移行の対象を見極める
最初に、その CoreDNS が何の役割を担っているかを確認します。
- 単体のキャッシュ・転送サーバー:社内の端末が使う名前解決サーバーとして、上流への転送とキャッシュを担っている CoreDNS です。Unbound への移行に向いています。
- Kubernetes のクラスター DNS:
kubernetesプラグインで Service や Pod の名前を答えている CoreDNS です。Unbound には同じ機能がないため、置き換えの対象にはしません。クラスターの外の名前解決だけを Unbound に任せたい場合は、CoreDNS のforwardの転送先を Unbound に向ける構成にします。
Unbound に移す理由も整理しておきます。DNSSEC の検証を名前解決サーバーで行いたい、上流に頼らずルートから自分で名前をたどりたい、キャッシュの統計を細かく見たい、といった理由がよく挙げられます。理由がはっきりしていると、移行後の確認項目も決めやすくなります。
現在の Corefile を棚卸しする
移行前の Corefile の例です。この内容を Unbound に移していきます。
# 移行前の /etc/coredns/Corefile
.:53 {
bind 10.0.0.53
acl {
allow net 10.0.0.0/8 127.0.0.1/32
block
}
hosts /etc/coredns/hosts {
fallthrough
}
forward . 192.0.2.1 192.0.2.2
cache 300
errors
}
corp.example.com:53 {
bind 10.0.0.53
forward . 172.16.0.53 172.16.0.54
errors
}
lab.example.com:53 {
bind 10.0.0.53
file /etc/coredns/db.lab.example.com
errors
}
使っているプラグインを書き出し、Unbound での対応先を決めます。主な対応は次のとおりです。
| CoreDNS のプラグイン | Unbound での対応 |
|---|---|
| bind | interface |
| acl | access-control(送信元ごとの許可・拒否) |
forward(. 全体) |
forward-zone の name: "."、または転送せず自分でルートからたどる |
| forward(特定ゾーン) | forward-zone(転送先が再帰サーバー)または stub-zone(転送先が権威サーバー) |
| hosts | local-data と local-data-ptr |
| file | auth-zone(ゾーンファイルをそのまま使える)、小さなゾーンなら local-zone と local-data |
| cache | cache-max-ttl・cache-min-ttl とキャッシュの容量の設定 |
| log | log-queries・log-replies |
| prometheus・health・ready | 直接の対応なし(unbound-control stats や外部の監視で代替) |
rewrite や template のように、Unbound に同じ働きをする項目がないプラグインを使っている場合は、移行の前にその機能が今も必要かを確かめてください。必要なら、その部分だけ CoreDNS を残す構成も選択肢になります。
unbound.conf を書く
上の Corefile を置き換えた例です。並行稼働のため、最初は 5353番ポートで待ち受けます。
# /etc/unbound/unbound.conf.d/migrate.conf
server:
# 並行稼働中は 5353番で待ち受ける。切り替え時に @5353 を外す
interface: 10.0.0.53@5353
interface: 127.0.0.1@5353
# acl の置き換え。書いていない送信元は拒否される
access-control: 10.0.0.0/8 allow
access-control: 127.0.0.0/8 allow
# cache 300 の置き換え(キャッシュの最大 TTL を 300秒にする)
cache-max-ttl: 300
# hosts の置き換え
local-data: "nas.corp.example.com. IN A 10.0.0.30"
local-data-ptr: "10.0.0.30 nas.corp.example.com."
# 社内ゾーンは公開側の DNSSEC の検証から外す(理由は後述)
domain-insecure: "corp.example.com."
domain-insecure: "lab.example.com."
# 社内のアドレスの逆引きを上流に流すため、既定の空応答を止める
local-zone: "10.in-addr.arpa." nodefault
# forward . の置き換え
forward-zone:
name: "."
forward-addr: 192.0.2.1
forward-addr: 192.0.2.2
# corp.example.com の転送(転送先は社内の再帰サーバー)
forward-zone:
name: "corp.example.com."
forward-addr: 172.16.0.53
forward-addr: 172.16.0.54
# 10.0.0.0/8 の逆引きも社内の DNS に転送する
forward-zone:
name: "10.in-addr.arpa."
forward-addr: 172.16.0.53
# file プラグインのゾーンを auth-zone で配信する
auth-zone:
name: "lab.example.com."
zonefile: "/etc/unbound/zones/db.lab.example.com"
for-downstream: yes
for-upstream: yes
CoreDNS の file プラグインで使っていたゾーンファイルは、標準的なゾーンファイルの書式であれば auth-zone でそのまま読み込めます。コピーした後、ファイルの所有者と権限を Unbound の実行ユーザーが読める状態にしてください。
設定を書いたら、sudo unbound-checkconf で文法を検査してから Unbound を起動します。
DNSSEC と社内ゾーンの注意点
CoreDNS の forward プラグインは DNSSEC の検証を行いませんが、Unbound はトラストアンカーが設定されていれば、既定で検証を行います。この違いが、移行で最もつまずきやすい点です。
corp.example.com のような社内ゾーンは、インターネット上では親の example.com から委任されていません。親ゾーンが DNSSEC で署名されていると、Unbound は「そのような子ゾーンは存在しない」という署名付きの証明を受け取り、社内 DNS から届いた答えを不正なものとして扱って SERVFAIL を返します。domain-insecure は、指定したゾーンをこの検証から外す設定です。社内ゾーンごとに指定してください。
逆引きにも違いがあります。Unbound は、10.in-addr.arpa などのプライベートアドレスの逆引きゾーンについて、既定で外に問い合わせず空の答えを返します。CoreDNS で社内の逆引きを社内 DNS に転送していた場合は、上の例のように local-zone を nodefault にしたうえで、転送先を書く必要があります。対象のゾーンと既定の動作はバージョンによって変わることがあるため、手元のバージョンの unbound.conf の説明で確かめてください(2026年10月現在)。
社内の名前がプライベートアドレスを返す環境で private-address を設定している場合は、private-domain で社内ゾーンを除外しないと、答えが取り除かれます。CoreDNS にはこの仕組みがないため、新たに設定するときは影響を確認してから有効にします。
並行稼働で応答を比べる
CoreDNS を 53番、Unbound を 5353番で動かしたまま、同じ名前を両方に問い合わせて答えを比べます。
# 比べたい名前を names.txt に1行ずつ書いておく(社内・社外・逆引き・存在しない名前を含める)
while read -r name type; do
a=$(dig @10.0.0.53 -p 53 "$name" "$type" +short | sort)
b=$(dig @10.0.0.53 -p 5353 "$name" "$type" +short | sort)
[ "$a" = "$b" ] && echo "OK $name $type" || echo "DIFF $name $type"
done < names.txt
names.txt には、www.example.org A、nas.corp.example.com A、30.0.0.10.in-addr.arpa PTR、no-such-host.corp.example.com A のように、種類の違う問い合わせを並べます。複数のアドレスを順番を変えて返す名前は、sort で並べ替えてから比べています。
DIFF が出た名前は、+short を外して status と答えの中身を確認します。CoreDNS では NOERROR なのに Unbound で SERVFAIL になる場合は、前の節の DNSSEC の検証が原因であることが多いです。Unbound のログの詳しさを一時的に上げると、検証の失敗の理由が記録されます。
あわせて、実際の利用者に近い使い方でも試します。検証用の端末の DNS サーバーを Unbound に向けられない場合は、dig -p 5353 での確認を、普段よく使う社内システムの名前にも広げておきます。
切り替える
比較で問題がなければ、同じアドレスのまま CoreDNS から Unbound に入れ替えます。端末側の DNS サーバーの設定を変えずに済むため、影響は入れ替えの数秒に限られます。利用の少ない時間帯を選び、関係者に作業時間を伝えておきます。
# 1. Unbound の待ち受けを 53番に変える(interface から @5353 を外す)
sudo unbound-checkconf
# 2. CoreDNS を止めて、Unbound を再起動する
sudo systemctl stop coredns
sudo systemctl restart unbound
# 3. 53番で Unbound が答えるかを確認する
dig @10.0.0.53 www.example.org
dig @10.0.0.53 nas.corp.example.com
CoreDNS をコンテナで動かしている場合は、コンテナの停止に置き換えてください。確認が済んだら、CoreDNS がサーバーの再起動時に自動で起動しないよう systemctl disable coredns しておきます。設定とバイナリは、しばらく残しておきます。
問題が出たときの切り戻し
社内のシステムで名前が引けないなどの問題が出たら、原因の調査より先に元に戻します。
sudo systemctl stop unbound
sudo systemctl start coredns
戻した後、Unbound を再び 5353番で動かし、問題の名前で並行稼働の比較を行って原因を調べます。切り戻しの手順は作業前に一度試しておくと、いざというときに慌てずに済みます。
まとめ
- Kubernetes のクラスター DNS は移行の対象にせず、単体のキャッシュ・転送サーバーとして動く CoreDNS を移します。
- Corefile のプラグインを棚卸しし、forward・hosts・file・acl などを unbound.conf の項目に対応させます。対応する機能がないものは、要否を確かめます。
- Unbound は既定で DNSSEC を検証するため、社内ゾーンには
domain-insecure、社内の逆引きにはnodefaultと転送先の設定が必要です。 - 5353番で並行稼働させて応答を比べ、同じアドレスのまま入れ替えます。切り戻しの手順も事前に用意しておきます。