octoDNS 1.22.0 常用 CLI 命令详解

octoDNS 1.22.0 全部 7 个 CLI 入口逐个实测:octodns-sync 预览/--doit/--force 与加删记录到 dig 验证的完整闭环、octodns-dump 单文件与 --split 导出、compare/report(csv/json)/validate/schema/versions,全部 --help 参数表与回显逐行照录。

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

octoDNS 1.22.0 常用 CLI 命令详解

‍‍​​‌​> 目标:把 octoDNS 1.22.0 安装后自带的全部 7 个 CLI 入口(octodns-sync、octodns-dump、octodns-compare、octodns-report、octodns-validate、octodns-schema、octodns-versions)逐一讲透——真实参数表、可独立执行的示例与实测回显,并以「加记录 → dry-run 预览 → --doit 下发 → dig 验证 → 删记录」的完整闭环演示 octodns-sync 的日常工作流。全部输出为实机逐行照录。

一、环境信息

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

项目值
操作系统Ubuntu 22.04 LTS(jammy)
Python3.10.12
octoDNS1.22.0(pip 虚拟环境 /opt/octodns/venv)
octodns-bind1.1.0
BIND99.18.39-0ubuntu0.22.04.7-Ubuntu(RFC 2136 目标,TSIG 密钥 octodns-key)
管理配置/opt/octodns/config/dynamic.yaml(YAML 源 → RFC 2136 目标,含主区与子区)
记录源目录/opt/octodns/zones/(example.com.yaml、sub.example.com.yaml)
版本获取日期2026-10-26(PyPI 实查 1.22.0 为最新稳定版)

管理配置 dynamic.yaml 内容(TSIG secret 以占位符表示,注入方式见《octoDNS 1.22.0 配置参数详解》6.3):

---
providers:
  config:
    class: octodns.provider.yaml.YamlProvider
    directory: /opt/octodns/zones
    default_ttl: 300
  bind:
    class: octodns_bind.BindProvider
    host: 127.0.0.1
    port: 53
    key_name: octodns-key
    key_secret: __OCTODNS_TSIG_SECRET__
    key_algorithm: hmac-sha512
zones:
  example.com.:
    sources:
      - config
    targets:
      - bind
  sub.example.com.:
    sources:
      - config
    targets:
      - bind

二、工具概览

octoDNS 1.22.0 共提供 7 个 CLI 入口,安装后在虚拟环境 bin/ 下。实测清单:

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

实测回显:

octodns-compare
octodns-dump
octodns-report
octodns-schema
octodns-sync
octodns-validate
octodns-versions
命令用途高频场景
octodns-sync计划并(可选)执行同步日常预览 + 下发,核心命令
octodns-dump从任意 source 导出区数据为 YAML迁移导入、生成规范格式
octodns-compare比对两个数据源的差异变更前核对、漂移检测
octodns-report向指定解析器逐条查询并报表变更后核验、多解析器一致性检查
octodns-validate校验配置与记录合法性提交前自检、CI 门禁
octodns-schema生成配置/记录的 JSON Schema编辑器校验辅助
octodns-versions打印各 provider 包的实际版本环境盘点、排障第一步

💡 说明:老教程中出现的 octodns-verify 在 1.22.0 已不存在(上方实测目录可见),记录校验能力由 octodns-validate 承担、计划校验由 octodns-sync 预览承担。另注意本组命令的版本查询是全局参数:任意入口加 --version 均回显 octoDNS 1.22.0。

七个命令共享一组通用日志参数(每个 --help 末尾都有):--log-stream-stdout(日志改投 stdout,默认 stderr)、--log-syslog(日志同时发 syslog)、--syslog-device、--syslog-facility、--debug(提级详细日志)、--quiet(降级只看告警/错误与计划)、--logging-config(YAML 定义日志格式)、--version。下文各命令详解只展开各自专属参数。

三、前置条件

  1. 已安装 octoDNS 1.22.0 与 octodns-bind 1.1.0(安装全流程见《Ubuntu 22.04 安装 octoDNS 1.22.0 指南》),下文命令统一用虚拟环境绝对路径 /opt/octodns/venv/bin/<命令> 调用,也可先 export PATH=/opt/octodns/venv/bin:$PATH 简写;
  2. 有一份可用的管理配置(本篇为 /opt/octodns/config/dynamic.yaml,结构见第一章);octodns-schema 与 octodns-validate 之外,各命令都要传 --config-file;
  3. 除 octodns-schema(无需源数据)外,示例均假定记录源目录已有 example.com.yaml(13 条记录);涉及 sync 下发的两节需要目标 BIND9 已按配置授权 TSIG 更新。

四、octodns-sync:同步(核心命令)

