出租窝

DNS不只是A记录:从Homelab到企业平台的一些思考

背景

我最早对 DNS 产生兴趣,不是因为 A 记录,而是因为 MX 记录。

刚开始做邮件工程师时,我对域名、主机名和记录之间的对应关系非常敏感。MX 指向的是邮件主机名,不是一个可以随便填写的 IP;邮件主机还要有正确的 A/AAAA 和 PTR;发送时的 EHLO、反向解析以及 SPF、DKIM、DMARC 又共同影响着身份判断和投递结果。一个看起来不起眼的主机名不一致,就可能让邮件进入垃圾箱,甚至直接被拒收。

那段经历让我第一次意识到:DNS 并不只是“域名解析到 IP”的电话簿。它同时参与了服务定位、身份表达、信任建立和系统边界的划分。域名怎么分层、主机名怎么命名、正向和反向是否一致,这些都属于系统设计的一部分。

2019 年写《家用DNS选择》时,我又从邮件系统回到了 Homelab:家里 DHCP 分配的 IP 总在变化,希望找一个轻量、支持 API、方便迁移的 DNS 服务。那时最直接的需求仍然是给变化的地址一个稳定名称,但继续往下折腾后会发现,A 记录只是 DNS 最基础的一种玩法。

DNS 还能描述服务端口和优先级、发布少量资源元数据、引导 Agent 找到平台入口、约束证书签发,并成为自动化和安全审计的一部分。这些能力不只适用于 Homelab:小公司需要低成本的服务发现和资产归属,大公司则需要域名委派、权限、审计、高可用和安全治理。规模不同,组件和管理方式会变,但底层原则是通用的。

这篇文章不是某个 DNS 产品的部署手册,也不是一个项目实施计划,而是我对 DNS 在基础设施和平台系统中还能承担哪些职责的一次整理:

DNS 负责回答“去哪里、怎么连接、由谁管理”;平台 API 负责实时状态、完整配置、权限和复杂业务逻辑。

或者说得更简单一点:DNS 是目录,不是数据库;是路标,不是目的地。

我理解的 DNS 设计原则

名称是稳定契约,地址只是实现细节

IP、机房、集群和容器都会变化,但调用方不应该跟着基础设施不断修改配置。一个稳定的服务名称应当成为客户端与平台之间的契约,再由 A、AAAA 或 CNAME 把它映射到当前实现。

域名层级应该表达稳定边界

域名结构适合表达环境、项目、服务等相对稳定的维度,例如 api.test.project.example.com。团队名称和负责人经常变化,更适合放在 TXT 或服务目录中,而不是永久编码进域名。好的域名规划应该支持委派,让不同团队能够管理自己的命名空间,而不必共享整个根域的修改权限。

正向和反向解析是一套完整身份

邮件系统让我对 PTR 特别敏感,但这个原则并不局限于邮件。A/AAAA 回答“这个名字在哪里”,PTR 回答“这个地址是谁”。两者一致时,日志、审计、资产核对和故障排查都会清晰很多。

主机名应该成为物理资产的稳定索引

以前管理几百台物理服务器时,一个很实用的原则是:日常运维使用主机名,不直接使用 IP。

当机器规模达到 200~1000 台以后,IP 往往分散在不同网段,中间还有扩容、迁移和历史遗留形成的大量不连续地址。如果仍然把 IP、用途和负责人维护在 Excel 中,表格很快就会过期,同一台机器还可能在监控、Ansible、SSH 配置和故障记录里出现不同的写法。

更合理的方式是给每台物理机一个稳定、唯一的主机名,例如:

srv-00231.hosts.example.com. 300 IN A 10.42.3.17
17.3.42.10.in-addr.arpa. 3600 IN PTR srv-00231.hosts.example.com.

运维系统统一引用 srv-00231.hosts.example.com。机器换 IP 时只需要修改 A 和 PTR,Ansible inventory、监控目标、SSH 入口和日志中的身份都不需要跟着变化。主机名中的编号最好对应稳定的资产 ID;机架、用途、负责人等容易变化的信息仍然放在 CMDB,而不是全部编码进名字。

这种方式并不是用 DNS 替代 CMDB,而是让 DNS 成为物理资产的访问索引:CMDB 保存完整资产关系,DNS 负责把稳定身份映射到当前地址。对于几百到上千台机器,这比维护一份不连续 IP 的 Excel 更可靠,也更适合自动化。

TTL 是一致性模型,不只是性能参数

