dns服务器系统架构,详解 DNS 与 CoreDNS 的实现原理

原文链接:https://draveness.me/dns-coredns

【编者的话】域名系统(Domain Name System)是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解 IP 地址,使得互联网的使用者不再需要直接接触很难阅读和理解的 IP 地址。

我们在这篇文章中的第一部分会介绍 DNS 的工作原理以及一些常见的 DNS 问题,而第二部分我们会介绍 DNS 服务 CoreDNS 的架构和实现原理。

DNS域名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,如果没有了 DNS,那么可能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,如果我们为 IP 地址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,那么我们就可以保证集群对外提供的服务能够相对稳定地被其他客户端访问。

24cdf164ddd0c76d4b0b3a3cc69746cf.png

DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化的分布式数据库,存储着从域名到 IP 地址的映射。

工作原理在我们对 DNS 有了简单的了解之后,接下来我们就可以进入 DNS 工作原理的部分了,作为用户访问互联网的第一站,当一台主机想要通过域名访问某个服务的内容时,需要先通过当前域名获取对应的 IP 地址。这时就需要通过一个 DNS 解析器负责域名的解析,下面的图片展示了 DNS 查询的执行过程:

15dfe73de4f1a60a4494272e0ec8dc00.png

本地的 DNS 客户端向 DNS 解析器发出解析 draveness.me 域名的请求;

DNS 解析器首先会向就近的根 DNS 服务器 . 请求顶级域名 DNS 服务的地址;

拿到顶级域名 DNS 服务 me. 的地址之后会向顶级域名服务请求负责 dravenss.me. 域名解析的命名服务;

得到授权的 DNS 命名服务时,就可以根据请求的具体的主机记录直接向该服务请求域名对应的 IP 地址;

DNS 客户端接受到 IP 地址之后,整个 DNS 解析的过程就结束了,客户端接下来就会通过当前的 IP 地址直接向服务器发送请求。

对于 DNS 解析器,这里使用的 DNS 查询方式是迭代查询,每个 DNS 服务并不会直接返回 DNS 信息,而是会返回另一台 DNS 服务器的位置,由客户端依次询问不同级别的 DNS 服务直到查询得到了预期的结果;另一种查询方式叫做递归查询,也就是 DNS 服务器收到客户端的请求之后会直接返回准确的结果,如果当前服务器没有存储 DNS 信息,就会访问其他的服务器并将结果返回给客户端。

域名层级域名层级是一个层级的树形结构,树的最顶层是根域名,一般使用.来表示,这篇文章所在的域名一般写作 draveness.me,但是这里的写法其实省略了最后的.,也就是全称域名(FQDN)dravenss.me.。

e72bf8290395de64c786dea4966e49f9.png

根域名下面的就是 com、net 和 me 等顶级域名以及次级域名 draveness.me,我们一般在各个域名网站中购买和使用的都是次级域名、子域名和主机名了。

域名服务器既然域名的命名空间是树形的,那么用于处理域名解析的 DNS 服务器也是树形的,只是在树的组织和每一层的职责上有一些不同。DNS 解析器从根域名服务器查找到顶级域名服务器的 IP 地址,又从顶级域名服务器查找到权威域名服务器的 IP 地址,最终从权威域名服务器查出了对应服务的 IP 地址。

$ dig -t A draveness.me +trace

我们可以使用 dig 命令追踪 draveness.me 域名对应 IP 地址是如何被解析出来的,首先会向预置的 13 组根域名服务器发出请求获取顶级域名的地址:

.            56335   IN  NS  m.root-servers.net.

.           56335   IN  NS  b.root-servers.net.

.           56335   IN  NS  c.root-servers.net.

.           56335   IN  NS  d.root-servers.net.

.           56335   IN  NS  e.root-servers.net.

.           56335   IN  NS  f.root-servers.net.

.           56335   IN  NS  g.root-servers.net.

.           56335   IN  NS  h.root-servers.net.

.           56335   IN  NS  i.root-servers.net.

.           56335   IN  NS  a.root-servers.net.

.           56335   IN  NS  j.root-servers.net.

.           56335   IN  NS  k.root-servers.net.

.           56335   IN  NS  l.root-servers.net.