octodns-sync 读取管理配置,逐区「populate 装载源 → plan 生成计划 → apply 执行」。1.22.0 没有 --dry-run 旗标:只输出计划不落数据就是默认行为,真正执行必须显式加 --doit。这是它区别于很多工具的安全设计:预览零成本、执行显式确认。

4.1 语法与全部参数

octodns-sync --help

实测回显(全量):

usage: octodns-sync [-h] --config-file CONFIG_FILE [--doit] [--force]
                    [--checksum CHECKSUM] [--source SOURCE] [--target TARGET]
                    [--version] [--log-stream-stdout] [--log-syslog]
                    [--syslog-device SYSLOG_DEVICE]
                    [--syslog-facility SYSLOG_FACILITY] [--debug] [--quiet]
                    [--logging-config LOGGING_CONFIG]
                    [zone ...]

Octo-DNS Multiplexer

positional arguments:
  zone                  Limit sync to the specified zone(s)

options:
  -h, --help            show this help message and exit
  --config-file CONFIG_FILE
                        The Manager configuration file to use
  --doit                Whether to take action or just show what would change,
                        ignored when Manager.enable_checksum is used
  --force               Acknowledge that significant changes are being made
                        and do them
  --checksum CHECKSUM   Provide the expected checksum, apply will only
                        continue if it matches the plan's computed checksum
  --source SOURCE       Limit sync to zones with the specified source(s) (all
                        sources will be synchronized for the selected zones)
  --target TARGET       Limit sync to the specified target(s)
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details

参数速读:--config-file 必填;zone 位置参数可限定一个或多个区;--doit 从「预览」切到「执行」;--force 放行大比例变更与根 NS 变更两类护栏(见 4.7/4.8);--source/--target 按名字筛选本次参与的 provider;--checksum 配合 manager.enable_checksum: true 做「计划校验和确认后才执行」的强管控。

4.2 预览计划(默认行为,无变更)

先看无变更时的标准形态——源与目标一致,只输出计划框:

octodns-sync --config-file=/opt/octodns/config/dynamic.yaml

实测回显(日志尾部节选,前半为逐区 populate/plan 日志):

2026-10-26T04:17:26  [127248227450880] INFO    YamlProvider[config] populate:   found 14 records, exists=True
2026-10-26T04:17:26  [127248227450880] INFO    Rfc2136Provider[bind] plan:   No changes
...
2026-10-26T04:17:27  [127248227450880] INFO    Plan
********************************************************************************
No changes were planned
********************************************************************************

「No changes were planned」即预览结论;此时不加任何旗标也不会写任何数据。

4.3 新增记录 → 预览 → 下发 → dig 验证(Create 闭环)

在源文件 zones/example.com.yaml 中按字母序插入一条新记录(grafana 插在 api 与 mail 之间):

grafana:
  ttl: 300
  type: A
  value: 203.0.113.77

再次预览,实测回显(关键行节选):

2026-10-26T04:17:26  [127248227450880] INFO    YamlProvider[config] populate:   found 14 records, exists=True
2026-10-26T04:17:26  [127248227450880] INFO    Rfc2136Provider[bind] populate:   found 13 records
2026-10-26T04:17:26  [127248227450880] INFO    Rfc2136Provider[bind] plan:   Creates=1, Updates=0, Deletes=0, Existing=13, Meta=False
2026-10-26T04:17:27  [127248227450880] INFO    Plan
********************************************************************************
* example.com.
********************************************************************************
* bind (Rfc2136Provider)
*   Create <ARecord A 300, grafana.example.com., ['203.0.113.77']> (config)
*   Summary: Creates=1, Updates=0, Deletes=0, Existing=13, Meta=False
********************************************************************************

计划框逐行解读:区名行(* example.com.)→ 目标与 provider 类(* bind (Rfc2136Provider))→ 逐条变更(行尾 (config) 表示新值来自名为 config 的源)→ Summary 统计(新建 1 条、既有 13 条)。注意增量比对的证据:populate 显示源 14 条、目标 13 条,计划只含差额 1 条 Create——这正是 RFC 2136 目标与 zone 文件目标(整文件重写、计划恒为全量 Creates)的行为差异。

确认计划无误后加 --doit 执行:

octodns-sync --config-file=/opt/octodns/config/dynamic.yaml --doit

实测回显(执行日志节选):

2026-10-26T04:11:42  [131529151401984] INFO    Rfc2136Provider[bind] apply: making 1 changes to example.com.
2026-10-26T04:11:42  [131529151401984] INFO    Manager sync:   1 total changes

dig 验证记录真实生效(RFC 2136 动态更新即时生效,无需 reload):

