はじめに
PowerDNSを用いて、物理的、地理的に冗長化(※)された権威DNSサーバの構築例を紹介します。本稿では「さくらのクラウド」上に構築していますが、同等の構成をとれる環境では同じ設定が可能かと思います。
PowerDNS自体の説明は、Wikipediaやその他わかりやすいサイトや資料が多数あるため、本稿では割愛しますが、
- 権威DNSサーバとして動作する PowerDNS Authoritative Server
- DNSキャッシュサーバとして動作する PowerDNS Recursor
の2種類があります。ここでは権威サーバとして使用するため前者を導入します。
ところで、冗長化したからといって必ずしも高可用性を実現できるわけではありません。冗長化はその必要条件にすぎないからです。ことDNSの可用性については、DDoS対策、オペレーション、バックアップ体制など、他にも重要な要素が多々存在します。
インフラ構成
本稿では以下のような構成例を元に設定例を紹介します。
この構成例では、「プライマリサーバ」と「DNSサーバ」に、役割を明確に分けています。
プライマリサーバについて
DNSゾーンのリソースレコードなどの情報を一元管理するサーバです。インターネットからのDNSクエリへの応答は行いません。そのため、一般的なDNSのコンテキストにおいて、シャドウマスタやヒドゥンマスタと呼ばれることもあります。
なお、さくらのクラウド上に構築するということで、インフラ部分が冗長化されているという前提の元、サーバ(VM)レベルでの冗長化は行っていません。
- ディスクイメージが格納されるストレージ機器は冗長化されており、ハードウェア故障に対するデータの保護耐性がある
- サーバ(VM)が動作するホストはHA構成となっており、ホストがダウンしても別のホスト上で自動的に再起動される
今回の例では、東京側に1台のみ作成しています。
DNSサーバについて
インターネット上のレゾルバからのクエリに応答するDNSサーバは、地理的に分散されるよう東京と石狩の両拠点に構築します。
さらに、各拠点内でハードウェア冗長性を確保するため、2台のサーバをVRRPで冗長化し、それぞれのVRRP仮想IPアドレスに対してゾーンの権威をもたせる(上位ゾーンからNSを向ける)ようにします。
例示する各サーバのIPアドレス
各サーバのIPアドレス(例示)は以下の通りとなります。お手元の環境のアドレスに適宜読み替えてください。
ホスト名 | IPアドレス | 仮想IPアドレス |
---|---|---|
is-dns1 | 203.0.113.5 | 203.0.113.4(ns1.example.jp) |
is-dns2 | 203.0.113.6 | 同上 |
tk-dns1 | 192.0.2.5 | 192.0.2.4(ns2.example.jp) |
tk-dns2 | 192.0.2.6 | 同上 |
tk-dns-primary | 192.0.2.10 | なし |
ミドルウェア構成
PowerDNSでは、様々なバックエンドデータベースを利用可能ですが、ここではMariaDBを使用します。プライマリサーバとDNSサーバ間の関係は下図のとおりです。
プライマリサーバがもっているゾーン情報は、MariaDBのレプリケーションを用いてDNSサーバに伝搬します。DNSサーバ上で動いているPowerDNSは、自ホスト上のMariaDBを参照しながらDNS応答を行います。したがって、プライマリサーバがダウンしても、ゾーン情報の更新ができなくなるだけで、DNS応答が途絶えるすることはありません。
※ DNSホスティング事業者では、ユーザがゾーン情報の変更できない状態も、サービス障害とみなすことが多いようですが。。
本稿では以下のソフトウェア、バージョンの組み合わせで動作確認を行っております。
項目 | 種別・バージョン |
---|---|
OS/ディストリビューション | CentOS7.9 Linux 3.10.0 |
データベース | MariaDB 10.9.3 |
DNSサーバ | PowerDNS 4.6.3 |
VRRP | Keepalived 1.3.5 |
余談:ゾーン転送について
従来から存在するBINDなどのDNSサーバソフトウェアでは、DNSサーバ間のゾーン情報の同期には、元々DNSのプロトコルとして定義されている「ゾーン転送」という動作が広く用いられてきました。PowerDNSでは、この「ゾーン転送」を用いずに、上記のようなデータベース(RDBMS)のレプリケーションのような信頼性のある仕組みを利用することが推奨されています。
従来の「ゾーン転送」では、ゾーンのSOAレコード中のシリアル番号を元に更新を検知したり、プライマリサーバからセカンダリサーバに更新を通知するためにDNS Notifyパケットが存在したり、そもそもの仕組みが複雑であることからトラブルや負荷の要因となっていました。PowerDNSとRDBMSのレプリケーションを組み合わせた方法では、このようなサーバ間の情報同期の煩わしさから開放されます。
※ 別途、RDBMSの運用ノウハウが必要になるのはさておき
1. ネットワーク(ルータ+スイッチ)の作成
ここから具体的な構築手順を説明していきます。まずはサーバを収容するネットワークの準備を行います。
さくらのクラウドでは、専用のネットワークアドレス帯とL2ブロードキャストドメインを提供する「ルータ+スイッチ」というリソースがあり、これを今回使用します。東京と石狩に1つずつ作成します。
作成が完了すると、以下のようにネットワークアドレスが確認できます。以降、例示アドレスは割り当てられたIPアドレスに読み替えてください。
2. サーバの作成
コントロールパネルのサーバの画面を開き、サーバの作成を進めます。同じ要領で5台分作成してください。
「サーバの情報」に設定しているタグ(@group
)は、サーバが起動されるホストが冗長となるように制御するものです。a
~d
の4種類の記号を設定することができ、異なる記号が設定されているサーバは、異なるホスト上で起動されます。これで、ホスト障害により、冗長化している2つのサーバが同時にダウンすることを防ぐことができます。
今回の構成では、以下のように1台目にはa、2台目にはbを設定しておくとよいでしょう。
ホスト名 | グループタグ |
---|---|
is-dns1 | @group=a |
is-dns2 | @group=b |
tk-dns1 | @group=a |
tk-dns2 | @group=b |
詳しくは、特殊タグ一覧 | さくらのクラウドドキュメントをご確認ください。
3. OS初期セットアップ
サーバの作成が完了したら、それぞれにログインし、以下のセットアップを進めます。
全サーバにて
アップデートし、基本パッケージをインストールします。
yum -y update
yum -y groupinstall base
続いて、パケットフィルタ設定を行います。
DNSのトラフィックによりconntrackの負荷が増大しないよう、ここではステートレスなフィルタを設定しています。ここでは手順をシンプルにするため、firewalldを用いず、iptablesのルールを直接叩き込んでいますが、お好みで選択してください。
yum -y install iptables-services
cat > /etc/sysconfig/iptables <<_EOF_
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i eth0 -s xx.xx.xx.xx/xx -j ACCEPT ← SSHアクセスを許可するIPアドレスを記入する
-A INPUT -i eth0 -s 192.0.2.0/28 -j ACCEPT ← 東京側ネットワークアドレスに書き換える
-A INPUT -i eth0 -s 203.0.113.0/28 -j ACCEPT ← 石狩側ネットワークアドレスに書き換える
-A INPUT -i eth0 -p tcp --dport 22 -j DROP
-A INPUT -i eth0 -p tcp --dport 3306 -j DROP
COMMIT
_EOF_
systemctl disable firewalld
systemctl enable iptables
一度再起動します。
reboot
4. ミドルウェアインストール
全サーバにて
MariaDBをインストールします
curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash
yum -y install MariaDB-server
PowerDNSをインストールします
yum -y install epel-release yum-plugin-priorities
curl -o /etc/yum.repos.d/powerdns-auth-46.repo https://repo.powerdns.com/repo-files/el-auth-46.repo
yum -y install pdns pdns-tools pdns-backend-mysql
5. MariaDB設定
レプリケーション用のサーバID割り当て
今回は、GTIDによるレプリケーションを行います。そこで5台でユニークなサーバIDを割り当てる必要があります。ここでは以下のように割り当てました。
ホスト名 | サーバID |
---|---|
tk-dns-primary | 1 |
is-dns1 | 11 |
is-dns2 | 12 |
tk-dns1 | 21 |
tk-dns2 | 22 |
プライマリサーバにて
以下の内容で/etc/my.cnfを作成します。
[mariadb]
server_id = 1
log_bin = log-bin
MariaDBを起動し、PowerDNSが用いる初期データベースの構築を行います。
以降、rootユーザとレプリケーション用ユーザ(repl)のパスワードを himitsu-0716
として例示していますが、安全なパスワードを設定してください。
systemctl enable mariadb
systemctl restart mariadb
# anonymousユーザとtestデータベースを削除します
mysql -e "DELETE FROM mysql.global_priv WHERE User=''"
mysql -e "DROP DATABASE IF EXISTS test"
mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'"
# rootユーザのパスワードを設定します
mysqladmin password "himitsu-0716"
mysql -e "flush privileges"
# レプリケーション用のユーザを作成します
mysql -e "CREATE USER repl IDENTIFIED BY 'himitsu-0716'"
mysql -e "GRANT REPLICATION SLAVE ON *.* TO 'repl'"
mysql -e "flush privileges"
# データベースを作成します
mysql -e "CREATE DATABASE pdns CHARACTER SET utf8"
mysql pdns < /usr/share/doc/pdns-backend-mysql-*/schema.mysql.sql
6. データベースレプリケーション設定
DNSサーバにて
以下ような内容で /etc/my.cnf を作成します。
以下はis-dns1の例です。server-idはサーバ毎に書き換えてください。
[mariadb]
server_id = 11 ← サーバごとに書き換える
relay_log = relay-bin
replicate_do_db = pdns
skip-networking
MariaDBを起動します。
systemctl enable mariadb
systemctl restart mariadb
testデータベースを削除します
mysql -e "DROP DATABASE IF EXISTS test"
mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'"
プライマリサーバからのレプリケーションを設定します。
192.0.2.10の部分はプライマリサーバのIPアドレスに読み替えてください。
ssh 192.0.2.10 mysqldump --all-databases --master-data=1 --gtid --single-transaction --routines > dump.sql
mysql -e 'stop replica'
mysql -e 'reset replica all'
mysql < dump.sql
rm -f dump.sql
mysql -e "CHANGE MASTER TO MASTER_HOST = '192.0.2.10', MASTER_USER = 'repl', MASTER_PASSWORD = 'himitsu-0716', MASTER_USE_GTID = slave_pos"
mysql -e "start replica"
レプリケーション動作が正しく行われていることを確認します。
mysql -e "show replica status\G"
! 以下となっていればレプリケーションが正しく行われています
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Gtid_IO_Pos: 0-1-XX
DNSサーバは、レプリケーションセカンダリとして動作しているため、データベースに変更を加えてはなりません(pdnsutilコマンドによる更新操作などを含め)。不整合が発生しレプリケーションが停止してしまいます。
7. PowerDNS設定
全サーバにて
以下の内容で /etc/pdns/pdns.conf を作成します
expand-alias=yes
resolver=8.8.8.8
setgid=pdns
setuid=pdns
negquery-cache-ttl=10
launch=gmysql
gmysql-socket=/var/lib/mysql/mysql.sock
gmysql-user=root
gmysql-dbname=pdns
gmysql-password=himitsu-0716
/etc/pdns ディレクトリ配下のownerをpdnsユーザに変更します
chown -R pdns:pdns /etc/pdns
DNSサーバにて
PowerDNSを有効化し、デーモンを起動します
systemctl enable pdns
systemctl start pdns
systemctl status pdns
プライマリサーバではDNS応答を行わないため、PowerDNSデーモン(pdns_server)を起動しておく必要はありません。pdns_serverを起動せずとも、pdnsutilコマンドを用いてゾーン情報をデータベースに反映することは可能です。
8. DNS応答動作確認
プライマリサーバ側でテスト用のDNSゾーンを作成し、DNSサーバ側でDNSが引けるようになるかどうか確認します。
プライマリサーバにて
テスト用のDNSゾーンを作成します。
pdnsutil load-zone example.jp /dev/stdin <<_EOF_
@ 259200 IN NS ns1
259200 IN NS ns2
3600 IN SOA ns1.example.jp. noc.example.jp. 1 3600 900 2419200 300
ns1 259200 IN A 203.0.113.4
ns2 259200 IN A 192.0.2.4
test 3600 IN A 192.0.2.1
test-alias 3600 IN ALIAS a.root-servers.net
_EOF_
DNSサーバにて
ゾーン情報がレプリケーションされているか確認します
pdnsutil list-all-zones
! ゾーンのリストを取得し、example.jpゾーンが作成されていればOKです
pdnsutil list-zone example.jp
! 以下のようにゾーンの内容が正しく表示されれば成功です
$ORIGIN .
example.jp 259200 IN NS ns1.example.jp.
example.jp 259200 IN NS ns2.example.jp.
example.jp 3600 IN SOA ns1.example.jp noc.example.jp 1 3600 900 2419200 300
ns1.example.jp 259200 IN A 203.0.113.4
ns2.example.jp 259200 IN A 192.0.2.4
test-alias.example.jp 3600 IN ALIAS a.root-servers.net
test.example.jp 3600 IN A 192.0.2.1
DNSクエリに応答するか確認します
dig @127.0.0.1 test.example.jp a
! 以下のようにレスポンスが返ってくれば成功です
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.5 <<>> @127.0.0.1 test.example.jp a
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49992
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;test.example.jp. IN A
;; ANSWER SECTION:
test.example.jp. 3600 IN A 192.0.2.1
;; Query time: 2 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: 金 7月 16 21:59:46 JST 2021
;; MSG SIZE rcvd: 60
dig +short @127.0.0.1 test-alias.example.jp a
! 以下のようにレスポンスが返ってくれば成功です(a.root-servers.netのIPアドレス)
198.41.0.4
9. keepalived(VRRP)設定
DNSサーバにて
keepalivedをインストールし、VRRPの設定をします。
yum -y install keepalived
cat > /etc/keepalived/keepalived.conf <<_EOF_
vrrp_instance V1 {
state BACKUP
interface eth0
virtual_router_id 4 ← VIPの第4オクテットとする
priority 100 ← 2号機側は90にする
advert_int 5
virtual_ipaddress {
203.0.113.4 ← 書き換える
}
}
_EOF_
systemctl start keepalived
systemctl status keepalived
systemctl enable keepalived
! インターフェイスにVIPが付与されることを確認する
ip addr show
VIPがDNS応答するか確認します
dig @203.0.113.4 test.example.jp
10. おつかれさまでした
本稿では割愛しますが、以下のようなことを確認、検討しておくと良いでしょう。
- DNSサーバの応答性能を計測する(以下に補足説明)
- DNSサーバのVRRPが想定通り切り替わるか動作確認をする
- プライマリサーバのデータベースを定期にバックアップする
- 死活監視、モニタリングを行う
- 各サーバのCPU、メモリ、ディスク空き容量など基本的なチェック、傾向把握
- DNS応答の外形監視(それぞれの実IP、VIPに対してDNSクエリを発行)
- データベースのレプリケーションが停止していないかチェック("show replica status"の出力などを確認)
- MariaDBレプリケーション用に安全なネットワークを用意する
補足:PowerDNSのDNS応答性能について
前述の通り、PowerDNSはRDBMS(ここではMariaDB)に検索SQL(SELECT文)を発行し、リソースレコードなどの情報を得た上でDNS応答します。一方で、Packet Cacheという高速化機構を備えており、RDBMSから得た情報をしばらくメモリ上にキャッシュします(デフォルトで20秒間)。キャッシュにヒットした場合はSQLを発行することなくDNS応答できます。
そのため、キャッシュにヒットする場合、しない場合で大幅に性能が変化します。安全をみるのであれば、最悪値を元にサイジング、プロビジョニングを行うのがよいでしょう。なお、性能チューニングに関して詳しくは、PowerDNSのマニュアル(Performance and Tuning)を参考にしてみてください。
更新履歴
- 2022/09/26
MariaDBとPowerDNSのバージョンをアップデートし、設定ファイル、コマンドを更新しました