.           56335   IN  RRSIG   NS 8 0 518400 20181111050000 20181029040000 2134 . G4NbgLqsAyin2zZFetV6YhBVVI29Xi3kwikHSSmrgkX+lq3sRgp3UuQ3 JQxpJ+bZY7mwzo3NxZWy4pqdJDJ55s92l+SKRt/ruBv2BCnk9CcnIzK+ OuGheC9/Coz/r/33rpV63CzssMTIAAMQBGHUyFvRSkiKJWFVOps7u3TM jcQR0Xp+rJSPxA7f4+tDPYohruYm0nVXGdWhO1CSadXPvmWs1xeeIKvb 9sXJ5hReLw6Vs6ZVomq4tbPrN1zycAbZ2tn/RxGSCHMNIeIROQ99kO5N QL9XgjIJGmNVDDYi4OF1+ki48UyYkFocEZnaUAor0pD3Dtpis37MASBQ fr6zqQ==

;; Received 525 bytes from 8.8.8.8#53(8.8.8.8) in 247 ms

根域名服务器是 DNS 中最高级别的域名服务器,这些服务器负责返回顶级域的权威域名服务器地址,这些域名服务器的数量总共有 13 组,域名的格式从上面返回的结果可以看到是 .root-servers.net,每个根域名服务器中只存储了顶级域服务器的 IP 地址,大小其实也只有 2MB 左右,虽然域名服务器总共只有 13 组,但是每一组服务器都通过提供了镜像服务,全球大概也有几百台的根域名服务器在运行。在这里,我们获取到了以下的 5 条 NS 记录,也就是 5 台 me. 定义域名 DNS 服务器:

me.          172800  IN  NS  b0.nic.me.

me.         172800  IN  NS  a2.nic.me.

me.         172800  IN  NS  b2.nic.me.

me.         172800  IN  NS  a0.nic.me.

me.         172800  IN  NS  c0.nic.me.

me.         86400   IN  DS  2569 7 1 09BA1EB4D20402620881FD9848994417800DB26A

me.         86400   IN  DS  2569 7 2 94E798106F033500E67567B197AE9132C0E916764DC743C55A9ECA3C 7BF559E2

me.         86400   IN  RRSIG   DS 8 1 86400 20181113050000 20181031040000 2134 . O81bud61Qh+kJJ26XHzUOtKWRPN0GHoVDacDZ+pIvvD6ef0+HQpyT5nV rhEZXaFwf0YFo08PUzX8g5Pad8bpFj0O//Q5H2awGbjeoJnlMqbwp6Kl 7O9zzp1YCKmB+ARQgEb7koSCogC9pU7E8Kw/o0NnTKzVFmLq0LLQJGGE Y43ay3Ew6hzpG69lP8dmBHot3TbF8oFrlUzrm5nojE8W5QVTk1QQfrZM 90WBjfe5nm9b4BHLT48unpK3BaqUFPjqYQV19C3xJ32at4OwUyxZuQsa GWl0w9R5TiCTS5Ieupu+Q9fLZbW5ZMEgVSt8tNKtjYafBKsFox3cSJRn irGOmg==

;; Received 721 bytes from 192.36.148.17#53(i.root-servers.net) in 59 ms

当 DNS 解析器从根域名服务器中查询到了顶级域名 .me 服务器的地址之后,就可以访问这些顶级域名服务器其中的一台 b2.nic.me 获取权威 DNS 的服务器的地址了:

draveness.me.        86400   IN  NS  f1g1ns1.dnspod.net.

draveness.me.       86400   IN  NS  f1g1ns2.dnspod.net.

fsip6fkr2u8cf2kkg7scot4glihao6s1.me. 8400 IN NSEC3 1 1 1 D399EAAB FSJJ1I3A2LHPTHN80MA6Q7J64B15AO5K  NS SOA RRSIG DNSKEY NSEC3PARAM

fsip6fkr2u8cf2kkg7scot4glihao6s1.me. 8400 IN RRSIG NSEC3 7 2 8400 20181121151954 20181031141954 2208 me. eac6+fEuQ6gK70KExV0EdUKnWeqPrzjqGiplqMDPNRpIRD1vkpX7Zd6C oN+c8b2yLoI3s3oLEoUd0bUi3dhyCrxF5n6Ap+sKtEv4zZ7o7CEz5Fw+ fpXHj7VeL+pI8KffXcgtYQGlPlCM/ylGUGYOcExrB/qPQ6f/62xrPWjb +r4=