dig @127.0.0.1 grafana.example.com A +short

实测回显:

203.0.113.77

4.4 删除记录 → 预览 → 下发 → dig 验证(Delete 闭环)

从源文件删掉 grafana 记录块后预览,实测回显(关键行节选):

2026-10-26T04:11:49  [136961256718336] INFO    YamlProvider[config] populate:   found 13 records, exists=True
2026-10-26T04:11:49  [136961256718336] INFO    Rfc2136Provider[bind] populate:   found 14 records
*   Delete <ARecord A 300, grafana.example.com., ['203.0.113.77']>
*   Summary: Creates=0, Updates=0, Deletes=1, Existing=14, Meta=False

执行并验证(删除比例 1/14 ≈ 7.1%,低于 30% 阈值,无需 --force):

octodns-sync --config-file=/opt/octodns/config/dynamic.yaml --doit
dig @127.0.0.1 grafana.example.com A +short

实测回显(dig 无输出即记录已删除):

2026-10-26T04:11:49  [135327783968768] INFO    Rfc2136Provider[bind] apply: making 1 changes to example.com.
2026-10-26T04:11:49  [135327783968768] INFO    Manager sync:   1 total changes

4.5 zone 位置参数:只同步指定区

配置里声明了多个区时,把区名作为位置参数可只跑指定区(多区全量同步是默认):

octodns-sync --config-file=/opt/octodns/config/dynamic.yaml "sub.example.com."

实测回显(尾部节选,只有子区被处理):

2026-10-26T04:08:04  [135460420648960] INFO    Rfc2136Provider[bind] populate:   found 3 records
2026-10-26T04:08:04  [135460420648960] INFO    Rfc2136Provider[bind] plan:   No changes
2026-10-26T04:08:04  [135460420648960] INFO    Plan
********************************************************************************
No changes were planned
********************************************************************************

⚠️ 区名带尾点时注意 shell 引用:"sub.example.com." 建议加引号,避免个别 shell 补全/展开干扰。

4.6 --quiet:只看计划与告警

日常巡检不需要 populate 细节日志时用 --quiet(输出降为告警、错误与计划):

octodns-sync --config-file=/opt/octodns/config/dynamic.yaml --quiet

实测回显(全量输出就这四行):

2026-10-26T04:08:04  [128476810354688] INFO    Plan
********************************************************************************
No changes were planned
********************************************************************************

CI 流水线里解析计划结果建议固定用 --quiet,输出面最小、最稳。

4.7 --force:放行根 NS 变更护栏

apex NS 记录的任何变更默认被拒绝(防止误改委派)。实测当源文件中子区根 NS 的 TTL 与服务器现状不一致时,预览直接抛异常:

octodns.provider.plan.RootNsChange: Root NS record change, force required

按官方语义加 --force 后该计划才会执行。⚠️ 两个实测确认的细节:一是护栏在 apply 之前生效,被拒时什么都没有下发(计划框只是展示,不代表已执行);二是 --force 放行的是整个计划(包括同批次的其他变更),所以更稳妥的做法是把根 NS 变更单独成批次执行。若团队约定根 NS 一律不由 octoDNS 管理,可在配置里挂 IgnoreRootNsFilter/ExcludeRootNsChanges 处理器直接滤掉此类差异(见《octoDNS 1.22.0 配置参数详解》8.5)。

4.8 --force:放行 30% 变更比例护栏

Update/Delete 条数占既有记录比例超过 30%(update_pcent_threshold/delete_pcent_threshold 默认 0.3)时同样拒绝执行。实测在 12 条既有记录中区删除 4 条(33.33%)触发:

octodns.provider.plan.TooMuchChange: [example.com.] Too many deletes, 33.33% is over 30.00% (4/12), force required

处理方式二选一:加 --force 人工确认后执行;或大迁移分批删除(每批控制在 30% 以内)。批量换服务商场景建议后者,每一批预览一次、下发一次、report 核验一次。

4.9 --source / --target:按 provider 名筛选

--target bind 只向名为 bind 的目标同步(多目标配置下做灰度下发很有用):

octodns-sync --config-file=/opt/octodns/config/dynamic.yaml --target bind

实测回显(当前无差异,尾部节选):

No changes were planned
********************************************************************************

--source config 同理,把本次同步限定在由 config 源驱动的区上。二者都接受多次传参(如 --target bind --target backup)。

4.10 调试与日志落 syslog

排障时加 --debug 提升日志级别,能看到每个 provider 的 populate 细节与记录比对过程;需要把日志交给 rsyslog 集中采集时加 --log-syslog(可用 --syslog-device 指定 socket、--syslog-facility 指定 facility),或用 --logging-config 传一份 YAML 日志配置(格式遵循 Python logging.config dictSchema)。日志默认走 stderr,流水线采集建议加 --log-stream-stdout 改投 stdout。

