2026年6月16日
2026年6月16日
DNSフェイルオーバーの設定方法|ヘルスチェック機能の有無で選ぶ3つのやり方と切り替え試験
DNSフェイルオーバーを設定するときは、最初に「今使っているDNSに、ヘルスチェックと連動して応答を切り替える機能があるか」を確認します。あればその機能を使うのが一番安全で、なければ PowerDNS の LUA レコードを使うか、外部の監視からAPIでレコードを書き換える方法を組みます。この記事では、3つのやり方の選び方と具体的な設定、切り替え試験、運用で事故を防ぐ工夫までを説明します。
設定の前に決めておく4つのこと
どの方式でも、次の4点を先に決めておくと設定がぶれません。
- 監視するURL:トップページではなく、アプリとデータベースまで動いているかを返す軽いURL(例:
/healthzがOKを返す)を用意します。ページキャッシュやCDNのキャッシュ対象からは外してください。キャッシュされた200が返り続けると、障害に気づけません。 - 判定の厳しさ:チェック間隔と「何回続けて失敗したら異常とみなすか」です。30秒間隔で3回なら、検知までおよそ1分半です。厳しくするほど早く切り替わりますが、一時的な遅延で誤って切り替わりやすくなります。
- TTL:切り替え対象のレコードは60〜300秒程度が一般的です。ただしリゾルバーやアプリがTTLより長く結果を保持することもあり、全員が同じ時間で切り替わるわけではありません。
- 切り戻しの方針:メインが復旧したら自動で戻すのか、人が確認してから戻すのかです。予備サーバーでもコメントや注文などの書き込みを受け付ける構成なら、自動で戻すとデータが食い違うおそれがあるため、手動で戻す運用をおすすめします。
やり方1:DNSサービスのヘルスチェック機能を使う
マネージドDNSの多くは、ヘルスチェックと応答の切り替えを機能として持っています(2026年10月現在)。名称や課金体系、無料プランで使えるかどうかは各社が変更するため、公式ドキュメントで確認してください。
- Amazon Route 53:ヘルスチェックを作り、レコードのルーティングポリシーを「フェイルオーバー」にしてプライマリとセカンダリを指定します。
- Cloudflare:DNSのレコード自体には死活監視がなく、有料の Load Balancing 機能でオリジンのプールと監視を設定します。
- Azure Traffic Manager:ルーティング方法に優先度(Priority)を選ぶと、正常な中で優先度の高いエンドポイントが返ります。
- Google Cloud DNS:ルーティングポリシーでフェイルオーバーや重み付けを設定できますが、ヘルスチェックの対象にできるものに条件があるため、対応範囲を事前に確かめます。
この方式の利点は、監視が複数の拠点から行われ、監視の仕組み自体の冗長化をサービス側が担ってくれることです。注意点は、監視元からのアクセスをファイアウォールやWAFで止めてしまうと、正常なのに異常と判定されることです。Route 53 であれば、ヘルスチェッカーのIPアドレス範囲がAWSの公開するIP範囲の一覧に載っているので、それを許可します。
やり方2:PowerDNS の LUA レコードで切り替える
自前で権威DNSを運用しているなら、PowerDNS Authoritative Server の LUA レコードが使えます。レコードの中に「どの条件でどのアドレスを返すか」を書けるもので、ヘルスチェック付きの応答を外部サービスなしで実現できます。LUA レコードはバージョン4.2で導入された機能で、関数やオプションはその後も追加されているため、使っているバージョンのドキュメントで確認してください。
まず pdns.conf で LUA レコードを有効にし、サービスを再起動します。
# /etc/powerdns/pdns.conf(場所はディストリビューションによって異なる)
enable-lua-records=yes
次に、ゾーンにレコードを追加します。pdnsutil edit-zone example.com で編集する場合の例です。
www.example.com. 60 IN LUA A "ifurlup('https://www.example.com/healthz', {{'203.0.113.10'}, {'198.51.100.20'}}, {stringmatch='OK'})"
ifurlup は、指定したURLに各アドレスで接続し、応答が正常なアドレスを返す関数です。アドレスを {{メイン}, {予備}} のように別々の組に分けると、最初の組に正常なアドレスがあればそれを返し、なければ次の組を返すので、アクティブ/スタンバイのフェイルオーバーになります。stringmatch を付けると、本文にその文字列が含まれる場合だけ正常とみなします。
編集後は pdnsutil check-zone example.com でゾーンに誤りがないかを確認し、権威サーバーに直接問い合わせて応答を見ます。
dig +short www.example.com A @ns1.example.com
この方式で気をつけたいのは次の点です。
- 監視は各権威サーバーが自分で行います。サーバーごとに判定が一時的に食い違うことがあり、権威サーバー自身のネットワークが不調だと誤判定の原因になります。
- 判定はバックグラウンドで行われ、起動直後や初回の問い合わせ直後は結果がそろっていないことがあります。
- セカンダリサーバーにゾーン転送するとき、受け手も LUA レコードに対応した PowerDNS でないと同じ動作になりません。
- すべてのアドレスが異常と判定された場合の返し方も仕様で決まっているので、ドキュメントで確かめておきます。
やり方3:外部の監視からAPIでレコードを書き換える
ヘルスチェック機能がなく、APIでレコードを変更できるDNSサービスなら、監視スクリプトから書き換える方法があります。次は Cloudflare のAPIで、メインが3回続けて異常なら予備のIPに書き換える例です。cron で1分ごとに実行する想定です。
#!/usr/bin/env bash
# /usr/local/bin/dns-failover.sh
# メインが THRESHOLD 回続けて異常なら、DNSレコードを予備のIPに書き換える(自動では戻さない)
set -euo pipefail
: "${CF_API_TOKEN:?CF_API_TOKEN を環境変数で渡してください}"
ZONE_ID="your-zone-id"
RECORD_ID="your-record-id"
NAME="www.example.com"
PRIMARY_IP="203.0.113.10"
SECONDARY_IP="198.51.100.20"
THRESHOLD=3
STATE_DIR="/var/lib/dns-failover"
API="https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}"
mkdir -p "$STATE_DIR"
exec 9>"${STATE_DIR}/lock"
flock -n 9 || exit 0 # 前回の実行が終わっていなければ何もしない
# メインのIPに直接つないで、ヘルスチェックURLが OK を返すかを見る
if curl -fsS --max-time 5 --resolve "${NAME}:443:${PRIMARY_IP}" \
"https://${NAME}/healthz" | grep -q 'OK'; then
echo 0 > "${STATE_DIR}/fail_count"
exit 0
fi
count=$(( $(cat "${STATE_DIR}/fail_count" 2>/dev/null || echo 0) + 1 ))
echo "$count" > "${STATE_DIR}/fail_count"
if [ "$count" -lt "$THRESHOLD" ]; then
exit 0
fi
current=$(curl -fsS -H "Authorization: Bearer ${CF_API_TOKEN}" "$API" | jq -r '.result.content')
if [ "$current" = "$SECONDARY_IP" ]; then
exit 0 # すでに切り替え済み
fi
curl -fsS -X PATCH \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"content\":\"${SECONDARY_IP}\"}" \
"$API" > /dev/null
logger -t dns-failover "${NAME} を ${SECONDARY_IP} に切り替えました"
--resolve を使うと、DNSの応答に関係なくメインのIPへ接続できるので、切り替え後もメインの状態を正しく見られます。失敗回数をファイルに残しているのは、一度の遅延で切り替わらないようにするためです。復旧時に自動で戻さないのは、メインが不安定なまま戻って切り替えを繰り返す「フラッピング」と、データの食い違いを避けるためです。
この方式を安全に動かすための条件は次のとおりです。
- 監視はメインとも予備とも別のネットワークで動かす:メインと同じ場所で動かすと、メインのネットワーク障害で監視も止まります。監視サーバー自体の死活も別の仕組みで見ておきます。
- APIトークンは最小権限にする:対象ゾーンのDNS編集だけに絞ったトークンを作り、スクリプトに直接書かず、権限を絞った環境変数ファイルから渡します。トークンをGitに含めないでください。
- 通知を付ける:切り替えたことに気づけないと、予備で動き続けていることを見落とします。
loggerに加えてメールやチャットへの通知を足してください。
Cloudflare でプロキシを有効にしているレコードなら、利用者に見えるのはCloudflareのIPのままで、オリジンの切り替えはCloudflare側で反映されるため、リゾルバーのTTLを待つ影響は小さくなります。プロキシを無効(DNSのみ)にしている場合は、通常のTTLの影響を受けます。
切り替えを試験する
設定したら、利用者の少ない時間帯に意図的にメインを異常にして、切り替えと切り戻しを試します。作業前に現在のDNSレコードをエクスポートしておくと、何かあったときに戻せます。
- 平常時の応答を、権威サーバーと公開リゾルバーの両方で記録する
- メインのヘルスチェックURLだけを503にする(Webサーバーを止めるより影響が小さい)
- 権威サーバーの応答が予備のIPに変わるまでの時間を測る(検知時間)
- 公開リゾルバー経由で変わるまでの時間を測る(キャッシュの影響)
- メインを戻し、決めた方針どおりに切り戻せるかを確かめる
# 権威サーバーと公開リゾルバーの応答を10秒ごとに並べて記録する
while true; do
printf '%s auth=%s public=%s\n' "$(date +%T)" \
"$(dig +short www.example.com A @ns1.example.com | head -1)" \
"$(dig +short www.example.com A @8.8.8.8 | head -1)"
sleep 10
done
実測した時間は、障害時に「あと何分で切り替わるか」を判断する材料になります。予備サーバーにも同じドメインのSSL証明書が有効な状態で入っているか、この試験で一緒に確認してください。普段アクセスが来ない予備側は、証明書の更新が失敗していても気づきにくい場所です。
まとめ
- まず使っているDNSにヘルスチェック連動の切り替え機能があるかを確認し、あればそれを使います。
- 自前の権威DNSなら、PowerDNS の LUA レコード(
ifurlupなど)で、外部サービスなしに切り替えを組めます。 - APIで書き換える方式は、監視を別ネットワークに置く、最小権限のトークン、自動で戻さない、通知を付ける、の4点で事故を防ぎます。
- 監視URL、判定回数、TTL、切り戻し方針を先に決め、試験で検知時間とキャッシュの影響を実測します。