qcolpi5mj0866sefv2jgp4jnbtfrehej.me. 8400 IN NSEC3 1 1 1 D399EAAB QD4QM6388QN4UMH78D429R72J1NR0U07  NS DS RRSIG

qcolpi5mj0866sefv2jgp4jnbtfrehej.me. 8400 IN RRSIG NSEC3 7 2 8400 20181115151844 20181025141844 2208 me. rPGaTz/LyNRVN3LQL3LO1udby0vy/MhuIvSjNfrNnLaKARsbQwpq2pA9 +jyt4ah8fvxRkGg9aciG1XSt/EVIgdLSKXqE82hB49ZgYDACX6onscgz naQGaCAbUTSGG385MuyxCGvqJdE9kEZBbCG8iZhcxSuvBksG4msWuo3k dTg=

;; Received 586 bytes from 199.249.127.1#53(b2.nic.me) in 267 ms

这里的权威 DNS 服务是作者在域名提供商进行配置的,当有客户端请求 draveness.me 域名对应的 IP 地址时,其实会从作者使用的 DNS 服务商 DNSPod 处请求服务的 IP 地址:

draveness.me.        600 IN  A   123.56.94.228

draveness.me.       86400   IN  NS  f1g1ns2.dnspod.net.

draveness.me.       86400   IN  NS  f1g1ns1.dnspod.net.

;; Received 123 bytes from 58.247.212.36#53(f1g1ns1.dnspod.net) in 28 ms

最终,DNS 解析器从 f1g1ns1.dnspod.net 服务中获取了当前博客的 IP 地址 123.56.94.228,浏览器或者其他设备就能够通过 IP 向服务器获取请求的内容了。

从整个解析过程,我们可以看出 DNS 域名服务器大体分成三类,根域名服务、顶级域名服务以及权威域名服务三种,获取域名对应的 IP 地址时,也会像遍历一棵树一样按照从顶层到底层的顺序依次请求不同的服务器。

胶水记录在通过服务器解析域名的过程中,我们看到当请求 me. 顶级域名服务器的时候,其实返回了 b0.nic.me 等域名:

me.          172800  IN  NS  b0.nic.me.

me.         172800  IN  NS  a2.nic.me.

me.         172800  IN  NS  b2.nic.me.

me.         172800  IN  NS  a0.nic.me.

me.         172800  IN  NS  c0.nic.me.

...

就像我们最开始说的,在互联网中想要请求服务,最终一定需要获取 IP 提供服务的服务器的 IP 地址;同理,作为 b0.nic.me 作为一个 DNS 服务器,我也必须获取它的 IP 地址才能获得次级域名的 DNS 信息,但是这里就陷入了一种循环:

如果想要获取 dravenss.me 的 IP 地址,就需要访问 me 顶级域名服务器 b0.nic.me

如果想要获取 b0.nic.me 的 IP 地址,就需要访问 me 顶级域名服务器 b0.nic.me

如果想要获取 b0.nic.me 的 IP 地址,就需要访问 me 顶级域名服务器 b0.nic.me

……

为了解决这一个问题,我们引入了胶水记录(Glue Record)这一概念,也就是在出现循环依赖时,直接在上一级作用域返回 DNS 服务器的 IP 地址:

$ dig +trace +additional draveness.me

...

me.         172800  IN  NS  a2.nic.me.

me.         172800  IN  NS  b2.nic.me.

me.         172800  IN  NS  b0.nic.me.

me.         172800  IN  NS  a0.nic.me.

me.         172800  IN  NS  c0.nic.me.

me.         86400   IN  DS  2569 7 1 09BA1EB4D20402620881FD9848994417800DB26A

me.         86400   IN  DS  2569 7 2 94E798106F033500E67567B197AE9132C0E916764DC743C55A9ECA3C 7BF559E2