五、octodns-dump:导出区数据为 YAML

octodns-dump 从任意已配置的 source 拉取区数据,写成 YamlProvider 规范格式的 YAML——迁移入库(把现网记录导入 Git 仓库)和「重新格式化」都靠它。

5.1 语法与全部参数

octodns-dump --help

实测回显(全量):

usage: octodns-dump [-h] --config-file CONFIG_FILE --output-dir OUTPUT_DIR
                    [--output-provider OUTPUT_PROVIDER] [--lenient] [--split]
                    [--version] [--log-stream-stdout] [--log-syslog]
                    [--syslog-device SYSLOG_DEVICE]
                    [--syslog-facility SYSLOG_FACILITY] [--debug] [--quiet]
                    [--logging-config LOGGING_CONFIG]
                    zone source [source ...]

Octo-DNS Dumper

positional arguments:
  zone                  Zone to dump, '*' (single quoted to avoid expansion)
                        for all configured zones
  source                Source(s) to pull data from

options:
  -h, --help            show this help message and exit
  --config-file CONFIG_FILE
                        The Manager configuration file to use
  --output-dir OUTPUT_DIR
                        The directory into which the results will be written
                        (Note: will overwrite existing files)
  --output-provider OUTPUT_PROVIDER
                        The configured provider to use when dumping records.
                        Must support copy() and directory
  --lenient             Ignore record validations and do a best effort dump
  --split               Split the dumped zone into a YAML file per record
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details

要点:zone 传 '*'(单引号防 shell 展开)可导出配置里的全部区;--output-dir 会覆盖既有文件;--lenient 在源记录有轻微不规范时尽力导出。

5.2 单文件导出(实测)

mkdir -p /opt/octodns/dump-d1
octodns-dump --config-file=/opt/octodns/config/dynamic.yaml \
  --output-dir=/opt/octodns/dump-d1 "example.com." config

实测回显(日志尾部节选):

2026-10-26T04:07:39  [129975178948608] INFO    Manager dump: zone=example.com., output_dir=/opt/octodns/dump-d1, output_provider=None, lenient=False, split=False, sources=['config']
2026-10-26T04:07:39  [129975178948608] INFO    YamlProvider[config] populate:   found 13 records, exists=True
2026-10-26T04:07:39  [129975178948608] INFO    YamlProvider[dump] populate:   found 0 records, exists=False
2026-10-26T04:07:39  [129975178948608] INFO    YamlProvider[dump] plan:   Creates=13, Updates=0, Deletes=0, Existing=0, Meta=False
2026-10-26T04:07:39  [129975178948608] INFO    YamlProvider[dump] apply: making 13 changes to example.com.

生成文件与内容(头部节选):

ls /opt/octodns/dump-d1/
example.com.yaml
---
? ''
: - ttl: 300
    type: A
    values:
    - 203.0.113.10
    - 203.0.113.11
  - type: CAA
    value:
      flags: 0
      tag: issue
      value: letsencrypt.org
  - type: NS
    value: ns1.example.com.
  - ttl: 300
    type: TXT
    value: v=spf1 ip4:203.0.113.0/24 -all
_dmarc:

导出格式两点解读:区根记录键被写成 ? ''(YAML 显式空键的标准写法,与手写的 "" 等价);TTL 等于导出 provider 默认 default_ttl=3600 的记录(CAA、NS)省略 ttl 键,其余保留——这是「规范格式」的一部分,直接可被 YamlProvider 重新加载。

5.3 --split:每条记录一个文件(实测)

mkdir -p /opt/octodns/dump-d1-split
octodns-dump --config-file=/opt/octodns/config/dynamic.yaml \
  --output-dir=/opt/octodns/dump-d1-split --split "example.com." config

实测回显(日志尾部节选,注意 provider 名变为 SplitYamlProvider):

2026-10-26T04:07:39  [125574253019136] INFO    YamlProvider[config] populate:   found 13 records, exists=True
2026-10-26T04:07:39  [125574253019136] INFO    SplitYamlProvider[dump] populate:   found 0 records, exists=False
2026-10-26T04:07:39  [125574253019136] INFO    SplitYamlProvider[dump] plan:   Creates=13, Updates=0, Deletes=0, Existing=0, Meta=False
2026-10-26T04:07:39  [125574253019136] INFO    SplitYamlProvider[dump] apply: making 13 changes to example.com.
ls /opt/octodns/dump-d1-split/example.com./

实测回显(目录名即区名,每记录一个文件):