DNS 天生有缓存。TTL 越长,查询压力越小,但变更传播越慢;TTL 越短,变化更快,却会增加权威和递归服务器的负担。设计记录时必须先接受“最终一致”的事实,再根据记录的变化频率选择 TTL,而不是遇到故障时才临时把 TTL 调低。

DNS 只保存稳定、非敏感、低频的信息

适合进入 DNS 的内容应该短小、非敏感、允许缓存。实时健康状态、密钥、动态权限和完整配置不符合这些条件,应该留在监控、Vault、IAM 或配置中心。DNS 最适合保存索引和入口,而不是正文。

每条记录都应该有 Owner 和生命周期

域名创建很容易,回收往往被忽略。无论记录来自人工、DHCP、云平台还是 GitOps,都应该知道由谁管理、为什么存在、什么时候可以删除。自动化不只是自动创建,还应包括校验、审计和回收。

标准记录优先,个性逻辑按需增加

A、AAAA、CNAME、MX、SRV、TXT、PTR 和 CAA 已经能解决大量问题。只有标准记录表达不了需求时,才需要 rewrite、template、自定义插件或复杂流量策略。标准协议越多,客户端兼容性和长期维护成本越可控。

从 PVE 和 OpenWrt 的 hostname.lan 开始

在 Homelab 里,DNS 最容易落地的玩法甚至不需要先搭一套权威服务器。PVE 上的虚拟机或 LXC 配合 OpenWrt 自带的 dnsmasq,就可以获得一套很好用的本地主机名解析。

例如一台虚拟机通过 cloud-init 或客户机系统设置 hostname 为 gitlab,再通过 OpenWrt DHCP 获取地址并上报 hostname,dnsmasq 就可以把 DHCP 租约转换成本地 DNS 记录:

gitlab.lan → 192.168.1.20

OpenWrt 常见的本地域配置是:

local domain: lan
local server: /lan/
expand hosts: enabled

如果 DHCP 同时向客户端下发 lan 作为搜索后缀,那么下面两种访问方式都可以工作:

ssh gitlab.lan
ssh gitlab       # 解析器自动补成 gitlab.lan

实际使用时最好给重要虚拟机配置静态 DHCP 租约,将 MAC、IP 和 hostname 绑定起来。虚拟机如果在操作系统内直接配置静态 IP,而不经过 DHCP,dnsmasq 通常无法自动学习它的 hostname,这时需要在 OpenWrt 中手动增加 host 记录。

.lan 在 Homelab 中简单直观,也被 OpenWrt 广泛使用,但它不是正式保留的家庭网络后缀。更严格地遵循标准可以使用 RFC 8375 定义的 home.arpa;应避免把 .local 当普通 DNS 后缀,因为它通常由 mDNS 使用。如果需要公共 HTTPS 证书,则应该使用自己拥有的真实域名,而不是 .lan

这个例子看起来很小,却已经包含了 DNS 平台化的雏形:DHCP 是数据来源,dnsmasq 自动生成记录,搜索后缀改善客户端体验,静态租约承担最基础的资产约束。规模扩大以后,只是把这些职责拆分给更专业的组件。

DNS 在平台里的位置

内部 DNS 可以按四层能力来理解。

L1:基础解析

使用 A、AAAA、CNAME 和 PTR,解决名称、地址和稳定入口问题。

这是 DNS 最传统也最核心的职责。

L2:服务发现

使用 SRV、TXT,以及按需验证的 SVCB/HTTPS、URI 等记录,发布服务端口、协议、Owner 和平台入口。

SRV 比单纯的 A 记录多回答了几个问题:服务通过什么协议访问、端口是多少、多个实例的优先级和权重是什么。TXT 则可以补充少量稳定元数据。

L3:自动化管理

通过 DNS API(例如 PowerDNS API)、RFC 2136、GitOps 或 ExternalDNS 类 Controller 管理记录生命周期。

DNS 记录不应该长期依赖人工登录后台修改。更理想的流程是:

开发提交 DNS 声明
CI 校验格式、命名和冲突
合并后由 Controller 调用 DNS API
记录创建、更新、审计和回收

L4:安全治理

通过 CAA、SSHFP、TLSA、DNSSEC、RPZ/Sinkhole 和查询审计,参与证书治理、主机身份验证和威胁检测。

这里的重点不是让 DNS 承担全部安全职责,而是利用它位于所有连接之前的天然位置,提供低成本的治理入口。

一套推荐的记录组合

下面以内部项目服务为例。假设我们要发布一个测试环境 API:

; 稳定业务入口,与实际部署解耦
api.test.satellite.devops.example.com. 300 IN CNAME \
  gateway.k3s-test.k8s.devops.example.com.

; 服务地址、端口、优先级和权重
_api._tcp.test.satellite.devops.example.com. 60 IN SRV \
  10 100 443 gateway.k3s-test.k8s.devops.example.com.

; 轻量、稳定的资源元数据
_meta.api.test.satellite.devops.example.com. 300 IN TXT \
  "service-id=svc-1024"
_meta.api.test.satellite.devops.example.com. 300 IN TXT \
  "owner=satellite-team"
_meta.api.test.satellite.devops.example.com. 300 IN TXT \
  "environment=test"
_meta.api.test.satellite.devops.example.com. 300 IN TXT \
  "managed-by=gitops"

这组记录分别回答:

  1. 服务入口在哪里;
  2. 使用什么协议和端口;
  3. 这个服务属于哪个平台对象;
  4. 谁负责;
  5. 当前是什么环境;
  6. 由什么系统管理。

应用仍然可以像过去一样只解析 A/CNAME。平台 CLI、Agent 或排障工具则可以进一步查询 SRV 和 TXT,获得更多上下文。

TXT 只保存“索引”,不要保存“正文”

TXT 很灵活,也最容易被滥用。比较适合保存的内容包括:

字段示例
service-idsvc-1024
ownersatellite-team
environmentteststagingprod
managed-bygitopsterraformansible
projectsatellite
regionbj1sh2

这些信息应当稳定、简短、低频变化。完整详情仍然放在平台或 CMDB 中:

DNS TXT 返回 service-id
平台 API 查询完整资产、依赖和负责人信息

以下内容不应该进入 TXT:

原因也很直接:DNS 有缓存,响应大小有限,查询通常会被日志记录,而且不适合复杂检索和权限控制。

DNS 适合做 Bootstrap

新主机、CI Runner 或 Agent 经常遇到一个“先有鸡还是先有蛋”的问题:它需要从平台获取配置,但首先得知道平台在哪里。

DNS 很适合解决这个入口发现问题:

_bootstrap._tcp.devops.example.com. 60 IN SRV \
  0 100 443 bootstrap.devops.example.com.

客户端先通过 DNS 找到 Bootstrap 服务,再通过 HTTPS 下载完整配置:

DNS:发现配置服务的位置
HTTPS/API:下载 JSON、YAML、证书和策略
Vault:提供密钥和敏感配置

DNS 不直接下发完整配置,只负责把客户端带到正确的服务入口。

DNS 故障切换的边界

健康检查系统可以调用 DNS API,动态修改 A、AAAA、CNAME 或 SRV,实现主备入口、网关、机房或集群切换:

健康检查发现故障
DNS API 更新记录
客户端在 TTL 过期后获得新地址

这个方案适合分钟级、机房级切换,但它不是实时 HA。实际恢复时间还会受到以下因素影响:

因此 DNS 不能替代负载均衡器、数据库代理、Service Mesh 或强实时故障转移系统。

证书与安全治理

CAA:约束证书签发

CAA 可以声明允许哪些 CA 为域名签发证书:

satellite.devops.example.com. 86400 IN CAA 0 issue "ca.example.com"
satellite.devops.example.com. 86400 IN CAA 0 iodef "mailto:security@example.com"

如果需要通配符证书,还应明确配置 issuewild。CAA 是治理手段,不负责完成 ACME 验证;内网权威 DNS 无法被公网 CA 查询时,仍然需要公共 DNS API、CNAME challenge delegation 或内部 CA。

RPZ 与 Sinkhole:拦截恶意域名

DNS 还可以配合 RPZ 或安全 DNS,把已知恶意域名指向内部 Sinkhole,用于阻断回连和识别失陷主机。

但不建议人工维护大量普通 Zone 记录来做封禁,规则规模起来以后应该交给 RPZ 或专门的安全 DNS 产品。

查询日志:比“用 DNS 传数据”更有价值

DNS 日志可以用于识别:

DNS Tunnel 技术上可以传输数据,但它是常见的数据外带手法。内部平台应该建设的是检测、审计和阻断能力,而不是把 DNS 当成消息或遥测通道。

理解 DNS,也要理解它的边界