me.         86400   IN  RRSIG   DS 8 1 86400 20181116050000 20181103040000 2134 . cT+rcDNiYD9X02M/NoSBombU2ZqW/7WnEi+b/TOPcO7cDbjb923LltFb ugMIaoU0Yj6k0Ydg++DrQOy6E5eeshughcH/6rYEbVlFcsIkCdbd9gOk QkOMH+luvDjCRdZ4L3MrdXZe5PJ5Y45C54V/0XUEdfVKel+NnAdJ1gLE F+aW8LKnVZpEN/Zu88alOBt9+FPAFfCRV9uQ7UmGwGEMU/WXITheRi5L h8VtV9w82E6Jh9DenhVFe2g82BYu9MvEbLZr3MKII9pxgyUE3pt50wGY Mhs40REB0v4pMsEU/KHePsgAfeS/mFSXkiPYPqz2fgke6OHFuwq7MgJk l7RruQ==

a0.nic.me.      172800  IN  A   199.253.59.1

a2.nic.me.      172800  IN  A   199.249.119.1

b0.nic.me.      172800  IN  A   199.253.60.1

b2.nic.me.      172800  IN  A   199.249.127.1

c0.nic.me.      172800  IN  A   199.253.61.1

a0.nic.me.      172800  IN  AAAA    2001:500:53::1

a2.nic.me.      172800  IN  AAAA    2001:500:47::1

b0.nic.me.      172800  IN  AAAA    2001:500:54::1

b2.nic.me.      172800  IN  AAAA    2001:500:4f::1

c0.nic.me.      172800  IN  AAAA    2001:500:55::1

;; Received 721 bytes from 192.112.36.4#53(g.root-servers.net) in 110 ms

...

也就是同时返回 NS 记录和 A(或 AAAA) 记录,这样就能够解决域名解析出现的循环依赖问题。

服务发现讲到现在,我们其实能够发现 DNS 就是一种最早的服务发现的手段,通过虽然服务器的 IP 地址可能会经常变动,但是通过相对不会变动的域名,我们总是可以找到提供对应服务的服务器。

在微服务架构中,服务注册的方式其实大体上也只有两种,一种是使用 ZooKeeper 和 etcd 等配置管理中心,另一种是使用 DNS 服务,比如说 Kubernetes 中的 CoreDNS 服务。

使用 DNS 在集群中做服务发现其实是一件比较容易的事情,这主要是因为绝大多数的计算机上都会安装 DNS 服务,所以这其实就是一种内置的、默认的服务发现方式,不过使用 DNS 做服务发现也会有一些问题,因为在默认情况下 DNS 记录的失效时间是 600s,这对于集群来讲其实并不是一个可以接受的时间,在实践中我们往往会启动单独的 DNS 服务满足服务发现的需求。

CoreDNSCoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目以及工程师都会使用 CoreDNS 为集群提供服务发现的功能,Kubernetes 就在集群中使用 CoreDNS 解决服务发现的问题。

18916cc9e40a31a53738a0c4864dd6a2.png

作为一个加入 CNCF(Cloud Native Computing Foundation)的服务 CoreDNS 的实现可以说的非常的简单。

架构整个 CoreDNS 服务都建立在一个使用 Go 编写的 HTTP/2 Web 服务器 Caddy · GitHub 上,CoreDNS 整个项目可以作为一个 Caddy 的教科书用法。

f077823b45d42e6e2f4c83b368dfd337.png

CoreDNS 的大多数功能都是由插件来实现的,插件和服务本身都使用了 Caddy 提供的一些功能,所以项目本身也不是特别的复杂。

插件作为基于 Caddy 的 Web 服务器,CoreDNS 实现了一个插件链的架构,将很多 DNS 相关的逻辑都抽象层了一层一层的插件,包括 Kubernetes 等功能,每一个插件都是一个遵循如下协议的结构体:

type (

Plugin func(Handler) Handler

Handler interface {

ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)

Name() string

}

)

所以只需要为插件实现 ServeDNS 以及 Name 这两个接口并且写一些用于配置的代码就可以将插件集成到 CoreDNS 中。

Corefile另一个 CoreDNS 的特点就是它能够通过简单易懂的 DSL 定义 DNS 服务,在 Corefile 中就可以组合多个插件对外提供服务:

coredns.io:5300 {

file db.coredns.io

}

example.io:53 {

log

errors

file db.example.io

}

example.net:53 {

file db.example.net

}

.:53 {

kubernetes

proxy . 8.8.8.8

log

errors

cache

}

对于以上的配置文件,CoreDNS 会根据每一个代码块前面的区和端点对外暴露两个端点提供服务:

b48ecaaea013506c6374f4823c062b0c.png

该配置文件对外暴露了两个 DNS 服务,其中一个监听在 5300 端口,另一个在 53 端口,请求这两个服务时会根据不同的域名选择不同区中的插件进行处理。

原理CoreDNS 可以通过四种方式对外直接提供 DNS 服务,分别是 UDP、gRPC、HTTPS 和 TLS:

c0ce482326f3bca890545fc7dd48a479.png

但是无论哪种类型的 DNS 服务,最终队会调用以下的 ServeDNS 方法,为服务的调用者提供 DNS 服务:

func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {

m, _ := edns.Version(r)

ctx, _ := incrementDepthAndCheck(ctx)

b := r.Question[0].Name

var off int

var end bool

var dshandler *Config

w = request.NewScrubWriter(r, w)

for {

if h, ok := s.zones[string(b[:l])]; ok {

ctx = context.WithValue(ctx, plugin.ServerCtx{}, s.Addr)

if r.Question[0].Qtype != dns.TypeDS {

rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)

dshandler = h

}

off, end = dns.NextLabel(q, off)

if end {

break

}

}

if r.Question[0].Qtype == dns.TypeDS && dshandler != nil && dshandler.pluginChain != nil {

rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)

plugin.ClientWrite(rcode)

return

}

if h, ok := s.zones["."]; ok && h.pluginChain != nil {

ctx = context.WithValue(ctx, plugin.ServerCtx{}, s.Addr)

rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)

plugin.ClientWrite(rcode)

return

}

}

在上述这个已经被简化的复杂函数中,最重要的就是调用了『插件链』的 ServeDNS 方法,将来源的请求交给一系列插件进行处理,如果我们使用以下的文件作为 Corefile:

example.org {

file /usr/local/etc/coredns/example.org

prometheus     # enable metrics

errors         # show errors

log            # enable query logs

}

那么在 CoreDNS 服务启动时,对于当前的 example.org 这个组,它会依次加载 file、log、errors 和 prometheus 几个插件,这里的顺序是由 zdirectives.go 文件定义的,启动的顺序是从下到上:

var Directives = []string{

// ...

"prometheus",

"errors",

"log",

// ...

"file",

// ...

"whoami",

"on",

}

因为启动的时候会按照从下到上的顺序依次『包装』每一个插件,所以在真正调用时就是从上到下执行的,这就是因为 NewServer 方法中对插件进行了组合:

func NewServer(addr string, group []*Config) (*Server, error) {

s := &Server{

Addr:        addr,

zones:       make(map[string]*Config),

connTimeout: 5 * time.Second,

}

for _, site := range group {

s.zones[site.Zone] = site

if site.registry != nil {

for name := range enableChaos {

if _, ok := site.registry[name]; ok {

s.classChaos = true

break

}

}

}

var stack plugin.Handler

for i := len(site.Plugin) - 1; i >= 0; i-- {

stack = site.Plugin[i](stack)

site.registerHandler(stack)

}

site.pluginChain = stack

}

return s, nil

}

对于 Corefile 里面的每一个配置组,NewServer 都会讲配置组中提及的插件按照一定的顺序组合起来,原理跟 Rack Middleware 的机制非常相似,插件 Plugin 其实就是一个出入参数都是 Handler 的函数:

type (

Plugin func(Handler) Handler

Handler interface {

ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)

Name() string

}

)

所以我们可以将它们叠成堆栈的方式对它们进行操作,这样在最后就会形成一个插件的调用链,在每个插件执行方法时都可以通过 NextOrFailure 函数调用下一个插件的 ServerDNS 方法:

func NextOrFailure(name string, next Handler, ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {

if next != nil {

if span := ot.SpanFromContext(ctx); span != nil {

child := span.Tracer().StartSpan(next.Name(), ot.ChildOf(span.Context()))

defer child.Finish()

ctx = ot.ContextWithSpan(ctx, child)

}

return next.ServeDNS(ctx, w, r)

}

return dns.RcodeServerFailure, Error(name, errors.New("no next plugin found"))

}

除了通过 ServeDNS 调用下一个插件之外,我们也可以调用 WriteMsg 方法并结束整个调用链。

6e4574fc5c511a105d686b38656b8423.png

从插件的堆叠到顺序调用以及错误处理,我们对 CoreDNS 的工作原理已经非常清楚了,接下来我们可以简单介绍几个插件的作用。

LoadBalanceLoadBalance 这个插件的名字就告诉我们,使用这个插件能够提供基于 DNS 的负载均衡功能,在 setup 中初始化时传入了 RoundRobin 结构体:

func setup(c *caddy.Controller) error {

err := parse(c)

if err != nil {

return plugin.Error("loadbalance", err)

}

dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {

return RoundRobin{Next: next}

})

return nil

}

