Cloudpods region-dns:CoreDNS 插件的解析行为约定与实现解析
发布时间:2026/10/13 15:00:58 锦皓数字建站

后端云原生运维虚拟化人工智能AI 应用【免费下载链接】cloudpods开源、云原生的多云管理及混合云融合平台项目地址https://gitcode.com/yunionio/cloudpods点击查看免费下载pkg/dns是 Cloudpods 提供的 CoreDNS 插件yunion用于让每个 Region 内的云主机、物理机、自定义 DNS 记录以Cloudpods 域名如xxx.dns_domain的形式被解析。本文以 pkg/dns/README.md 中定义的“行为约定”A 记录与 PTR 记录各场景的期望结果为骨架结合插件源码逐项拆解 region-dns 的匹配规则、记录来源优先级、反向解析链路以及 Corefile 配置参数与log插件配置约定帮助你完整掌握 Cloudpods 内置 DNS 服务的解析行为与部署配置方式。1. region-dns 是什么基于 CoreDNS 的 Region 级 DNS 插件region-dns 的可执行入口在 cmd/region-dns/main.go它通过coremain.Run()启动一个 CoreDNS 实例并以空导入方式注册 Cloudpods 插件_ yunion.io/x/onecloud/pkg/dns同时在 main.go 中声明了yunion指令所在的指令顺序directives插件被安排在acl之后、hosts/file/loop/forward之前——即匹配到 yunion zone 的查询先交给 Cloudpods 插件处理处理不了fallthrough才会继续流向后面的插件。插件本体是 pkg/dns/dns.go 中的SRegionDNS结构体核心字段包括Zones/PrimaryZone监听的 zone 列表与主域名即dns_domainguest 名解析会拼接到其下SqlConnection直接连接 Cloudpods 的 MySQL用于查询 guest、host、dns records 表AuthUrl/AdminUser/AdminProject/AdminDomain/AdminPassword以管理员身份访问 region 服务配置的凭据InCloudOnly为 true 时拒绝云外来源的查询请求Upstream/Fall/NextCoreDNS 的转发与 fallthrough 机制。插件在 setup.go 中通过plugin.Register(PluginName, setup)注册其中PluginName固定为yunion见 dns.go。此外 xfr.go 实现了Transferer接口Serial返回当前 Unix 时间戳MinTTL返回 30但Transfer直接返回SERVFAIL即 zone 传输AXFR/IXFR不被支持。1.1 支持的记录类型dns.go 中的DNSTypeMap定义了插件可应答的记录类型记录类型说明A主机名 → IPv4AAAA主机名 → IPv6TXT文本记录含保留用途的dns-version探测记录CNAME/MX/SRV常规记录类型PTR反向解析在reverse.go中走独立逻辑SOA/NSzone 权威记录不在该映射表内的 QTYPEServices()会直接返回errRefusedREFUSED见 dns.go。2. 行为约定A 记录README 定义的完整测试场景README 的“行为约定”一节给出了 A 记录解析的验收脚本覆盖 guest、host、自定义记录、公共转发四类数据源。完整继承如下names names$names titan #ok, guest PlainName names$names titan.hq.cloud.yunionyun.com #ok, guest CloudZoneFQDN names$names kubenode #ok, host PlainName names$names kubenode.hq.cloud.yunionyun.com #ok, host CloudZoneFQDN names$names whoever-the-ether #NXDOMAIN, nonexistent PlainName names$names whoever-the-ether.titan.hq.cloud.yunionyun.com #NXDOMAIN, nonexistent CloudZoneFQDN names$names mail.google.com #ok, dnsrecords names$names www.douban.com #ok, pub names$names app #ok, k8s svc PlainName in default ns names$names app.default #NXDOMAIN, k8s svc name.namespace names$names app.default.hq.cloud.yunionyun.com #ok, k8s name.ns CloudZoneFQDN names$names mon-kafka.system #NXDOMAIN, k8s svc name.namespace names$names mon-kafka.system.hq.cloud.yunionyun.com #ok, k8s name.ns CloudZoneFQDN # TODO source IP # TODO other TYPEs for name in $names; do echo ############### $name #dig 192.168.222.171 $name #dig -p 54 10.168.222.136 $name dig -p 54 192.168.222.171 $name done对照源码可以逐条印证这些约定如何落地场景期望源码依据titanguest PlainNameok单 label 名走 guest/host 内部解析见 dns.gofindInternalIpstitan.hq.cloud.yunionyun.comguest CloudZoneFQDNok属于主域名isMyDomain匹配取首段 label 查 guestkubenode/kubenode.zonehostokgetHostIpWithName按名查 host 表并返回AccessIp见 dns.go不存在的 PlainName / FQDNNXDOMAIN各级查找均落空后返回errNotFound映射为RcodeNameErrormail.google.com自定义 dns recordsok非本域名走queryLocalDnsRecords查数据库 DNS 记录表www.douban.compub云外公网名ok未匹配记录且不在主域名内时返回errCallNext由 fallthrough 交给forward等插件解析公网appk8s svc PlainNamedefaultnsok约定行为当前源码中 k8s service 后端解析逻辑处于注释停用状态dns.go可推断该场景在完整部署中依赖流水线中其他插件或后续版本的 k8s 集成app.default/mon-kafka.systemname.namespace形式NXDOMAINk8s svc 的name.namespace裸名不做解析必须带 CloudZoneFQDN 后缀2.1 来源 IP 识别与 in_cloud_only 拦截每条查询进入Services()前会先经过Records()dns.go其中parseRequestparse.go负责识别请求来源取源 IPSrcIP4()先查models.GuestnetworkManager.GetGuestByAddress(srcIP)——若该 IP 属于某台云主机网络则记录来源项目srcProjectId并标记srcInCloud true否则查models.NetworkManager.GetOnPremiseNetworkOfIP(srcIP)——若属于本地OnPremise网络同样标记srcInCloud true不归属具体项目。源码注释明确说明“顺序很重要要尽可能精确地定位源 IP 的项目”。若配置了in_cloud_only云外来源srcInCloud false的查询会被直接REFUSEDerrRefused见 dns.go防止内部域名被外网探测。这也是 README 中 “TODO source IP” 备注所指的演进方向。2.2 域名归属判定PlainName、CloudZoneFQDN 与 dnsrecords 的分流findRecordsdns.go按三条路径分流非本域名如mail.google.com直接查本地 DNS 记录表queryLocalDnsRecords命中则应答未命中返回errCallNext交给后续插件即 README 中www.douban.com的 “pub” 场景PlainName单 label如titan仅当源 IP 在云内SrcInCloud()才解析内部记录否则REFUSED——裸名只允许云内解析避免内部机器名被云外获取本域名 FQDNisMyDomain判定同样查内部记录表查不到则NXDOMAIN。isMyDomain的实现parse.go 附近逻辑是查询名的 label 数必须大于主域名PrimaryZone的 label 数且后缀逐段匹配主域名——这正好区分了titanPlainName与titan.hq.cloud.yunionyun.comCloudZoneFQDN两种约定场景。2.3 内部记录的查找链与优先级对于 guest/host 名解析findInternalIpsdns.go按如下优先级依次尝试guest 表getGuestIpsWithName调用models.GuestManager.GetIpsInProjectWithName(projectId, name, wantOnlyExitfalse, IPv4/IPv6)注意这里传入了来源项目projectId——即按来源项目作用域查找 guest 的 IPv4/IPv6 地址分别对应 A/AAAA 查询见 dns.gohost 表getHostIpWithName按名字查 host 并返回其AccessIp仅 A 记录k8s service 后端源码中该段实现getK8sServiceBackends按 svc 的 label selector 选出 Pod IP当前被整体注释停用dns.go从源码结构看属于保留的集成点。所有内部应答统一使用defaultTTL 300dns.go。2.4 自定义 DNS 记录dnsrecords的查询实现queryLocalDnsRecordsdns.go最终落到models.DnsRecordManager.QueryDns(projId, name, type)pkg/compute/models/dnsrecords.go。从该实现可以看到两个值得注意的细节作用域过滤zone 必须启用且manager_id为空Region 级 zone并且仅匹配is_public的 zone 或属于来源项目tenant_id的 zone泛域名支持查询条件显式包含dns_name name与dns_name *. name两种匹配即支持*.example.com形式的泛记录解析dns_name由记录名与 zone 名在 SQL 层CONCAT拼接而成。此外SRV 记录的dns_value被解析为priority weight port host四段空格分隔的文本任何一段格式非法都会返回错误日志并放弃该记录dns.go。记录 TTL 为 0 时回落到defaultTTL。3. 行为约定PTR反向解析的三级查找README 的 PTR 部分给出了反向解析的验收脚本names names$names #ok, guest ip names$names #ok, host ip names$names #ok, ptr records in db names$names #NXDOMAIN, others for name in $names; do echo ############### $name dig -p 54 192.168.222.171 -x $name done即guest IP、host IP、数据库中的 PTR 记录三类应能解析其余返回 NXDOMAIN。其实现位于 reverse.goReverse()先用dnsutil.ExtractAddressFromReverse从in-addr.arpa名中提取出 IP提取失败则退回正向Records()逻辑。成功提取后进入getNameForIp按 README 约定的顺序做三级查找reverse.go本地 DNS 记录表models.DnsRecordManager.QueryPtr(projectId, ip)pkg/compute/models/dnsrecords.go。该 SQL 按来源项目过滤 zone公有 zone 或本租户 zone按 IP 是 v4/v6 分别限定dns_type为A/AAAA取dns_value ip的记录应答名为dns_name . zone.name的拼接结果guest 表models.GuestnetworkManager.GetGuestByAddress(ip, projectId)命中则应答guest.Hostname与PrimaryZone的拼接joinDomainhost 表models.HostnetworkManager.GetHostByAddress(ip)命中则应答host.Name . PrimaryZone均未命中返回errNotFound最终呈现为 NXDOMAIN——对应 README 中的 “NXDOMAIN, others”。注意第 2、3 步的TTL均为defaultTTL300 秒而数据库 PTR 记录沿用记录自身的TTL字段。4. 其他查询类型的处理细节NS仅当查询名恰为 zone 本身时由Services()返回 zone 的 NS 记录当前nsAddr()返回占位实现见 ns.gons.dns.zone这类默认 NS 名也走同样的 fake 记录路径TXTServices()对单 label 的dns-version查询固定返回文本0.0.1、TTL28800可用于客户端探测本插件版本其他 TXT 走数据库记录查询不支持的类型ServeDNSdns.go对映射表外的 QTYPE 会故意做一次 fake A 查询用其结果区分应答 NXDOMAIN 还是 NODATA错误映射errNotFound→RcodeNameErrorNXDOMAINerrRefused→RcodeRefusederrCallNext→ 若fallthrough覆盖该 zone 则交给下一个插件否则 NXDOMAINdns.go。5. Corefile 配置yunion插件支持的指令setup()setup.go解析的 Corefile 指令如下表指令是否需参数作用zone 位置参数是yunion zone...指定的 zone省略时回退为 Corefile 的 ServerBlockKeysdns_domain是主域名但启动时会被 region 服务的dns_domain配置覆盖见下文sql_connection是MySQL 连接串插件直接查询 guest/host/dns records 表auth_url是Keystone 认证地址admin_user/admin_password/admin_domain是管理员凭据domain 缺省为默认 domainadmin_project/admin_project_domain是管理员项目及其 domainregion是当前 region 标识用于构造管理员 sessionin_cloud_only否出现即启用拒绝云外来源查询upstream可选上游 DNS 参数fallthrough可选允许查询继续流向后续插件的 zone 列表示例配置结合各指令参数自拟供参考hq.cloud.yunionyun.com { yunion { sql_connection root:123456tcp(127.0.0.1:3306)/cloudpods auth_url http://127.0.0.1:30153 admin_user cloudadmin admin_password 123456 admin_project admin region cn-north-1 in_cloud_only fallthrough } log class denial class error forward . /etc/resolv.conf }其中有两个关键机制需要注意dns_domain以 region 服务配置为准setup()会用管理员 session 调用identity.ServicesV3.GetConfig(session, apis.SERVICE_TYPE_REGION)拉取 region 服务的dns_domain配置项并覆盖本地值setup.go随后用regutils.MatchDomainName校验格式并规范化为带尾点的形式。也就是说 Corefile 里写不写dns_domain都不影响最终结果它以数据库中的 region 配置为唯一事实来源。compute 服务对应的选项为DNSDomainjson:dns_domain见 pkg/compute/options/options.govpcagent 也会从该配置读取域名pkg/vpcagent/service/service.go插件依赖数据库直连initDB会cloudcommon.InitDBConn并db.InitAllManagers()初始化全部模型管理器dns.go因此sql_connection必须指向 Cloudpods 主库。5.1log插件配置约定README 的“配置”一节给出了 CoreDNSlog插件的配置约定原文继承如下log { # note that apart from rcode like NXDOMAIN, SERVFAIL, coredns will also # log NOERROR response when its NoData as defined by coredns itself # # NoData indicates name found, but not the type: NOERROR in header, SOA in auth. # class denial class error }要点是log插件除了记录NXDOMAIN、SERVFAIL这类异常 rcode还会把 CoreDNS 定义的NoData头部为 NOERROR、但权威部分是 SOA即“名字存在但类型不存在”作为 error 类记录一并输出。配合class denial/class error运维人员可以在日志中同时看到“被拒绝的查询”和“解析失败的查询”这与第 2、3 节行为约定中大量的 NXDOMAIN 验收场景正好对应是验证解析行为的主要观测手段。6. 构建与运行构建region-dns是标准构建目标之一镜像打包脚本 scripts/docker_push.sh 中包含region-dns目标可直接make region-dns得到二进制运行启动region-dns -conf CorefileCorefile 中按第 5 节配置yunion指令README 中的验收脚本默认对192.168.222.171的54 端口执行dig说明部署时该服务可运行在非标准端口Corefile 中以zone:54形式声明即可可观测性main.go注册了prometheusmetrics、log、health、ready等标准 CoreDNS 插件可直接对接 Prometheus 采集。7. 小结Cloudpods 的 region-dns 以 CoreDNSyunion插件为核心将 guest 表、host 表与 DNS 记录表三大数据源统一到一个权威解析域中其解析行为在 pkg/dns/README.md 中被明确约定为可验收的场景集正向 A/AAAAPlainName 仅云内可解析CloudZoneFQDN 按 guest → host 优先级取 IP非本域名先查自定义记录表再 fallthrough 到公网转发反向 PTR数据库 PTR 记录 → guest → host 三级查找其余 NXDOMAIN安全边界in_cloud_only拒绝云外来源来源 IP 的项目归属通过 guest 网络与 OnPremise 网络两级判定配置来源zone 主域名以 region 服务的dns_domain配置为准插件通过sql_connection直连主库实时解析TTL 默认 300 秒。掌握以上内容后你可以依据 pkg/dns/README.md 中的dig验收脚本逐条验证部署后的解析行为并沿 pkg/dns/dns.go、pkg/dns/reverse.go、pkg/compute/models/dnsrecords.go 三个文件继续深入阅读具体实现。赞分享后端云原生运维虚拟化人工智能AI 应用【免费下载链接】cloudpods开源、云原生的多云管理及混合云融合平台项目地址https://gitcode.com/yunionio/cloudpods点击查看免费下载相关推荐Trigger.dev 插件系统解析基于 trigger.dev/plugins 的 RBAC 与 SSO 插件契约实现指南Trigger.dev 插件系统解析基于 trigger.dev/plugins 的 RBAC 与 SSO 插件契约实现指南 本文基于开源仓库 packagAI Agent后端任务调度开发工具可观测性AI 应用Micronaut 构建插件体系深度解析micronaut-build 插件注册规则、行为契约与维护者实战指南Micronaut 构建插件体系深度解析micronaut build 插件注册规则、行为契约与维护者实战指南 导读 本篇文章面向 Micronaut Fra后端微服务Web框架Vite-PWA插件中的Service Worker策略与行为解析Vite PWA插件中的Service Worker策略与行为解析 前言 在现代Web应用开发中渐进式Web应用 PWA 已经成为提升用户体验的重要手段。作为前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。