DNS 与流量工程
Cirrus CDN 集成内部权威 DNS,以动态反映控制平面状态。本章阐述 CNAME 服务如何构建区域数据、监控节点健康,并与从域名服务器协同,将客户端路由至健康的缓存节点。
CNAME 服务概览
src/cirrus/cname/service.py 定义的 CnameService 负责:
- 通过
get_cname_settings()(cname/settings.py)加载基础配置。 - 启动隐藏主 DNS 服务器(
cname/dns_server.py中的HiddenMasterServer)。 - 订阅 Redis 发布订阅通道
cdn:cname:dirty。 - 在配置或健康事件变更时重建 DNS 区域。
- 向下游从节点(如 NSD)发送 NOTIFY 消息。
该服务在 FastAPI 启动时初始化,并维护后台任务以响应 dirty 事件。
配置与设置
cname/settings.py 从环境变量读取并构建 CnameSettings:
CNAME_BASE_DOMAIN:顶级域名(docker-compose.yml中为cdn.local.test)。CNAME_REPLICAS_PER_SITE:每个站点分配的节点数量(默认 2)。CNAME_DEFAULT_TTL:生成记录的 TTL(默认 60 秒)。DNS_SOA_*:SOA 计时参数,用于阴性缓存与刷新策略。DNS_NS1_A、DNS_NS1_AAAA:权威 DNS 的 glue 记录。DNS_MASTER_BIND_ADDR、DNS_MASTER_PORT:隐藏主节点监听地址/端口(默认0.0.0.0:10053)。CNAME_DNS_SLAVES:以逗号分隔的从节点端点列表,支持 IPv4/IPv6。
辅助结构 NodeHealthSettings 控制健康检查行为,对应环境变量包括 NODE_HEALTH_PORT、NODE_HEALTH_INTERVAL_SECS、NODE_HEALTH_FAILS_TO_DOWN、NODE_HEALTH_SUCCS_TO_UP 等。
区域构建
Rendezvous 哈希
cname/hashing.py 实现了 rendezvous 哈希(rendezvous_topk),用于生成稳定的域名节点映射:
- 以访问 FQDN 作为哈希种子。
- 为每个节点 ID 评分,选出前
replicas_per_site个。 - 当节点增减时仅最小化重排。
ZoneBuilder
cname/zone.py 的 ZoneBuilder 负责:
- 过滤出
node.can_serve()的激活节点。 - 为每个域名生成指向节点 IP 的
A/AAAA记录,同时将域名转换为 IDNA 安全的访问 FQDN。 - 为基础区域添加 SOA 与 NS 记录。
- 依据记录内容生成确定性签名,仅在签名变化时递增序列号,保持符合 RFC。
- 产出包含
records、serial与generated_at时间戳的ZoneSnapshot。
访问 FQDN
compute_access_fqdn(domain, settings) 将业务域转换为服务端点:
<site-domain>.<base_fqdn>
例如 www.example.com 会映射为 www.example.com.cdn.local.test。函数会验证每个标签不超过 63 字符,整体 FQDN 不超过 255 字符。
隐藏主服务器
cname/dns_server.py 实现了同时支持 UDP 与 TCP 的轻量权威服务器:
- 安全性——仅允许配置的从节点 IP 查询/AXFR(通过
ipaddress归一化),未授权客户端返回REFUSED。 - UDP 处理——响应标准查询,区分 NXDOMAIN 与 NOERROR,并在阴性应答中附加 SOA。
- TCP AXFR——顺序回放记录完成全量区域传输,确保与 NSD 兼容。
- NOTIFY——每次区域重建后向各个从节点发送 DNS NOTIFY,提示其执行 AXFR;重试采用指数退避,并在线程池中运行以避免阻塞事件循环。
这里未采用 OpenResty 式的 LRU 缓存;DNS 依靠内存快照加锁机制保证重建过程的一致性。
基于健康的路由
cname/health.py 通过 Celery 运行(见第 4 章):
- 遍历
cdn:nodes中注册的节点。 - 对每个节点在配置端口(默认 9145,对应 OpenResty 指标服务器)执行
/healthzHTTP GET。 - 调整
health_fails/health_succs计数,在达到阈值时切换active标志。 - 当激活状态变化时发布
cdn:cname:dirty,强制触发 DNS 更新。 - 返回结构化结果,用于日志或调试。
此健康循环将控制平面操作与数据平面稳定性解耦,确保 DNS 仅公布健康端点。
与外部 DNS 集成
docker-compose.yml 中的 nsd 容器作为可公开访问的从节点:
- 在
10054端口接收 NOTIFY。 - 向客户或其他递归解析器提供区域。
- Cirrus 可通过扩展
CNAME_DNS_SLAVES来横向增加从节点。
生产环境中,NSD 实例通常分布在不同区域并使用 anycast IP 以降低延迟。
故障场景与恢复
- Redis 故障——缺少配置时 DNS 服务无法启动;API 会记录
cname_settings_invalid。运行期若因数据拉取异常导致无法重建区域,会记录日志并等待后续事件。 - 节点抖动——健康检查依赖连续失败/成功阈值避免震荡;rendezvous 哈希减少记录变动。
- 从节点断连——NOTIFY 失败会逐个记录,运维需排查网络连通性。
- 配置错误——非法的基础域名或从节点配置会导致启动失败,API 在启动阶段即可暴露问题。
运维要点
- 确保生产环境将
CNAME_BASE_DOMAIN委派给 NSD(或等效)实例。 - 关注
cname_hidden_master_listening日志确认绑定地址,并留意cname_notify_failed以排查从节点通信。 - 定期检查 Redis 键空间防止遗留条目;通过 API 删除域名即可自动清理,描述性键名也便于手工排查。
DNS 是将控制平面声明连接至终端体验的关键环节。下一章将探讨 OpenResty 在请求处理时如何解读同一份域名与节点状态。