当用户请求 CoreDNS 服务时,我们会根据插件链调用 LoadBalance 这个包中的 ServeDNS 方法,在方法中会改变用于返回响应的 Writer:

func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {

wrr := &RoundRobinResponseWriter{w}

return plugin.NextOrFailure(rr.Name(), rr.Next, ctx, wrr, r)

}

所以在最终服务返回响应时,会通过 RoundRobinResponseWriter 的 WriteMsg 方法写入 DNS 消息:

func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error {

if res.Rcode != dns.RcodeSuccess {

return r.ResponseWriter.WriteMsg(res)

}

res.Answer = roundRobin(res.Answer)

res.Ns = roundRobin(res.Ns)

res.Extra = roundRobin(res.Extra)

return r.ResponseWriter.WriteMsg(res)

}

上述方法会将响应中的 Answer、Ns 以及 Extra 几个字段中数组的顺序打乱:

func roundRobin(in []dns.RR) []dns.RR {

cname := []dns.RR{}

address := []dns.RR{}

mx := []dns.RR{}

rest := []dns.RR{}

for _, r := range in {

switch r.Header().Rrtype {

case dns.TypeCNAME:

cname = append(cname, r)

case dns.TypeA, dns.TypeAAAA:

address = append(address, r)

case dns.TypeMX:

mx = append(mx, r)

default:

rest = append(rest, r)

}

}

roundRobinShuffle(address)

roundRobinShuffle(mx)

out := append(cname, rest...)

out = append(out, address...)

out = append(out, mx...)

return out

}

打乱后的 DNS 记录会被原始的 ResponseWriter 结构写回到 DNS 响应中。

LoopLoop 插件会检测 DNS 解析过程中出现的简单循环依赖,如果我们在 Corefile 中添加如下的内容并启动 CoreDNS 服务,CoreDNS 会向自己发送一个 DNS 查询,看最终是否会陷入循环:

. {

loop

forward . 127.0.0.1

}

在 CoreDNS 启动时,它会在 setup 方法中调用 Loop.exchange 方法向自己查询一个随机域名的 DNS 记录:

func (l *Loop) exchange(addr string) (*dns.Msg, error) {

m := new(dns.Msg)

m.SetQuestion(l.qname, dns.TypeHINFO)

return dns.Exchange(m, addr)

}

如果这个随机域名在 ServeDNS 方法中被查询了两次,那么就说明当前的 DNS 请求陷入了循环需要终止:

func (l *Loop) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {

if r.Question[0].Qtype != dns.TypeHINFO {

return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)

}

// ...

if state.Name() == l.qname {

l.inc()

}

if l.seen() > 2 {

log.Fatalf("Forwarding loop detected in \"%s\" zone. Exiting. See https://coredns.io/plugins/loop#troubleshooting. Probe query: \"HINFO %s\".", l.zone, l.qname)

}

return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)

}

就像 Loop 插件的 README 中写的,这个插件只能够检测一些简单的由于配置造成的循环问题,复杂的循环问题并不能通过当前的插件解决。

总结如果想要在分布式系统实现服务发现的功能,DNS 以及 CoreDNS 其实是一个非常好的选择,CoreDNS 作为一个已经进入 CNCF 并且在 Kubernetes 中作为 DNS 服务使用的应用,其本身的稳定性和可用性已经得到了证明,同时它基于插件实现的方式非常轻量并且易于使用,插件链的使用也使得第三方插件的定义变得非常的方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kafka 创建topic_Kafka试题

1.Kafka中的ISR、AR又代表什么?ISR:与leader保持同步的follower集合AR:分区的所有副本2.Kafka中的HW、LEO等分别代表什么?LEO:没个副本的最后条消息的offsetHW:一个分区中所有副本最小的offset3.Kafka中是怎…

