Ubuntu 22.04 安装 octoDNS 1.22.0 指南

Ubuntu 22.04 从零安装 octoDNS 1.22.0(pip 虚拟环境 + octodns-bind)并跑通 YAML 源到 BIND9 的同步闭环:安装、权威区准备、同步配置、octodns-sync 预览下发、dig 验证与排障实录,全部命令实测。

octoDNSoctoDNSDNS服务器网络自动化运维2026/10/26

Ubuntu 22.04 安装 octoDNS 1.22.0 指南

‍‍​​‌​> 目标:在 Ubuntu 22.04 服务器上从零安装 octoDNS 1.22.0(pip 虚拟环境形态),并跑通「YAML 源 → octodns-sync 预览 → 下发 → BIND9 加载 → dig 验证」的完整闭环。本篇全部命令与回显均为实机操作记录;本文档站同类工具《DNSControl 5.7 介绍与选型指南》提供 Go 形态的对照方案。

一、环境信息

实测服务器环境如下,全篇命令与回显均在此环境中逐条验证:

项目值
操作系统Ubuntu 22.04 LTS(jammy)
主机名dns01.example.com
Python3.10.12(系统默认 python3)
pip22.0.2
BIND99.18.39-0ubuntu0.22.04.7-Ubuntu
octoDNS1.22.0(PyPI 最新稳定版,安装日期 2026-10-26)
octodns-bind1.1.0(BIND provider 官方包)
网络服务器内网可达公网 PyPI 源

💡 版本说明:octoDNS 要求 Python >= 3.10(PyPI requires_python 字段)。Ubuntu 22.04 默认 Python 3.10.12 刚好满足,无需额外升级 Python。若在 Ubuntu 20.04(默认 3.8)上安装会直接被 pip 拒绝,属于预期行为。

二、引言

octoDNS 是 GitHub 开源的 DNS 声明式同步工具:记录以 YAML 文件维护,octodns-sync 命令将「期望状态」与 DNS 服务器「实际状态」比对后生成变更计划并下发。与同类的《DNSControl 5.7 介绍与选型指南》相比,octoDNS 依赖 Python 生态但 provider 包划分更细。本篇完成三件事:装好 octoDNS 与 BIND9、写出首个同步配置、用 dig 验证记录真实生效。

三、安装 octoDNS 与 BIND9

3.1 更新软件源并安装基础包

apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y \
  bind9 bind9utils bind9-doc dnsutils \
  python3 python3-venv python3-pip

执行完成后验证两个关键组件版本:

python3 --version
named -v

实测回显:

Python 3.10.12
BIND 9.18.39-0ubuntu0.22.04.7-Ubuntu (Extended Support Version) <id:5ea386b>

3.2 创建虚拟环境并安装 octoDNS

octoDNS 通过 pip 安装,为避免污染系统 Python 环境,先建独立虚拟环境:

python3 -m venv /opt/octodns/venv
/opt/octodns/venv/bin/pip install octodns==1.22.0 octodns-bind

实测回显(依赖自动解析安装):

Successfully installed PyYaml-6.0.3 dnspython-2.8.0 fqdn-1.6.0 idna-3.20 natsort-8.4.0 octodns-1.22.0 octodns-bind-1.1.0 python-dateutil-3.0.0 six-1.17.0 tzdata-2026.5

⚠️ 必须同时安装 octodns-bind。只装 octodns 核心包时,配置里写 octodns_bind.ZoneFileProvider 会在运行时报模块缺失——BIND 相关 provider 全部在独立包 octodns-bind 中,这是 1.x 拆包后的官方设计。类名相关的坑详见第三章排障实录。

3.3 验证安装

/opt/octodns/venv/bin/octodns-sync --version

实测回显:

octoDNS 1.22.0

查看安装的 provider 及其版本(octodns-versions 需要配置文件,此处先确认 CLI 入口齐全):

ls /opt/octodns/venv/bin/ | grep octodns

实测回显(1.22.0 共 7 个 CLI 入口):

octodns-compare
octodns-dump
octodns-report
octodns-schema
octodns-sync
octodns-validate
octodns-versions

