Site icon rpine lab Tech Blog

趣味のプログラミング(Web系・バックエンド)や自宅ラボ(Homelab)構築・マイコンを使った電子工作などを雑多に扱った技術ブログです。

📛 Unbound + Keepalived による冗長化DNSサーバーの構築

はじめに

自宅ネットワーク環境における名前解決のために、2台構成で冗長化したDNSサーバーを構築してみたのでその手順を解説します。

この構成では、DNSキャッシュサーバーとしてUnboundを動作させ、Keepalivedを使ってVIP(仮想IPアドレス)を割り当てることで冗長化を図ります。

まず、VIPを2つ利用して通常時は各サーバーに別々のVIPが割り当てられるようにしておきます。

構成図
構成図

もし片方のサーバーが落ちたら、Keepalivedによって自動的にもう片側のサーバーに両方のVIPが割り当てられます。

これらのVIPをネットワーク機器のプライマリーDNSとセカンダリーDNSにそれぞれ指定することで、一方のサーバーで障害が発生した際には全DNSクエリがもう片側のサーバーだけに行くようになる仕組みになります。

フェイルオーバー時の動作
フェイルオーバー時の動作

システム構成

  • 仮想マシン:2台(別物理ホスト上)
    • DNSサーバー1(dns1):192.168.40.20
    • DNSサーバー2(dns2):192.168.40.21
  • OS:Ubuntu Server 24.04 LTS
  • ソフトウェア
    • Unbound (DNS キャッシュサーバー)
    • Keepalived (VRRPによる冗長化ソフト)
  • VIP(仮想IPアドレス)
    • VIP1(プライマリーDNS用):192.168.40.30
    • VIP2(セカンダリーDNS用):192.168.40.31

ソフトウェアのインストール

両サーバーに必要なパッケージをインストールします。

sudo apt install unbound keepalived

Unboundの設定

  1. ポート53を利用しているsystemd-resolvedを無効化します。
    sudo systemctl disable --now systemd-resolved
  2. /etc/unbound/unbound.conf.d/以下にUnboundの設定ファイルを作成します。今回は管理を容易にするため、設定ファイルを分割しています。
    server:
        interface: 0.0.0.0
        interface-automatic: yes # VIPを使うためには必要
        port: 53
    
    	  # ログ設定
        verbosity: 1
        log-local-actions: yes
        log-queries: yes
        log-servfail: yes
    
        tls-system-cert: yes # DNS over TLSのドメイン名検証に必要
    
        access-control: 127.0.0.0/8 allow
        access-control: ::1 allow
        access-control: 192.168.40.0/24 allow # ローカルネットワークからのアクセスを許可
    /etc/unbound/unbound.conf.d/custom_server.conf

    全ての名前解決の転送先として、Cloudflareが提供しているDNS(1.1.1.1)を指定します。接続にDNS over TLSを利用するため、forward-tls-upstreamオプションを指定します。

    forward-zone:
        name: "."
        forward-tls-upstream: yes # DNS over TLSを有効化
        forward-addr: 1.1.1.1@853#cloudflare-dns.com
        forward-addr: 1.0.0.1@853#cloudflare-dns.com
    /etc/unbound/unbound.conf.d/custom_forward_zone.conf
  3. 必要に応じて内部向けのDNSレコードを追加します。(.homeをローカルドメインとして想定)
    server:
        local-zone: "home." static
        # A レコード
        local-data: "srv1.home. A 192.168.40.10"
        local-data: "srv2.home. A 192.168.40.11"
        local-data: "dns1.home. A 192.168.40.20"
        local-data: "dns2.home. A 192.168.40.21"
        # PTR レコード
        local-data-ptr: "192.168.40.10 srv1.home."
        local-data-ptr: "192.168.40.11 srv2.home."
        local-data-ptr: "192.168.40.20 dns1.home."
        local-data-ptr: "192.168.40.21 dns2.home."
  4. Unboundサービスを有効化します。
    sudo systemctl enable unbound
    sudo systemctl restart unbound

Keepalivedの設定

Keepalivedの設定では、VIPに対する優先度を各サーバーで入れ替えて設定します。

VIP1 優先度VIP2 優先度
dns111090
dns290110
通常時割当dns1dns2

この設定により、両方のサーバーが動いているときには通常時はVIP1がdns1に、VIP2がdns2に割り当てられます。いずれかのサーバーがダウンした場合には、もう一方のサーバーが両方のVIPを引き継ぎます。これによってActive/Active構成の冗長化を実現します。