kdj指标主要看哪个值_KDJ绝密买卖法则

今天给大家带来的是一个的普通技术指标的绝密战法。是什么指标呢?就是最普通不过的kdj指标了。但是战法是战法中的战法,很多股民朋友都在kdj上栽过跟头,对不对呢?大家有没有反思为什么会这样了,我来告诉大家&#xff0…

css 高度塌陷_web前端入门到实战:CSS 负边距的行为表现

CSS 中的负边距margin 是可以设置为负值的&#xff0c;这会帮你实现靠近顶部/左边相邻元素的效果&#xff0c;或者实现靠近底部/右边相邻元素的效果。先介绍下我们的测试元素&#xff1a;一个简单的包含三个段落的容器元素。注意&#xff0c;段落设置了固定宽度 250px。<div…

华为手机怎么强制关机_华为手机电源键这么神?你只拿来锁屏关机,白浪费这么强的黑科技...

阅读本文前&#xff0c;请您先点击上面的蓝色字体“生活妙招噻”&#xff0c;再点击“关注”&#xff0c;这样您就可以继续免费收到内容了。每天都有分享。完全是免费订阅&#xff0c;请放心关注。手机电源键你用来干嘛&#xff1f;你肯定觉得它除了锁屏关机&#xff0c;就没其…

firefox使用掘金插件_Mozilla发布新的附加组件来修复旧版Firefox中的扩展禁用问题...

大约一个半星期前的5月4日&#xff0c;用于签署Firefox附加组件的证书过期&#xff0c;随后全球Firefox用户的已有附加组件和新添加附加组件完全停止工作。虽然在Mozilla在下一个周一早上发布修补程序版本&#xff0c;但由于问题解决不彻底&#xff0c;几天之后又发布了另一个&…

可以获得索引值码_SEO优化细节,怎样优化才能让网站获得更好的排名?

任何的网站&#xff0c;都是从“零开始”。无论是网站建设也好&#xff0c;还是网站优化也好&#xff0c;都是需要一步步的巩固基础才能有好的成效。对于网站优化来讲&#xff0c;更需要注重前期细节处理&#xff0c;这样后期的运营才能更好发展。SEO优化细节&#xff0c;怎样优…

file获取文件后缀_Python 工匠:高效操作文件的三个建议

前言在这个世界上&#xff0c;人们每天都在用 Python 完成着不同的工作。而文件操作&#xff0c;则是大家最常需要解决的任务之一。使用 Python&#xff0c;你可以轻松为他人生成精美的报表&#xff0c;也可以用短短几行代码快速解析、整理上万份数据文件。当我们编写与文件相关…

lte核心网由哪些设备组成_电气设备安装工程由哪些部分组成?

电气设备安装工程的组成通用电气设备安装项目是一个工程系统&#xff0c;通过接收电能&#xff0c;转换和分配电能&#xff0c;使用电能或从接收电能到分配给电气设备组成。根据其主要功能&#xff0c;它们分为电气照明系统&#xff0c;电源系统和配电系统。这种主要基于电能的…

guid主分区表损坏怎么办_抹盘提示进程失败,导致Win 10无法正常开机怎么办?...

点击上方「蓝字」关注我哦一般来说&#xff0c;ESP分区不足200MB会导致抹盘进程失败&#xff0c;并且损坏Win 10引导&#xff0c;需要进入PE模式将其修复。本教程所使用的PE为WePE。0、条件电脑进入PE系统。1、开始找到桌面上的“Dism”打开运行。打开程序后可能会提示临时目录…

bundle 安装_超级小白使用pip安装第三方库的正确姿势

为了感谢大家对“Python客栈”的关注与支持&#xff0c;即日起&#xff0c;我们每天会在留言中随机抽取三位粉丝发放6.6元小红包。快来参与吧&#xff01;11.pip是什么pip是python的包管理工具&#xff0c;python2.7/python3.4以上的版本都已经集成了该工具&#xff0c;我们可以…

arcgis js 地图打印_Arcgis在国土空间规划编制中的应用