💡 老教程中的 octodns-verify 命令在 1.22.0 已不存在(校验能力并入 octodns-sync 与 octodns-validate)。全部子命令的用法见《octoDNS 1.22.0 常用 CLI 命令详解》。

四、准备 BIND9 权威区

4.1 启动并常驻 BIND9

systemctl enable --now bind9
systemctl is-active bind9

实测回显:

active

4.2 声明待管理的权威区

创建 zone 文件目录并写区声明:

mkdir -p /etc/bind/zones
chown bind:bind /etc/bind/zones

编辑 /etc/bind/named.conf.local,追加:

//
// octoDNS 管理的权威区 example.com
zone "example.com" {
    type master;
    file "/etc/bind/zones/example.com.zone";
};

语法校验(无输出即通过):

named-checkconf

⚠️ BIND9 必须在写入 named.conf.local 之后重启才能加载新区。本篇实测中曾先启动 bind9 再补配置,导致后续 rndc reload example.com 报 not found(区声明尚未载入 named 进程),改用 systemctl restart bind9 后正常。详见第三章排障实录第 4 条。

五、编写 octoDNS 同步配置

5.1 记录源文件(YamlProvider 目录)

创建目录 /opt/octodns/zones,写入 /opt/octodns/zones/example.com.yaml。示例覆盖 A、AAAA、CNAME、MX、TXT、CAA、NS 七类记录,区根记录使用空字符串键 "":

---
"":
  - ttl: 300
    type: A
    values:
      - 203.0.113.10
      - 203.0.113.11
  - ttl: 300
    type: TXT
    value: v=spf1 ip4:203.0.113.0/24 -all
  - ttl: 3600
    type: CAA
    values:
      - flags: 0
        tag: issue
        value: letsencrypt.org
  - ttl: 86400
    type: NS
    values:
      - ns1.example.com.
      - ns2.example.com.
_dmarc:
  ttl: 300
  type: TXT
  value: v=DMARC1\; p=none\; rua=mailto:dmarc@example.com
api:
  ttl: 300
  type: CNAME
  value: www.example.com.
mail:
  ttl: 600
  type: MX
  values:
    - exchange: mx1.example.com.
      preference: 10
    - exchange: mx2.example.com.
      preference: 20
mx1:
  ttl: 300
  type: A
  value: 203.0.113.61
mx2:
  ttl: 300
  type: A
  value: 203.0.113.62
ns1:
  ttl: 86400
  type: A
  value: 203.0.113.53
ns2:
  ttl: 86400
  type: A
  value: 203.0.113.54
www:
  - ttl: 300
    type: A
    value: 203.0.113.20
  - ttl: 300
    type: AAAA
    value: 2001:db8::20

此文件有三处易错点(均为实测踩坑,报错原文见排障实录):记录字典内键须按字母序(ttl 在 type 前)、TXT 值中的分号必须 \; 转义、MX 记录值用 exchange+preference 两键。完整语法规则见《octoDNS 1.22.0 配置参数详解》。

5.2 管理配置文件

写入 /opt/octodns/config/production.yaml:

---
providers:
  config:
    class: octodns.provider.yaml.YamlProvider
    directory: /opt/octodns/zones
    default_ttl: 300
    enforce_order: true
  bind:
    class: octodns_bind.ZoneFileProvider
    directory: /etc/bind/zones
    file_extension: .zone
zones:
  example.com.:
    sources:
      - config
    targets:
      - bind

说明:ZoneFileProvider 的 file_extension 决定生成的 zone 文件名(区名去尾点 + 扩展名),默认 . 会生成名为 example.com. 的文件,此处改为 .zone 得到更常规的 example.com.zone。

六、首次同步与验证闭环

6.1 预览变更计划

/opt/octodns/venv/bin/octodns-sync --config-file=/opt/octodns/config/production.yaml

实测回显(末尾计划框,逐行照录):