DNS 的可扩展性很强,但“技术上能做”不等于“适合成为正式能力”。判断一个需求是否适合放进 DNS,可以先问几个问题:

  1. 这份数据是否允许被缓存?
  2. 它是否稳定、简短、非敏感?
  3. 客户端是否只需要定位,而不是事务和实时状态?
  4. 查询结果被重复、延迟或短暂不一致时,系统是否仍然安全?
  5. 使用标准记录能否表达,而不需要自定义协议?

如果答案大多是否定的,就不应该为了“DNS 也能做”而强行放进去。

不适合的场景主要原因更合理的系统
完整 CMDB数据量、资源关系和权限能力不足CMDB/服务目录
配置中心缓存、大小、版本和安全问题Nacos、Consul KV、etcd
实时健康状态TTL 无法保证实时性监控系统/平台 API
消息队列、Pub/Sub没有推送、ACK 和顺序保证NATS、Kafka、MQTT
IoT 遥测无可靠性和确认机制MQTT、HTTP、CoAP
函数触发查询可能被缓存和重放Webhook/事件总线
权限系统缓存会造成权限变更延迟IAM/RBAC

工具只是设计原则的实现

具体工具仍然值得区分,但选择应当发生在职责划分之后。

组件适合承担的职责
CoreDNSKubernetes/etcd 集成、转发、改写、模板和定制发现逻辑
PowerDNS Authoritative标准权威 Zone、数据库记录、REST API、DNSSEC 和主从复制
PowerDNS Recursor递归解析、缓存、条件转发和策略控制
dnsdist统一入口、规则分流、负载均衡、限速与协议接入

如果重点是稳定地管理公司权威域,PowerDNS Authoritative 的职责比较清晰;如果需要 Kubernetes、etcd、rewrite 或 template 等个性逻辑,CoreDNS 更灵活。如果希望使用完整的 PowerDNS 体系,可以用 Recursor 承担递归和缓存,再用 dnsdist 将权威查询与递归查询分发到不同后端。

更通用的理解不是“选 CoreDNS 还是 PowerDNS”,而是先回答三个问题:谁保存权威事实、谁负责递归和缓存、谁承载特殊逻辑。小环境里这些职责可以由一两个组件完成,规模扩大后再逐步拆开。

不同规模下,同一套原则

规模增长时,变化的是实现方式,而不是 DNS 的基本设计原则。

规模主要关注点设计重点
Homelab主机名、缓存、少量服务发现利用 DHCP、dnsmasq 和搜索后缀,让名称代替易变的 IP
小公司项目域名、Owner、自动化、证书和审计分清递归与权威,建立命名规范、管理接口和记录生命周期
大公司多团队委派、高可用、权限和安全治理让 Zone 委派对应组织边界,建设多节点、RBAC、审计、DNSSEC 和安全策略

Homelab 解决的是“我不想记 IP”;小公司开始面对“谁能创建什么域名、记录归谁、项目下线后如何回收”;大公司进一步处理组织边界、子域委派、多地域容灾和安全策略。三者并不是三套互不相关的方案,而是同一组原则在不同规模下的展开。

职责分离,而不是产品堆叠

客户端 / 应用 / Agent
DNS 入口
  ├── 递归与缓存 ──→ 外部或其他权威 DNS
  ├── 标准权威域 ──→ 权威记录与管理接口
  └── 特殊功能 ────→ 插件、动态数据源或自定义策略

自动化 / GitOps / 管理平台
          └──→ 权威记录的创建、校验、审计和回收

这张图刻意不绑定产品。使用 PowerDNS 时,可以由 Authoritative、Recursor 和 dnsdist 分别承担权威、递归与入口职责;需要插件化逻辑时,可以让 CoreDNS 管理特定的子域或作为独立后端。

真正重要的是:权威数据要稳定和可审计,递归查询要正确处理缓存,特殊逻辑要与标准域隔离,管理凭据不能直接暴露给普通用户。完整信息通过平台 API 获取,敏感信息交给密钥管理系统,日志和指标进入可观测平台。

最后

从 Homelab 中“把变化的 IP 变成稳定名字”,到公司环境里的服务发现、Owner、证书、GitOps 和安全审计,真正变化的不是 DNS 协议,而是我们如何组织和使用它。

DNS 的优势一直是简单、稳定、跨平台和无处不在。一个人的 Homelab、几十人的小公司和跨地域的大公司,都可以从同样的模型开始,只是在不同阶段逐步增加自动化、权限、高可用和安全能力。

只要守住边界,DNS 就能成为成本很低、覆盖面很广的一层基础能力:

DNS 不只是 A 记录。它可以成为基础设施的目录和平台的引导层,但它仍然只是路标,不是目的地。