规划行业经过多年的转变&#xff0c;从原来的城市规划到现在的国土空间规划&#xff0c;理论和技术应用都随之更新。新的国土空间规划在编制成果上&#xff0c;注重建立全国同一的国土空间基础信息平台。目前&#xff0c;单就国土空间规划的编制而言&#xff0c;已从传统规划的…

.network 中文文档_以太坊链下支付网络Raiden API中文文档

雷电网络(Raiden Network)是以太坊区块链的链下扩容方案&#xff0c;Raiden节点的Restful API官方文档中文版由汇智网提供&#xff0c;访问地址&#xff1a;http://cw.hubwiz.com/card/c/raiden-api/Raiden API访问端结点URL中通常包含有版本信息&#xff0c;以便支持对不同版本…

python write 写多行_如何用 Python 执行单行命令

一般来说&#xff0c;面对日常处理的一些小任务&#xff0c;直接用 sed,grep 之类的就可以搞定&#xff0c;更复杂一点的就会考虑 awk 或者用一些现成的轮子&#xff0c;要是 awk 搞不定我就只好用 Python 了。但有些时候&#xff0c;我仅仅只是想写一个一次性脚本&#xff0c;…

小红书点赞收藏有什么用_橱柜门用什么材料好?老师傅开口说话了!听进去算你的,收藏备用...

想做好一个橱柜&#xff0c;橱柜门的材质要选好&#xff0c;而一旦涉及到材质&#xff0c;那些专业生僻的叫法让人眼花缭乱&#xff0c;云里雾里的&#xff0c;难以理解。其实橱柜门的材质大概分为6大类&#xff0c;市面上主流的材质大致有3类。今天FUN姐就跟大家聊聊橱柜门到底…

ddr4服务器内存和普通内存_服务器内存介绍及其供电规范

内存(Memory)也称内存储器或主存储器&#xff0c;与CPU进行高速的数据沟通&#xff0c;用于暂时存放CPU的运算数据。见上图&#xff0c;内存紧挨CPU两侧布局&#xff0c;内存数量和容量根据系统需求和CPU能力决定&#xff0c;内存性能对服务器整体运行快慢起着至关重要的影响。…

小程序新闻列表页面布局代码_论坛小程序·“我的”页面布局

这次让我们来看看界面是怎么布局的吧&#xff01;比如打开模拟器里“我的”页面&#xff0c;找到其所对应的me文件夹里的文件打开me.wxml查看第一段代码&#xff0c;发现其中包含五个view类&#xff0c;他们的属性标签分别是&#xff1a;classamountBg、classimg、classaccount…

准考证打印系统关闭怎么办_2019年执业药师准考证无法正常打印,怎么办?

2019年各地区执业药师准考证打印入口陆续开通&#xff0c;但是很多考生会遇到准考证打印的问题&#xff0c;现小编整理了2019年执业药师准考证无法正常打印情况&#xff0c;供各位考生参考。一、打印准考证必须使用IE浏览器6.0或以上版本。二、禁用或卸载有关拦截工具。(Window…

安卓机更新系统会卡吗_安卓机可以用AirPods Pro吗?告诉你真实体验

[PConline评测]安卓系统可以使用AirPods Pro吗&#xff1f;从AirPods Pro发布起&#xff0c;这问题一直备受关注&#xff0c;毕竟苹果官方宣称AirPods Pro兼容多种苹果设备&#xff0c;可没说兼容安卓机。我们知道其他AirPods产品是可以用在安卓机上的&#xff0c;那么AirPods …

# 解析bt文件_BT、磁力链这些词语是什么意思?

“知其然知其所以然”。我们经常在下载资料的时候能看到BT、磁力链等词语&#xff0c;这些词语到底是什么意思呢&#xff1f;下载都会用&#xff0c;但是你了解吗&#xff1f;BT下载传统的下载模式是每个客户端从服务器拷贝文件&#xff0c;跟校园内常用的FTP一样。因为服务器宽…

怎么修改ppt的虚线间隔_还有一小时下班,领导交给我一份ppt,做不完不许走!...

相信在职场中的好多人&#xff0c;都会遇到领导突然给任务的时候&#xff0c;遇到这种情况该怎么办呢&#xff1f;还能怎么办&#xff1f;听话&#xff01;干活&#xff01;就是这份ppt&#xff0c;就是它&#xff0c;决定了我的下班时间&#xff01;ppt水平差&#xff0c;时间…