* plan: [example.com.] -> [ZoneFileProvider[bind]]
*   Create <ARecord A 300, example.com., [203.0.113.10, 203.0.113.11]> (config)
*   Create <TxtRecord TXT 300, example.com., ['v=spf1 ip4:203.0.113.0/24 -all']> (config)
*   Create <CaaRecord CAA 3600, example.com., [CaaValue(flags=0, tag='issue', value='letsencrypt.org')]> (config)
*   Create <NsRecord NS 86400, example.com., ['ns1.example.com.', 'ns2.example.com.']> (config)
*   Create <TxtRecord TXT 300, _dmarc.example.com., ['v=DMARC1\; p=none\; rua=mailto:dmarc@example.com']> (config)
*   Create <CnameRecord CNAME 300, api.example.com., www.example.com.> (config)
*   Create <MxRecord MX 600, mail.example.com., [MxValue(exchange='mx1.example.com.', preference=10), MxValue(exchange='mx2.example.com.', preference=20)]> (config)
*   Create <ARecord A 300, mx1.example.com., ['203.0.113.61']> (config)
*   Create <ARecord A 300, mx2.example.com., ['203.0.113.62']> (config)
*   Create <ARecord A 86400, ns1.example.com., ['203.0.113.53']> (config)
*   Create <ARecord A 86400, ns2.example.com., ['203.0.113.54']> (config)
*   Create <ARecord A 300, www.example.com., ['203.0.113.20']> (config)
*   Create <AaaaRecord AAAA 300, www.example.com., ['2001:db8::20']> (config)
* Summary: Creates=13, Updates=0, Deletes=0, Existing=0, Meta=False

不加 --doit 时 octodns-sync 只输出计划、不写任何数据,可放心作为日常预览命令。

6.2 正式下发

/opt/octodns/venv/bin/octodns-sync --config-file=/opt/octodns/config/production.yaml --doit

实测回显(计划框同上,追加 apply 日志):

[INFO] ZoneFileProvider[bind] apply: making 13 changes to example.com.
[INFO] Manager sync:   13 total changes

ZoneFileProvider 作为目标时每次同步都整文件重写 zone 文件(这是它的设计行为:zone_exists(target=True) 恒为 False,计划恒为全量 Creates,不做增量比对)。

6.3 检查生成的 zone 文件

cat /etc/bind/zones/example.com.zone

实测回显(octoDNS 自动生成 SOA,列对齐格式):

$ORIGIN example.com.
$TTL 3600
; Name: example.com.
@           3600 IN SOA       ns1.example.com. webmaster.example.com. ( 1792982188 ; Serial
                              3600 ; Refresh
                              600 ; Retry
                              604800 ; Expire
                              3600 ; NXDOMAIN ttl )
@           3600 IN NS        ns1.example.com.
@           3600 IN NS        ns2.example.com.
@           300 IN A          203.0.113.10
@           300 IN A          203.0.113.11
@           300 IN TXT        "v=spf1 ip4:203.0.113.0/24 -all"
@           3600 IN CAA       0 issue "letsencrypt.org"
_dmarc      300 IN TXT        "v=DMARC1\; p=none\; rua=mailto:dmarc@example.com"
api         300 IN CNAME      www.example.com.
mail        600 IN MX         10 mx1.example.com.
mail        600 IN MX         20 mx2.example.com.
mx1         300 IN A          203.0.113.61
mx2         300 IN A          203.0.113.62
ns1         86400 IN A        203.0.113.53
ns2         86400 IN A        203.0.113.54
www         300 IN A          203.0.113.20
www         300 IN AAAA       2001:db8::20

BIND 语法校验:

named-checkzone example.com /etc/bind/zones/example.com.zone

实测回显:

zone example.com/IN: loaded serial 1792982188 (OK)
ok

6.4 让 BIND9 加载新 zone 并用 dig 验证

rndc reload example.com

实测回显:

zone reload up-to-date

💡 若回显 not found,说明 named 进程尚未载入 named.conf.local 中的区声明(配置晚于服务启动),执行 systemctl restart bind9 后重试。

逐类型验证(实机 dig 回显):