認証パスワードの作成

認証用のパスワードとしてランダムな文字列(8文字以内)を作成します。作成したら両サーバーの設定ファイルに記載してください。

dns1サーバーの設定

Unboundの設定は次のようにします。

global_defs {
    vrrp_garp_master_refresh 60
    enable_script_security
    script_user root
}

vrrp_script track_unbound {
  script "/usr/bin/systemctl is-active unbound.service"
  interval 5
  fall 3
  rise 2
}

vrrp_instance VIP_1 {
    state BACKUP
    interface eth0 # インターフェースを指定
    virtual_router_id 10
    priority 110
    advert_int 1
    authentication {
        auth_type AH
        auth_pass <認証パスワード>
    }
    virtual_ipaddress {
        192.168.40.30/24 # VIP1のアドレス
    }
    track_script {
        track_unbound
    }
}

vrrp_instance VIP_2 {
    state BACKUP
    interface eth0 # インターフェースを指定
    virtual_router_id 20
    priority 90
    advert_int 1
    authentication {
        auth_type AH
        auth_pass <認証パスワード>
    }
    virtual_ipaddress {
        192.168.40.31/24 # VIP2のアドレス
    }
    track_script {
        track_unbound
    }
}
/etc/keepalived/keepalived.conf

dns2サーバーの設定

priority以外はdns1と一緒です。

global_defs {
    vrrp_garp_master_refresh 60
    enable_script_security
    script_user root
}

vrrp_script track_unbound {
  script "/usr/bin/systemctl is-active unbound.service"
  interval 5
  fall 3
  rise 2
}

vrrp_instance VIP_1 {
    state BACKUP
    interface eth0 # インターフェースを指定
    virtual_router_id 10
    priority 90
    advert_int 1
    authentication {
        auth_type AH
        auth_pass <認証パスワード>
    }
    virtual_ipaddress {
        192.168.40.30/24 # VIP1のアドレス
    }
    track_script {
        track_unbound
    }
}

vrrp_instance VIP_2 {
    state BACKUP
    interface eth0 # インターフェースを指定
    virtual_router_id 20
    priority 110
    advert_int 1
    authentication {
        auth_type AH
        auth_pass <認証パスワード>
    }
    virtual_ipaddress {
        192.168.40.31/24 # VIP2のアドレス
    }
    track_script {
        track_unbound
    }
}
/etc/keepalived/keepalived.conf

Keepalivedの有効化

両サーバーで実行します。

sudo systemctl enable keepalived
sudo systemctl restart keepalived

動作確認手順

  1. サービス状態の確認

    起動に失敗していたら設定ファイルのミスを確認してください。

    sudo systemctl status unbound
    sudo systemctl status keepalived
  2. VIP割り当ての確認
    $ ip a
    ...
        inet 192.168.40.30/24 scope global secondary eth0
  3. 名前解決の確認(別マシンから実施)
    dig @192.168.40.30 google.com +short
    dig @192.168.40.31 srv1.home +short
    別マシン上
  4. 冗長化の確認
    1. 片方のサーバーでUnboundをダウンしてみます。
      sudo systemctl stop unbound
      dns1上
    2. もう一方のサーバーに両方のVIPが割り当てられていることを確認します。
      $ journalctl -xe -u keepalived
      ...
      Dec 22 00:00:10 dns1 Keepalived_vrrp[00000]: (VIP_1) Entering MASTER STATE
      dns2上(ログの確認)
      $ ip a
      ...
          inet 192.168.40.30/24 scope global secondary eth0
      ...
          inet 192.168.40.31/24 scope global secondary eth0
      dns2上(インターフェースの確認)
    3. Unboundを起動し直してVIPが再度両サーバーに分配されることを確認します。
      sudo systemctl start unbound
      dns1上
      $ journalctl -xe -u keepalived
      ...
      Dec 22 00:10:10 dns2 Keepalived_vrrp[00000]: (VIP_1) Master received advert from 192.168.40.20 with higher priority 110, ours 90
      Dec 22 00:10:10 dns2 Keepalived_vrrp[00000]: (VIP_1) Entering BACKUP STATE
      dns2上(ログの確認)

まとめ

本記事ではUnboundとKeepalivedを利用して冗長化されたDNSサーバーを構築する手順を解説しました。

構築したDNSサーバーのVIP2つをそれぞれルーターやPCのプライマリー/セカンダリーDNSに設定することで、比較的障害に強いローカルDNS環境が実現できるはずです。

参考サイト