- 后端
- 网络
- 云原生
【免费下载链接】coredns
CoreDNS is a DNS server that chains plugins
CoreDNS 1.5.0(发布于 2019 年 4 月)是一次以「新能力落地 + 老能力收口」为双主线的版本迭代:一方面引入了两个全新的插件 —— 用于通过 gRPC 协议转发 DNS 报文的grpc与用于就绪探测的ready;另一方面对核心服务端代码做了精简,并正式弃用了file/auto插件中的TIMEOUT、no_reload指令以及proxy插件。读完本文,你将掌握这两个新插件的完整配置语法与底层实现原理,理解弃用清单的迁移路径,并了解 kubernetes 插件resyncperiod的默认值变更及两个关键活跃 bug 的定位背景。
版本概览:一次「加新、收旧、简化」的发布
本次发布的核心变化可以用三句话概括(详见 版本发布说明):
- 新增两个插件:grpc负责将 DNS 查询通过 gRPC 协议转发给上游解析器;ready提供就绪状态检查的 HTTP 端点,第一个使用者是kubernetes插件。
- 核心服务端代码打磨:dnsserver 部分做了「polish and simplifications」,即面向可维护性的清理与简化。
- 弃用动作收口:file与auto插件中的
TIMEOUT、no_reload指令被完全弃用,proxy插件同步进入弃用状态,与其承载的 gRPC 转发能力一并由新插件接管。
此外,版本说明同步更新了两个重要且活跃的 bug:
- Issue 2593:问题线索逐渐收敛到 Docker / 容器环境可能是诱发因素之一;
- Issue 2624:根因定位在forward插件中的 TLS 会话协商环节。
新插件一:grpc —— 用 gRPC 协议转发 DNS 报文
功能定位
grpc插件的核心职责是「facilitates proxying DNS messages to upstream resolvers via gRPC protocol」,即把 DNS 查询封装进 gRPC 通道转发给上游解析器。它同时支持 gRPC 与 TLS,并且每个 Server Block 中只能使用一次(从 setup.go 中plugin.ErrOnce的检查逻辑可以印证:parseGRPC中第二次进入for c.Next()就会直接报错)。
基础语法
grpc FROM TO...- FROM:请求要匹配的基域(base domain),匹配成功才会被代理转发;
- TO...:目标上游端点列表,数量上限为 15 个—— 该限制直接定义在 setup.go 的
const max = 15中,setup()会在启动解析阶段检查g.len() > max并拒绝启动。
多个上游首次使用时按policy策略随机排序;当某个上游返回错误时,会依次尝试列表中的下一个上游。
扩展语法与参数详解
grpc FROM TO... { except IGNORED_NAMES... tls CERT KEY CA tls_servername NAME policy random|round_robin|sequential fallthrough [ZONES...] }- except IGNORED_NAMES...:空格分隔的域名列表,这些域名的查询不会被转发。从 grpc.go 的
isAllowedDomain实现可见:请求名等于from域时直接放行,否则逐个检查ignored列表,命中任一忽略项即拒绝转发。 - tls CERT KEY CA:定义 TLS 连接的属性,0~3 个参数,含义如下表:
| 参数个数 | 写法 | 行为 |
|---|---|---|
| 0 | tls | 不进行客户端认证,使用系统 CA 验证服务器证书 |
| 1 | tls CA | 不进行客户端认证,使用指定的 CA 文件验证服务器证书 |
| 2 | tls CERT KEY | 使用指定的 cert/key 对进行客户端认证,服务器证书用系统 CA 验证 |
| 3 | tls CERT KEY CA | 使用指定的 cert/key 对进行客户端认证,服务器证书用指定的 CA 文件验证 |
在配置解析阶段,非绝对路径的证书参数会被拼接到dnsserver.GetConfig(c).Root之下,再经由pkgtls.NewTLSConfigFromArgs生成tls.Config(见 setup.go)。
- tls_servername NAME:在 TLS 配置中设置 SNI 服务器名。典型场景是 Quad9(9.9.9.9)必须设置为
dns.quad9.net;Cloudflare(1.1.1.1)需设置为cloudflare-dns.com。注意:多个上游必须使用同一个tls_servername,混用不同厂商(如同时配置 9.9.9.9 和 1.1.1.1)不会生效。代码中该值会被写入g.tlsConfig.ServerName(见 setup.go)。 - policy:上游选择策略,默认
random。可选值random、round_robin、sequential,未知值会在启动时报错(见 setup.go)。 - fallthrough [ZONES...]:当 gRPC 后端返回 NXDOMAIN 时,将请求交给下一个插件而不是直接返回 NXDOMAIN 响应。适用于「gRPC 后端虽然权威托管某区域,但不应为不属于该区域的查询返回权威 NXDOMAIN」的场景(例如搜索路径查询)。不写
[ZONES...]时对所有区域生效;写了具体区域则仅对该区域生效。
关于 TLS 的一个约束:TLS 配置对整条 grpc 代理是「全局」的,如果需要为不同上游设置不同的tls_servername,当前版本无能为力。
转发流程与错误处理(源码级)
grpc.go 中ServeDNS的完整调用链值得关注:
- 先做域名匹配:
match()要求请求名匹配from且不在except列表内;不匹配则交给Next插件链,无下一个插件时返回 SERVFAIL。 - 取上游列表
g.list(),设置总截止时间deadline = now + defaultTimeout(defaultTimeout = 5 * time.Second,见 grpc.go)。 - 循环尝试每个上游,每次调用
proxy.query(callCtx, r)前通过context.WithDeadline为单次调用设置截止时间。 - 单个上游出错则
continue尝试下一个;若所有上游都失败,最终返回 SERVFAIL 并携带ErrNoHealthy("no healthy gRPC proxies")。 - 若返回报文与请求不匹配(
!state.Match(ret)),直接向客户端写回 FORMERR。 - 若返回 NXDOMAIN 且
g.Fall.Through(state.Name())命中,则交给下一个插件处理(fallthrough 逻辑在错误路径和正常路径均有处理)。 - 正常响应直接
w.WriteMsg(ret)返回。
完整配置示例
将example.org.内的所有请求转发到本机 9005 端口的命名服务器:
example.org { grpc . 127.0.0.1:9005 }在三个解析器之间做负载均衡(其中一个为 IPv6 地址):
. { grpc . 10.0.0.10:53 10.0.0.11:1053 [2003::1]:53 }除example.org外全部转发:
. { grpc . 10.0.0.10:1234 { except example.org } }使用宿主resolv.conf的 nameserver 转发(except排除example.org):
. { grpc . /etc/resolv.conf { except example.org } }通过 TLS 协议转发到 9.9.9.9 并缓存 30 秒。注意tls_servername是必须项 —— 9.9.9.9 无法直接用于 TLS 协商:
. { grpc . 9.9.9.9 { tls_servername dns.quad9.net } cache 30 }同一厂商的多个上游:
. { grpc . 1.1.1.1 1.0.0.1 { tls_servername cloudflare-dns.com } cache 30 }转发到监听在 Unix 域套接字上的本地上游:
. { grpc . unix:///path/to/grpc.sock }example.org.的 gRPC 后端返回 NXDOMAIN 时回退到forward插件,以正确处理搜索路径查询:
example.org { grpc . 127.0.0.1:9005 { fallthrough } forward . 8.8.8.8 }监控指标
配合prometheus(即 metrics)插件启用监控后,grpc 导出如下指标(定义见 metrics.go):
coredns_grpc_request_duration_seconds{to}—— 每次上游交互的耗时;coredns_grpc_requests_total{to}—— 每个上游的查询计数;coredns_grpc_responses_total{to, rcode}—— 每个上游按 RCODE 统计的响应计数(指标上报使用random策略随机选择上游)。
新插件二:ready —— 插件级就绪状态探测
功能定位
ready插件暴露一个 HTTP 就绪检查端点:当所有能够上报就绪状态的插件都就绪后,端点返回200 OK;只要还有插件未就绪,端点返回503,且响应体中会列出尚未就绪的插件名。第一个接入方是kubernetes插件。源码层面,ready.go 中/ready的 handler 会调用plugins.Ready(),就绪时写200 OK,否则写 503 与未就绪插件列表。
语法与行为
ready [ADDRESS] { monitor until-ready|continuously }- ADDRESS:可选,默认
:8181;路径固定为/ready。 - monitor:可选,默认
until-ready:until-ready:插件上报一次就绪后不再重复检查,假定初始就绪确认之后状态保持稳定;continuously:持续监控插件就绪状态,插件可在 ready / not ready 之间动态切换,提供实时状态更新。
默认行为下,插件上报一次就绪后便不再被询问。
工作机制(源码级)
- 每个启用了ready插件的 Server Block,会把该 Server Block 内的插件就绪状态上报到同一端口上的
/ready端点。因此,同一个插件在不同 Server Block 中以不同配置出现时,其就绪状态取各自就绪状态的「并集」。 - 就绪检查的端口监听复用
reuseport.Listen,HTTP 服务的读写与空闲超时均为 5 秒,关闭时也有 5 秒的优雅退出窗口(见 ready.go)。 - 插件接入方式:任何希望上报就绪状态的插件只需实现
ready.Readiness接口,即实现一个Ready() bool方法(见 readiness.go),返回true表示就绪。
配置示例
为.与example.org两个服务器同时上报就绪状态(假定erratic、whoami也导出就绪能力):
. { ready erratic } example.org { ready whoami }在自定义端口运行就绪检查:
. { ready localhost:8091 }新插件三:cancel —— 请求级超时取消
cancel插件为每个请求创建一个可取消的 context,并在5001 毫秒后触发超时。这个「5001」是刻意选定的:DNS 客户端的默认超时是 5 秒,超过 5 秒客户端通常已经放弃等待(详见 cancel 插件说明)。
cancel [TIMEOUT]- TIMEOUT:自定义超时,默认 5001 毫秒(
5001 ms)。
插件侧配合:对取消状态感兴趣的插件应调用plugin.Done()检查 context;若 context 因超时被取消,该插件不应再向客户端写任何数据,并返回让 CoreDNS 也停止响应的值(返回零值即可)。
版本说明特别注明:该插件尚未默认启用("but not enabled - yet")。
example.org { cancel whoami }自定义超时写法:
example.org { cancel 1s whoami }其余插件增强一览
1.5.0 中还有一批既有插件的功能增强:
- forward:新增 dnstap 支持(相关实现见 forward/dnstap.go),为转发流量提供 dnstap 观测通道;
- route53:开始支持通配符(wildcard)记录;
- pprof:新增
block选项,开启 block profiling; - prometheus(metrics):新增
coredns_plugin_enabled指标,用于展示每个 server、zone、view 维度上启用了哪些插件。该指标的定义位于 metrics/vars/vars.go,在 metrics/setup.go 中为每个注册为 Handler 的插件写入1,并在重启时重置。注意:只有注册为 Handler 的插件才会出现在该指标中,reload、bind两个插件目前不会被上报; - chaos:当查询
authors.bind TXT CH时,返回维护者(maintainers)名单; - kubernetes:
resyncperiod选项默认值改为0 秒,即默认禁用重新同步。源码中object/informer.go的defaultResyncPeriod = 0(见 plugin/kubernetes/object/informer.go),FullResyncPeriod随之取该默认值。
弃用清单与迁移路径
本次发布明确了两条弃用线:
- file / auto 插件:
TIMEOUT与no_reload指令已被完全弃用(fully deprecated),不再作为受支持的配置项; - proxy 插件:整个插件进入弃用状态。若此前依赖proxy中的 gRPC 转发能力,可平滑迁移到 1.5.0 新增的grpc插件。
此外,kubernetes插件的resyncperiod选项同样被标记为弃用:计划在1.6.0中变为 no-op(无效配置),并在1.7.0中彻底移除。结合默认值已改为 0 秒的事实,可以理解为该机制正在逐步退出历史舞台,使用者应尽快从 Corefile 中移除该配置。
版本说明中的关键工程信息
最后补充两点发布说明中值得注意的工程背景:
- 核心服务端代码:dnsserver 部分进行了打磨与简化,属于面向长期可维护性的重构,不改变既有配置语义;
- 两个活跃 bug 的进展:
- Issue 2593 的线索集中在 Docker / 容器环境,怀疑其为触发因素之一;
- Issue 2624 根因指向forward插件中的 TLS 会话协商(TLS session negotiating)环节。
这两个问题在 1.5.0 发布时仍在跟进中,属于对已知问题的状态同步,而非本次版本已修复的功能点。
小结
CoreDNS 1.5.0 的核心价值在于:用grpc插件接棒了proxy中的 gRPC 转发能力(上限 15 个上游、四种 TLS 参数形态、三种选择策略、NXDOMAIN fallthrough 一应俱全);用ready插件为 k8s 生态提供了基于 HTTP 的就绪探测标准接口;同时通过弃用TIMEOUT、no_reload、proxy与resyncperiod,为后续版本的功能收敛铺平了道路。对于需要 gRPC 上游或就绪检查的用户,这份版本的语法与源码实现(plugin/grpc、plugin/ready、plugin/cancel)可以直接作为落地与二次开发的手册。
- 后端
- 网络
- 云原生
【免费下载链接】coredns
CoreDNS is a DNS server that chains plugins
相关推荐
CoreDNS 1.1.4 版本发布详解:插件弃用策略、核心优化与 Docker 镜像多阶段构建
CoreDNS 1.1.4 版本发布详解:插件弃用策略、核心优化与 Docker 镜像多阶段构建 CoreDNS 1.1.4 是 CoreDNS 在 2018
后端网络云原生CoreDNS v008 版本解析:日志体系重构、hosts/debug 新插件与插件链核心更新
CoreDNS v008 版本解析:日志体系重构、hosts/debug 新插件与插件链核心更新 CoreDNS 008(v008)是 CoreDNS 早期发布
后端网络云原生CoreDNS 1.0.5 版本解析:route53 新插件、health lameduck 与核心修复全景
CoreDNS 1.0.5 版本解析:route53 新插件、health lameduck 与核心修复全景 CoreDNS 1.0.5 是 CoreDNS 早
后端网络云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考