$example.com.yaml
_dmarc.yaml
api.yaml
mail.yaml
mx1.yaml
mx2.yaml
ns1.yaml
sub.yaml
www.yaml

$example.com.yaml 是兜底文件(split_catchall 默认开),收纳区根(空键)等特殊记录;其余文件名即记录名。拆分形态适合超大区(单文件数千行难评审),对应 YamlProvider 的 split_extension: true 加载形态。

5.4 --output-provider:用配置里已有的 provider 落盘

默认用内置 YamlProvider 写出;--output-provider <名字> 可改用配置里声明的任意支持 copy() 与 directory 的 provider 实例落盘(例如配置里已定义的拆分 provider),避免为导出单独建目录参数。

六、octodns-compare:两数据源差异比对

octodns-compare 不生成计划、不落数据,只回答「两个数据源之间有没有差异、差在哪」——变更前核对、漂移检测(怀疑有人绕过 octoDNS 手工改了 DNS)都适用。

6.1 语法与全部参数

octodns-compare --help

实测回显(全量):

usage: octodns-compare [-h] --config-file CONFIG_FILE --a A [A ...] --b B
                       [B ...] --zone ZONE [--ignore-prefix IGNORE_PREFIX]
                       [--version] [--log-stream-stdout] [--log-syslog]
                       [--syslog-device SYSLOG_DEVICE]
                       [--syslog-facility SYSLOG_FACILITY] [--debug] [--quiet]
                       [--logging-config LOGGING_CONFIG]
                       [zone ...]

Octo-DNS Comparator

options:
  -h, --help            show this help message and exit
  --config-file CONFIG_FILE
                        The Manager configuration file to use
  --a A [A ...]         First source(s) to pull data from
  --b B [B ...]         Second source(s) to pull data from
  --zone ZONE           Zone to compare
  --ignore-prefix IGNORE_PREFIX
                        Record prefix to ignore from list of changes
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details

--a/--b 各接受一个或多个 provider 名;--ignore-prefix 忽略指定前缀的记录(如临时对比时排除 _acme-challenge 类记录)。

6.2 实测:YAML 源 vs BIND 服务器现状

octodns-compare --config-file=/opt/octodns/config/dynamic.yaml \
  --a config --b bind --zone example.com.

实测回显(尾部两行为结论):

2026-10-26T04:07:07  [133641294356480] INFO    Manager compare: a=['config'], b=['bind'], zone=example.com.
2026-10-26T04:07:07  [133641294356480] INFO    YamlProvider[config] populate:   found 13 records, exists=True
2026-10-26T04:07:07  [133641294356480] INFO    Rfc2136Provider[bind] populate:   found 13 records
[]

结论 [] 表示两源完全一致(Python 列表字面量,无差异元素)。验证其灵敏度:若在 BIND 侧手工多建一条记录,输出会变成列出该记录变更详情的非空列表,退出码亦随差异变化,可直接做巡检脚本断言。

七、octodns-report:解析结果报表

octodns-report 把配置里的每条记录向指定 DNS 解析器逐条真实查询,输出「配置值 vs 实际应答」的一致性报表——下发后的最终核验、多解析器一致性审计都靠它。

7.1 语法与全部参数

octodns-report --help

实测回显(全量):

usage: octodns-report [-h] --config-file CONFIG_FILE --zone ZONE --source
                      SOURCE [--concurrency CONCURRENCY] [--timeout TIMEOUT]
                      [--output-format {csv,json}] [--lenient] [--version]
                      [--log-stream-stdout] [--log-syslog]
                      [--syslog-device SYSLOG_DEVICE]
                      [--syslog-facility SYSLOG_FACILITY] [--debug] [--quiet]
                      [--logging-config LOGGING_CONFIG]
                      server [server ...]

Octo-DNS Reporter

positional arguments:
  server                DNS resolver to query

options:
  -h, --help            show this help message and exit
  --config-file CONFIG_FILE
                        The Manager configuration file to use
  --zone ZONE           Zone to dump
  --source SOURCE       Source(s) to pull data from
  --concurrency CONCURRENCY
                        Maximum number of concurrent DNS queries
  --timeout TIMEOUT     Number seconds to wait for an answer
  --output-format {csv,json}
                        Output format
  --lenient             Ignore record validations and do a best effort dump
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details

位置参数 server 是要查询的解析器地址(可传多个做横向比对);并发与超时的实际生效值会在日志行中打印(本环境实测 report concurrency=4 timeout=1)。

7.2 实测:CSV 报表

octodns-report --config-file=/opt/octodns/config/dynamic.yaml \
  --zone example.com. --source config --output-format csv 127.0.0.1

实测回显(日志头部节选 + 全部数据行):