dig @127.0.0.1 example.com A +short
203.0.113.11
203.0.113.10
dig @127.0.0.1 www.example.com AAAA +short
2001:db8::20
dig @127.0.0.1 mail.example.com MX +short
10 mx1.example.com.
20 mx2.example.com.
dig @127.0.0.1 example.com CAA +short
0 issue "letsencrypt.org"
dig @127.0.0.1 example.com NS +short
ns1.example.com.
ns2.example.com.

A、AAAA、CNAME、MX、TXT、CAA、NS 七类记录全部与 YAML 源一致,闭环完成。

七、排障实录

以下四个坑均在本篇实测过程中真实触发,报错逐行照录,供对照排查。

7.1 只装 octodns 核心包导致 provider 类缺失

octoDNS 1.x 将服务商 provider 拆分为独立包。若配置写成老版本核心包的类名 octodns.provider.bind.BindProvider(老教程常见),运行即报:

octodns.manager.ManagerException: Unknown provider class: octodns.provider.bind.BindProvider, /opt/octodns/config/old-class.yaml, line 8, column 5

若类名写对为 octodns_bind.ZoneFileProvider 但漏装 octodns-bind 包,则报模块不存在。解法:pip install octodns-bind,统一使用 octodns_bind. 前缀类名。

7.2 YAML 键序不符合 enforce_order 默认校验

YamlProvider 的 enforce_order 默认开启,要求 YAML 键按字母序排列(记录字典内 ttl→type→value,顶层键同样按字母序)。乱序即报:

yaml.constructor.ConstructorError: keys out of order: expected ttl got type at /opt/octodns/zones/example.com.yaml, line 26, column 3

解法:按字母序重排;日常维护建议先用 octodns-dump 导出,让 octoDNS 生成规范格式再修改。

7.3 TXT 值中的分号未转义

DMARC/SPF 等 TXT 值常含分号,octoDNS 要求 \; 转义,否则记录校验失败:

octodns.record.exception.ValidationError: Invalid record "_dmarc.example.com.", /opt/octodns/zones/example.com.yaml, line 3, column 3
  - unescaped ; in "v=DMARC1; p=none; rua=mailto:dmarc@example.com", via: chunked-value-rfc

解法:写作 v=DMARC1\; p=none\; rua=mailto:dmarc@example.com。

7.4 BIND 启动时序导致区加载失败

先 systemctl start bind9 后补写 named.conf.local 时,rndc reload example.com 报 not found,dig 误答到公网递归结果。解法:systemctl restart bind9 让 named 重新读入全部配置;建议养成「改配置必 restart」的习惯,rndc reload 仅适合区数据变化场景。

八、常见问题

  1. pip 安装报 Python 版本不满足:1.22.0 要求 >= 3.10,Ubuntu 20.04 系统默认 3.8 无法安装,需先升级系统 Python(见 A 篇限制说明)。
  2. 如何升级 octoDNS:/opt/octodns/venv/bin/pip install -U octodns octodns-bind,升级后先跑一遍 octodns-sync 预览确认计划符合预期,再 --doit。
  3. 如何卸载:删除虚拟环境目录即可整体移除(rm -rf /opt/octodns/venv),不影响系统 Python。
  4. zone 文件被 octoDNS 重写后格式变化:属正常现象,octoDNS 按「区名去尾点 + file_extension」生成文件并自动维护 SOA(Serial 取同步时刻的 Unix 时间戳);只要 named-checkzone 通过、reload 生效即可。

九、参考链接

  1. PyPI 项目页:https://pypi.org/project/octodns/(1.22.0,检索日期 2026-10-26)
  2. octodns-bind 包:https://pypi.org/project/octodns-bind/(1.1.0,检索日期 2026-10-26)
  3. GitHub 仓库:https://github.com/octodns/octodns(官方 README 与文档站入口 octodns.readthedocs.io)
  4. 站内系列:《octoDNS 1.22.0 介绍与选型指南》《octoDNS 1.22.0 配置参数详解》《octoDNS 1.22.0 常用 CLI 命令详解》(专栏 octoDNS)
  5. 站内同类:《Ubuntu 22.04 安装 DNSControl 5.7 指南》(专栏 DNSControl)