DNS (Domain Name Server) 提供了域名/主机名到 IP 地址的正反向解析功能,是保障网络服务能够正常使用的重要基础设施。几乎每次网络连接都是从 DNS 解析开始的,比如访问 Web 页面、发送电子邮件等等,都需要将域名解析为 IP 地址。
实现 DNS 的服务有很多,BIND 是 Unix/Linux 操作系统首选的 DNS 服务器,对于建设私有 DNS,BIND 无疑是一个好选择。本文将通过以下主题介绍 BIND,并演示在 Rocky Linux 9 上自动化完成 BIND 主从架构的部署:
BIND 主从部署过程的自动化是通过 Ansible 实现的,因此需要 Ansible 控制节点,BIND 使用主从架构,因此需要 BIND Master 节点和 BIND Slave 节点,共需要三个节点。
三个节点分别为 2 核 CPU、2 G 内存的虚拟机。
Ansible 托管节点的信息已经在 “自动化部署 NTP 服务” 一文做过介绍,这里略过。Ansible Inventory hosts 文件内容如下:
[automate] automate-host.aiops.red [ntp] ntp1.server.aiops.red [dnsmaster] dns1-host.aiops.red [dnsslave] dns2-host.aiops.red
BIND Master 节点信息:
# IP:10.211.55.16 # NETMASK:255.255.255.0 # 主机名:dns1-host.aiops.red # 系统版本:Rocky Linux release 9.0
BIND Slave 节点信息:
# IP:10.211.55.17 # NETMASK:255.255.255.0 # 主机名:dns2-host.aiops.red # 系统版本:Rocky Linux release 9.0
Ansible 控制节点通过主机名访问 DNS 主从节点,在 DNS 部署完成前,需要在 Ansible 控制节点的 /etc/hosts
文件中做主机名与 IP 地址的绑定:
sudo vim /etc/hosts
为了实现部署过程的自动化,Ansible 控制节点需要通过 aiops
用户 (或其他有免密 sudo 权限的普通用户) 免密登录 DNS 主从节点,并可以免密执行 sudo 命令。在之后的小结中,会使用名为 set_authorized_key
的 Ansible Role 配置免密登录。
BIND (Berkeley Internet Name Domain) 是由 ISC (Internet Systems Consortium) 开发和维护的开源软件,为不同操作系统实现 DNS 协议。
在本节中,介绍通过一组 Ansible Roles 完成 BIND Master 的安装与配置。
创建 bind_master
Role,并进入 bind_master/
Role 目录:
ansible-galaxy role init --init-path ~/roles/ bind_master cd ~/roles/bind_master/
在 tasks/main.yml
文件中,定义安装 BIND Master 需要的 task,文件内容如下:
--- # tasks file for bind_master - name: install bind packages task ansible.builtin.dnf: name: - bind - bind-utils state: present - name: started dns service task ansible.builtin.systemd: name: named.service state: started enabled: true - name: turn on dns service in the firewall task ansible.builtin.firewalld: service: dns permanent: true immediate: true state: enabled - name: generate named.conf file from template task ansible.builtin.template: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: root group: named loop: - { src: named_master.conf.j2, dest: /etc/named.conf } - { src: named.conf.master.j2, dest: /etc/named.conf.master } - { src: domain_name.j2, dest: "/var/named/{{ domain_name }}" } - { src: arpa.j2, dest: "/var/named/{{ arpa }}.in-addr.arpa" } notify: restart dns service handlers
第一个 task 安装 BIND 软件包,bind
包提供了 BIND 的主程序 named;bind-utils
包提供了查询 DNS 信息的命令 (host、dig、nslookup)。
第二个 task 启动 named.service 服务,并将其设置为开机启动。
第三个 task 在 firewalld 防火墙中开启 dns service,以供客户端访问。
第四个 task 通过模板文件生成四个配置文件,并在文件有变化时,通过 notify 通知 “restart dns service handlers” 执行。
handlers/main.yml
文件的内容如下:
--- # handlers file for bind_master - name: restart dns service handlers ansible.builtin.systemd: name: named.service state: restarted
在接到通知后,重启 named.service 服务。
在模板配置文件中用到了几个变量,定义在了 defaults/main.yml
文件中:
--- # defaults file for bind_master allow_network: 10.211.55.0/24 arpa: 55.211.10 domain_name: aiops.red master_ip: 10.211.55.16 slave_ip: 10.211.55.17
这几个变量的值需要被使用者重新定义,因此把它们定义在了 defaults/main.yml
文件中。
在 ~/playbooks/
目录下创建用于存放部署 BIND Master 的 Playbook 及模板文件的目录,并切换到 deploy_bind_master/
:
mkdir -p ~/playbooks/deploy_bind_master/templates/ cd ~/playbooks/deploy_bind_master/
在 templates/
目录中创建模板文件。模板文件可以存放在 Role 的 templates/
目录下,也可以存放在 Playbook 的 templates/
目录下,如果两个位置分别存储了模板文件,Playbook 会覆盖 Role。
templates/named_master.conf.j2
文件生成 named 的主配置文件,内容如下:
options { listen-on port 53 { 127.0.0.1; {{ master_ip }}; }; listen-on-v6 port 53 { ::1; }; directory "/var/named"; dump-file "/var/named/data/cache_dump.db"; statistics-file "/var/named/data/named_stats.txt"; memstatistics-file "/var/named/data/named_mem_stats.txt"; secroots-file "/var/named/data/named.secroots"; recursing-file "/var/named/data/named.recursing"; allow-query { localhost; {{ allow_network }}; }; recursion yes; dnssec-validation yes; managed-keys-directory "/var/named/dynamic"; geoip-directory "/usr/share/GeoIP"; pid-file "/run/named/named.pid"; session-keyfile "/run/named/session.key"; include "/etc/crypto-policies/back-ends/bind.config"; }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; }; zone "." IN { type hint; file "named.ca"; }; include "/etc/named.rfc1912.zones"; include "/etc/named.root.key"; include "/etc/named.conf.master";
文件用到了 master_ip
和 allow_network
变量。master_ip
定义了 named 进程监听的 IP 地址;allow_network
定义了允许使用该 DNS 进行查询的网段。
文件末尾包含了 /etc/named.conf.master
文件,该文件由 templates/named.conf.master.j2
模板文件生成,内如如下:
zone "{{ domain_name }}" { type master; allow-transfer { {{ slave_ip }}; }; also-notify { {{ slave_ip }}; }; file "/var/named/{{ domain_name }}"; }; zone "{{ arpa }}.in-addr.arpa" { type master; allow-transfer { {{ slave_ip }}; }; also-notify { {{ slave_ip }}; }; file "/var/named/{{ arpa }}.in-addr.arpa"; };
zone 定义资源记录区,domain_name
是要托管的域名。allow-transfer 定义了将 Master DNS 上的区域文件内容复制到哪些 Slave DNS 上,以提供容错性。
templates/domain_name.j2
模板文件用于生成 zone 配置文件,内容如下:
$TTL 1h @ IN SOA {{ domain_name}}. root.{{ domain_name }}. ( 2022112101 ; Serial YYYYMMDDnn 24h ; Refresh 2h ; Retry 28d ; Expire 2d ) ; Minimum TTL ;Name Servers @ IN NS dns1-host @ IN NS dns2-host ;Mail Servers @ IN MX 0 mail1-host ;Other Servers dns1-host IN A 10.211.55.16 dns2-host IN A 10.211.55.17 mail1-host IN A 10.211.55.5 ntp1-host IN A 10.211.55.19 ;Canonical Names ntp1.server IN CNAME ntp1-host
templates/arpa.j2
生成 zone 反向区域文件,定义 PTR 记录,内容如下:
$TTL 1h @ IN SOA {{ arpa }}.in-addr.arpa root.{{ domain_name }}. ( 2022111801 ; Serial YYYYMMDDnn 24h ; Refresh 2h ; Retry 28d ; Expire 2d ) ; Minimum TTL ;Name Servers @ IN NS dns1-host @ IN NS dns2-host ;Other Servers dns1-host IN A 10.211.55.16 dns2-host IN A 10.211.55.17 ;PTR Records 16 IN PTR dns1-host 17 IN PTR dns2-host 5 IN PTR mail1 19 IN PTR ntp1-host
正反向区域文件中,分别添加了几个解析。
创建 deploy_bind_master.yaml
Playbook 文件,内容如下:
#!/usr/bin/env ansible-playbook --- - name: deploy bind master server play hosts: dnsmaster become: true gather_facts: false roles: - set_authorized_key - change_hostname - upgrade_packages - bind_master ...
Playbook 文件中使用了四个 Role。
set_authorized_key
Role 将 Ansible 控制节点的 aiops
用户的公钥拷贝到托管节点的指定用户,以配置免密登录。执行该 Role 时,需要 -k 选项,提供远程用户密码。
在该 Role 中定义了两个文件。
set_authorized_key/tasks/main.yml
内容如下:
--- # tasks file for set_authorized_key - name: initialization ssh key task ansible.builtin.authorized_key: comment: aiops user public key user: "{{ item }}" key: "{{ lookup('file', '/home/{{ local_user }}/.ssh/id_rsa.pub') }}" loop: "{{ remote_users }}"
set_authorized_key/defaults/main.yml
内容如下:
--- # defaults file for set_authorized_key local_user: aiops remote_users: - aiops
local_user
变量为 Ansible 控制节点的用户;remote_users
变量为托管节点的用户。
change_hostname
Role 根据 Inventory hosts 中的名称设置托管节点的主机名,change_hostname/tasks/main.yml
文件内容如下:
--- # tasks file for change_hostname - name: change host name task ansible.builtin.shell: cmd: hostnamectl set-hostname "{{ inventory_hostname }}"
upgrade_packages
Role 更新 dnf 包,类似在操作系统上执行 dnf upgrade -y 命令。如果有软件包更新,则重启操作系统。upgrade_packages/tasks/main.yml
文件的内容如下:
--- # tasks file for upgrade_packages - name: upgrade packages task ansible.builtin.dnf: update_cache: true name: "*" state: latest register: upgradeResult - name: reboot system task reboot: when: upgradeResult.changed
在 “Linux 9 自动化部署 NTP 服务” 一文中,对 Role 的创建做过介绍。
执行 deploy_bind_master.yaml
Playbook,安装并配置 BIND Master 节点:
ansible-playbook deploy_bind_master.yaml -k
Slave 节点提高了服务的可用性。
创建 bind_slave
Role,并进入 bind_slave/
Role 目录:
ansible-galaxy role init --init-path ~/roles/ bind_slave cd ~/roles/bind_slave
在 tasks/main.yml
文件中,定义安装 BIND Slave 的 task,内容如下:
--- # tasks file for bind_slave - name: install bind packages task ansible.builtin.dnf: name: - bind - bind-utils state: present - name: started dns service task ansible.builtin.systemd: name: named.service state: started enabled: true - name: set selinux to allow data synchronization from master task ansible.builtin.shell: cmd: setsebool -P named_write_master_zones on - name: generate configuration files task ansible.builtin.template: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: root group: named loop: - { src: named_slave.conf.j2, dest: /etc/named.conf } - { src: named.conf.slave.j2, dest: /etc/named.conf.slave } notify: restart dns service handlers
编辑 handlers/main.yml
文件,内容如下:
--- # handlers file for bind_slave - name: restart dns service handlers ansible.builtin.systemd: name: named.service state: restarted
在 defaults/main.yml
中设置变量:
--- # defaults file for bind_slave allow_network: 10.211.55.0/24 arpa: 55.211.10 domain_name: aiops.red master_ip: 10.211.55.16 slave_ip: 10.211.55.17
Slave 中用到的变量与 Master 相同。
在 ~/playbooks/
目录下创建用于存放部署 BIND Slave 的 Playbook 及模板文件的目录,并切换到 deploy_bind_slave/
:
mkdir -p ~/playbooks/deploy_bind_slave/templates cd ~/playbooks/deploy_bind_slave/
BIND Slave Role 用到了named_slave.conf.j2
、named.conf.slave.j2
两个模板文件,这两个文件存放在 ~/playbooks/deploy_bind_slave/templates/
目录下。
named_slave.conf.j2
内容如下:
options { listen-on port 53 { 127.0.0.1; {{ slave_ip }}; }; listen-on-v6 port 53 { ::1; }; directory "/var/named"; dump-file "/var/named/data/cache_dump.db"; statistics-file "/var/named/data/named_stats.txt"; memstatistics-file "/var/named/data/named_mem_stats.txt"; secroots-file "/var/named/data/named.secroots"; recursing-file "/var/named/data/named.recursing"; allow-query { localhost; {{ allow_network }}; }; recursion yes; dnssec-validation yes; managed-keys-directory "/var/named/dynamic"; geoip-directory "/usr/share/GeoIP"; pid-file "/run/named/named.pid"; session-keyfile "/run/named/session.key"; /* https://fedoraproject.org/wiki/Changes/CryptoPolicy */ include "/etc/crypto-policies/back-ends/bind.config"; }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; }; zone "." IN { type hint; file "named.ca"; }; include "/etc/named.rfc1912.zones"; include "/etc/named.root.key"; include "/etc/named.conf.slave";
named.conf.slave.j2
文件内容如下:
zone "{{ domain_name }}" { type slave; masters { {{ master_ip }}; }; file "/var/named/{{ domain_name }}"; }; zone "{{ arpa }}.in-addr.arpa" { type slave; masters { {{ master_ip }}; }; file "/var/named/{{ arpa }}.in-addr.arpa"; };
创建 deploy_bind_slave.yaml
Playbook 文件,内容如下:
#!/usr/bin/env ansible-playbook --- - name: deploy bind slave server play hosts: dnsslave become: true gather_facts: false roles: - set_authorized_key - change_hostname - upgrade_packages - bind_slave ...
执行 deploy_bind_slave.yaml
Playbook,安装并配置 BIND Slave 节点:
ansible-playbook deploy_bind_slave.yaml -k
DNS 主从节点已部署完成,接下来设置主机使用新建的 DNS 服务。
在 Ansible 控制节点上执行以下命令,为 /etc/resolv.conf
文件生成新内容:
sudo nmcli c m eth0 ipv4.dns-search aiops.red ipv4.dns 10.211.55.17,10.211.55.16
执行 restart_eth.sh
脚本,重启网卡连接,使配置生效。restart_eth.sh
脚本内容如下:
#!/bin/bash sudo nmcli c down eth0 sudo nmcli c up eth0
执行脚本:
sh restart_eth.sh
eth0 需要修改成正确的网卡接口。
查看 /etc/resolv.conf
文件的内容,可以看到,nameserver 地址已经配置为主从 DNS 的地址:
aiops.red
zone 添加了几个 A 记录以及解析 ntp1.server
的 CNAME 记录。删除 /etc/hosts
文件中包含 ntp1.server.aiops.red
的条目,然后访问该地址,以验证 DNS 的域名解析功能:
ping -c 1 ntp1.server.aiops.red dig ntp1.server.aiops.red
从输出可以看出,ntp1.server.aiops.red
域名是通过 DNS Slave 解析的。
可以编写一个 Ansible task,将 /etc/resolv.conf
文件拷贝到其他主机,使其他主机也能使用已部署的 DNS 服务解析域名。
本文介绍了在 Rocky Linux 9 及其他基于 RPM 的 Linux 发行版上,使用 Ansible 自动化部署主从架构的 DNS 服务。
本文作者:Gustav
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!