2026-10-26T04:07:30  [132141501915136] INFO    report concurrency=4 timeout=1
2026-10-26T04:07:30  [132141501915136] INFO    YamlProvider[config] populate:   found 13 records, exists=True
2026-10-26T04:07:30  [132141501915136] INFO    report server=127.0.0.1
Name,Type,TTL,127.0.0.1,Consistent
example.com.,A,300,203.0.113.10 203.0.113.11,True
example.com.,CAA,3600,0 issue "letsencrypt.org",True
example.com.,NS,3600,ns1.example.com.,True
example.com.,TXT,300,"v=spf1 ip4:203.0.113.0/24 -all",True
_dmarc.example.com.,TXT,300,"v=DMARC1; p=none; rua=mailto:dmarc@example.com",True
api.example.com.,CNAME,300,www.example.com.,True
mail.example.com.,MX,600,10 mx1.example.com. 20 mx2.example.com.,True
mx1.example.com.,A,300,203.0.113.61,True
mx2.example.com.,A,300,203.0.113.62,True
ns1.example.com.,A,86400,203.0.113.53,True
sub.example.com.,NS,3600,ns1.example.com.,True
www.example.com.,A,300,203.0.113.99,True
www.example.com.,AAAA,300,2001:db8::20,True

13 条记录全部 Consistent=True(配置值与 127.0.0.1 的实际应答一致)。任何一行 False 即配置与该解析器的现实存在漂移,行内并列展示双方值。

7.3 实测:JSON 报表

octodns-report --config-file=/opt/octodns/config/dynamic.yaml \
  --zone example.com. --source config --output-format json 127.0.0.1

实测回显(日志数据部分,JSON 单行原样照录):

{"example.com.": {"A": {"127.0.0.1": ["203.0.113.10", "203.0.113.11"], "consistent": true}, "CAA": {"127.0.0.1": ["0 issue \"letsencrypt.org"], "consistent": true}, "NS": {"127.0.0.1": ["ns1.example.com."], "consistent": true}, "TXT": {"127.0.0.1": ["v=spf1 ip4:203.0.113.0/24 -all"], "consistent": true}}, "_dmarc.example.com.": {"TXT": {"127.0.0.1": ["v=DMARC1; p=none; rua=mailto:dmarc@example.com"], "consistent": true}}, "api.example.com.": {"CNAME": {"127.0.0.1": ["www.example.com."], "consistent": true}}, "mail.example.com.": {"MX": {"127.0.0.1": ["10 mx1.example.com.", "20 mx2.example.com."], "consistent": true}}, "mx1.example.com.": {"A": {"127.0.0.1": ["203.0.113.61"], "consistent": true}}, "mx2.example.com.": {"A": {"127.0.0.1": ["203.0.113.62"], "consistent": true}}, "ns1.example.com.": {"A": {"127.0.0.1": ["203.0.113.53"], "consistent": true}}, "sub.example.com.": {"NS": {"127.0.0.1": ["ns1.example.com."], "consistent": true}}, "www.example.com.": {"A": {"127.0.0.1": ["203.0.113.99"], "consistent": true}, "AAAA": {"127.0.0.1": ["2001:db8::20"], "consistent": true}}}

结构按「记录名 → 类型 → 解析器 → 应答值与 consistent 结论」三层嵌套,程序化断言读各记录的 consistent 字段即可(CSV 与 JSON 的记录集合、consistent 结论完全一致)。

八、octodns-validate:配置与记录校验

octodns-validate 把管理配置里所有区的记录全部构建一遍(含强类型校验),但不生成计划、不触碰任何目标——提交前自检与 CI 门禁的标准工具。

8.1 语法与全部参数

octodns-validate --help

实测回显(全量):

usage: octodns-validate [-h] --config-file CONFIG_FILE [--all] [--version]
                        [--log-stream-stdout] [--log-syslog]
                        [--syslog-device SYSLOG_DEVICE]
                        [--syslog-facility SYSLOG_FACILITY] [--debug]
                        [--quiet] [--logging-config LOGGING_CONFIG]

Octo-DNS Validator

options:
  -h, --help            show this help message and exit
  --config-file CONFIG_FILE
                        The Manager configuration file to use
  --all                 Validate records in lenient mode, printing warnings so
                        that all validation issues are shown
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details

8.2 实测:合法配置零输出通过

octodns-validate --config-file=/opt/octodns/config/production.yaml; echo "exit=$?"

实测回显(无任何输出,退出码 0 即全部合法):

exit=0

8.3 实测:非法记录的报错定位

把 www 的 A 记录值故意写成 not-an-ip 后校验(zones-bad 目录的坏记录示例,管理配置 bad.yaml 指向它):

octodns-validate --config-file=/opt/octodns/config/bad.yaml; echo "exit=$?"

实测回显(错误定位行 + 堆栈节选):

Traceback (most recent call last):
  ...
  File "/opt/octodns/venv/lib/python3.10/site-packages/octodns/manager.py", line 1350, in validate_configs
    source.populate(zone, lenient=zone_lenient)
  ...
octodns.record.exception.ValidationError: Invalid record "www.example.com.", /opt/octodns/zones-bad/example.com.yaml, line 55, column 5
  - invalid IPv4 address "not-an-ip", via: ip-value-rfc
exit=1

错误首行给出文件、行号、列号,次行给出原因与校验器名(ip-value-rfc),退出码 1 可直接做 CI 失败信号。

8.4 --all:宽松模式尽量多报问题

--all 以 lenient 方式校验并打印告警,适合「一次看清全部问题」:

octodns-validate --config-file=/opt/octodns/config/bad.yaml --all

实测回显(节选:先打 WARNING 行,随后仍因硬错误终止):

2026-10-26T04:07:07  [132077689778176] WARNING Record Invalid record "www.example.com.", /opt/octodns/zones-bad/example.com.yaml, line 55, column 5
  - invalid IPv4 address "not-an-ip", via: ip-value-rfc
Traceback (most recent call last):
  ...
ipaddress.AddressValueError: Expected 4 octets in 'not-an-ip'
exit=1

九、octodns-schema:生成 JSON Schema

octodns-schema 输出记录文件(zone)或管理配置(config)的 JSON Schema,配合编辑器插件实现「保存即校验」。它只做静态输出,不读源数据。

9.1 语法与全部参数

octodns-schema --help

实测回显(全量):

usage: octodns-schema [-h] [--kind {zone,config}] [--indent INDENT]
                      [--output OUTPUT] [--version] [--log-stream-stdout]
                      [--log-syslog] [--syslog-device SYSLOG_DEVICE]
                      [--syslog-facility SYSLOG_FACILITY] [--debug] [--quiet]
                      [--logging-config LOGGING_CONFIG]

octoDNS JSON Schema generator for zone or config YAML files

options:
  -h, --help            show this help message and exit
  --kind {zone,config}  Which schema to emit: zone (default) or config
  --indent INDENT       Number of spaces to indent the JSON output (default:
                        2)
  --output OUTPUT       Write schema to this file instead of stdout
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details

9.2 实测:config Schema 的顶层键

octodns-schema --kind config | python3 -c \
  "import json,sys; d=json.load(sys.stdin); print('title:', d['title']); print('properties:', list(d['properties'].keys()))"

实测回显:

title: octoDNS config file
properties: ['manager', 'providers', 'processors', 'secret_handlers', 'validators', 'zones']

9.3 实测:zone Schema 头部与落盘

octodns-schema --kind zone | head -12

实测回显(zone Schema 定义了每类记录的值结构,含 octodns_meta 元数据定义):

{
  "$defs": {
    "octodns_meta": {
      "additionalProperties": true,
      "properties": {
        "excluded": {
          "items": {
            "type": "string"
          },
          "type": "array"
        },

落盘给编辑器使用:octodns-schema --kind zone --indent 2 --output /opt/octodns/zone-schema.json。

十、octodns-versions:provider 版本盘点

打印配置里每个 provider 实际加载的包与版本——排障第一步(确认「跑的是哪个版本的什么包」)。

10.1 语法与实测

octodns-versions --help

实测回显(全量):

usage: octodns-versions [-h] --config-file CONFIG_FILE [--version]
                        [--log-stream-stdout] [--log-syslog]
                        [--syslog-device SYSLOG_DEVICE]
                        [--syslog-facility SYSLOG_FACILITY] [--debug]
                        [--quiet] [--logging-config LOGGING_CONFIG]

octoDNS Versions

options:
  -h, --help            show this help message and exit
  --config-file CONFIG_FILE
                        The Manager configuration file to use
  --version             Print octoDNS version and exit
  --log-stream-stdout   Log to stdout instead of stderr
  --log-syslog          Send logging data to syslog in addition to stderr
  --syslog-device SYSLOG_DEVICE
                        Syslog device
  --syslog-facility SYSLOG_FACILITY
                        Syslog facility
  --debug               Increase verbosity to get details and help track down
                        issues
  --quiet               Decrease verbosity to show only warnings, errors, and
                        the plan
  --logging-config LOGGING_CONFIG
                        Configure logging with a YAML file, see https://docs.p
                        ython.org/3/library/logging.config.html#logging-
                        config-dictschema for schema details
octodns-versions --config-file=/opt/octodns/config/production.yaml

实测回显(节选:Manager 初始化日志 + 逐 provider 版本行):

2026-10-26T04:07:07  [134883902816256] INFO    Manager __init__: config_file=config/production.yaml, (octoDNS 1.22.0)
2026-10-26T04:07:07  [134883902816256] INFO    Manager _config_executor: max_workers=1
2026-10-26T04:07:07  [134883902816256] INFO    Manager _config_include_meta: include_meta=False
2026-10-26T04:07:07  [134883902816256] INFO    Manager _config_enable_checksum: enable_checksum=False
2026-10-26T04:07:07  [134883902816256] INFO    Manager _config_auto_arpa: auto_arpa=False
2026-10-26T04:07:07  [134883902816256] INFO    Manager __init__: provider=config (octodns.provider.yaml 1.22.0)
2026-10-26T04:07:07  [134883902816256] INFO    Manager __init__: provider=bind (octodns_bind 1.1.0)
2026-10-26T04:07:07  [134883902816256] INFO    Manager _configure_validators: enabling sets ['legacy']

版本行即 provider=<配置名> (<包> <版本>);顺带能看到 manager 四参数的实际生效值(本环境 max_workers=1 为单核下的自动值)。

十一、注意事项

  1. 预览与执行的边界:octodns-sync 不加 --doit 永不写数据;1.22.0 无 --dry-run 旗标,预览就是默认行为,脚本里判断「是否执行」只认 --doit 是否出现。
  2. 计划框是展示、护栏在 apply 前:RootNsChange/TooMuchChange 被拒时什么都没下发(4.7/4.8 实测),不要把计划框误读为已生效。
  3. --force 放行整个计划:把大变更拆小批次,别用 --force 一把梭。
  4. 目标形态决定计划形态:RFC 2136 目标出增量计划(Create/Update/Delete),ZoneFileProvider 文件目标恒为全量 Creates + 整文件重写(需 rndc reload),对比与排障时先确认目标类型。
  5. octodns-dump 会覆盖输出目录中的同名文件(help 原文注明),导出前确认目录用途。
  6. report 的 consistent 是逐解析器结论:多解析器审计要逐个 server 跑或一次传多个 server,单次 True 不代表全网一致。
  7. zone 名带尾点:sync 位置参数与 compare 的 --zone 都要传 example.com. 规范形式,建议加引号。

十二、常见问题

Q1:执行 octodns-sync 提示命令不存在? 八成在系统 Python 里找了。octoDNS 装在虚拟环境,用绝对路径 /opt/octodns/venv/bin/octodns-sync,或先 export PATH=/opt/octodns/venv/bin:$PATH。

Q2:预览有 Create/Update 计划,--doit 后记录没生效? 先看 --doit 输出有没有 apply: making N changes:没有说明护栏拦截(往上翻 RootNsChange/TooMuchChange 异常);有 making N changes 但 dig 查不到,检查 dig 指向的解析器是否为目标服务器本身(RFC 2136 即时生效,zone 文件目标则要先 rndc reload)。

Q3:octodns-validate 报 keys out of order? YAML 键序校验(enforce_order 默认开),顶层键按字母序、记录字典内 ttl → type → value(s),按报错行列号把键挪到正确位置;批量重排用 octodns-dump 重新导出规范格式最省事。

Q4: octodns-report 全部 False? 多为 server 参数指错(应答的不是配置里那个目标),或查询的区在解析器侧未加载;先 dig @<server> <区 SOA> 确认解析器认识该区再跑 report。

Q5:迁移老 BIND 服务器记录从哪条命令入手? 把该服务器配成 AXFR source(octodns-bind 包 AxfrSource)或临时 ZoneFileProvider source,先 octodns-compare 摸底、再 octodns-dump 导出 YAML 入库,之后按《octoDNS 1.22.0 配置参数详解》的 provider 配置接入正式同步。

十三、参考链接

  1. octoDNS 官方文档(工具总览):https://octodns.readthedocs.io/en/latest/(检索日期 2026-10-26)
  2. octodns-bind 包(ZoneFileProvider/BindProvider/AxfrSource):https://github.com/octodns/octodns-bind(1.1.0)
  3. PyPI 项目页:https://pypi.org/project/octodns/(1.22.0,检索日期 2026-10-26)
  4. 站内系列:《octoDNS 1.22.0 介绍与选型指南》、《Ubuntu 22.04 安装 octoDNS 1.22.0 指南》、《octoDNS 1.22.0 配置参数详解》(专栏 octoDNS)
  5. 站内同类工具(Go 形态 CLI 对照):《DNSControl 5.7 介绍与选型指南》、《Ubuntu 22.04 安装 DNSControl 5.7 指南》(专栏 DNSControl)