千家信息网

SkyDNS工作原理是什么

发表于:2025-02-04 作者:千家信息网编辑
千家信息网最后更新 2025年02月04日,这篇文章主要讲解了"SkyDNS工作原理是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"SkyDNS工作原理是什么"吧!SkyDNS2是SkyDN
千家信息网最后更新 2025年02月04日SkyDNS工作原理是什么

这篇文章主要讲解了"SkyDNS工作原理是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"SkyDNS工作原理是什么"吧!

SkyDNS2是SkyDNS Version 2.x的统称,其官方文档只有README.md,网上能找到的资料也不多,因此需要我们自行对代码进行一定的分析,才能对其有更好的理解,这就是本文的工作,通过走读SkyDNS的代码,了解其内部架构及其工作原理。

SkyDNS工作原理

SkyDNS Server的工作,依赖后端Key-Value存储的支持。当前支持etcd或etcd3作为Backend(架构图中蓝色部分),为SkyDNS提供配置和数据的管理。

通过环境变量ETCD_MACHINES进行etcd cluster的配置,如果Backend为etcd3,还需要设置etcd中/v2/keys//skydns/config/etcd3为true。SkyDNS中有etcd client模块,负责与ETCD_MACHINES的通信。

SkyDNS主要对应的etcd key path如下:

/v2/keys/skydns/config/v2/keys/skydns/local/skydns/east/production/rails/v2/keys/skydns/local/skydns/dns/stub/v2/keys/skydns/local/skydns/...

通过如下环境变量的配置,支持prometheus监控(架构图中棕色部分)。如果想disable prometheus监控,则配置环境变量PROMETHEUS_PORT的值为0即可。

Port      = os.Getenv("PROMETHEUS_PORT")Path      = envOrDefault("PROMETHEUS_PATH", "/metrics")Namespace = envOrDefault("PROMETHEUS_NAMESPACE", "skydns")Subsystem = envOrDefault("PROMETHEUS_SUBSYSTEM", "skydns")

如果/v2/keys/skydns/config/nameservers有值,则SkyDNS解析不了的Domain,会forward到对应的这些IP:Port构成的nameservers,由它们进行解析(架构图中绿色部分)。

参考官方文档https://github.com/skynetservices/skydns/blob/master/README.md完成参数配置后,便可启动SkyDNS。

SkyDNS Server的启动过程如下:

  • 创建etcd client对象;

  • dns_addr 和 nameservers参数合法性检查;

  • 加载启动参数到etcd,覆盖/v2/keys/skydns/config中原有数据;

  • 配置SkyDNS Server参数的default值,并创建SkyDNS server对象;

  • 去etcd中加载.../dns/stub//xx数据作为server的stub zones数据,并启动对.../dns/stub/的watcher,一旦有数据更新,就加载到server的stub zones数据中;

  • 注册SkyDNS metrics到prometheus;

  • 然后在/v2/keys/skydns/config/dns_addr配置的interface和port上开启tcp/udp监听服务并block住,由此开始提供DSN服务。

在github.com/skynetservices/skydns/server/server.go中的ServeDNS方法覆盖了miekg/dns/server中的ServeMux.ServeDNS方法,由自实现的ServeDNS提供来处理DNS client的请求。

github.com/skynetservices/skydns/server/server.go// ServeDNS is the handler for DNS requests, responsible for parsing DNS request, possibly forwarding// it to a real dns server and returning a response.func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {        ...        // Check cache first.        m1 := s.rcache.Hit(q, dnssec, tcp, m.Id)        if m1 != nil {                ...                // Still round-robin even with hits from the cache.                // Only shuffle A and AAAA records with each other.                if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA {                        s.RoundRobin(m1.Answer)                }                ...                return        }        for zone, ns := range *s.config.stub {                if strings.HasSuffix(name, "." + zone) || name == zone {                        metrics.ReportRequestCount(req, metrics.Stub)                        resp := s.ServeDNSStubForward(w, req, ns)                        if resp != nil {                                s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp)                        }                        metrics.ReportDuration(resp, start, metrics.Stub)                        metrics.ReportErrorCount(resp, metrics.Stub)                        return                }        }        ...                if name == s.config.Domain {                if q.Qtype == dns.TypeSOA {                        m.Answer = []dns.RR{s.NewSOA()}                        return                }                if q.Qtype == dns.TypeDNSKEY {                        if s.config.PubKey != nil {                                m.Answer = []dns.RR{s.config.PubKey}                                return                        }                }        }        if q.Qclass == dns.ClassCHAOS {                if q.Qtype == dns.TypeTXT {                        switch name {                        case "authors.bind.":                                fallthrough                        case s.config.Domain:                                hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}                                authors := []string{"Erik St. Martin", "Brian Ketelsen", "Miek Gieben", "Michael Crosby"}                                for _, a := range authors {                                        m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{a}})                                }                                for j := 0; j < len(authors)*(int(dns.Id())%4+1); j++ {                                        q := int(dns.Id()) % len(authors)                                        p := int(dns.Id()) % len(authors)                                        if q == p {                                                p = (p + 1) % len(authors)                                        }                                        m.Answer[q], m.Answer[p] = m.Answer[p], m.Answer[q]                                }                                return                        case "version.bind.":                                fallthrough                        case "version.server.":                                hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}                                m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{Version}}}                                return                        case "hostname.bind.":                                fallthrough                        case "id.server.":                                // TODO(miek): machine name to return                                hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}                                m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}}                                return                        }                }                // still here, fail                m.SetReply(req)                m.SetRcode(req, dns.RcodeServerFailure)                return        }        switch q.Qtype {        case dns.TypeNS:                if name != s.config.Domain {                        break                }                // Lookup s.config.DnsDomain                records, extra, err := s.NSRecords(q, s.config.dnsDomain)                if isEtcdNameError(err, s) {                        m = s.NameError(req)                        return                }                m.Answer = append(m.Answer, records...)                m.Extra = append(m.Extra, extra...)        case dns.TypeA, dns.TypeAAAA:                records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false)                if isEtcdNameError(err, s) {                        m = s.NameError(req)                        return                }                m.Answer = append(m.Answer, records...)        case dns.TypeTXT:                records, err := s.TXTRecords(q, name)                if isEtcdNameError(err, s) {                        m = s.NameError(req)                        return                }                m.Answer = append(m.Answer, records...)        case dns.TypeCNAME:                records, err := s.CNAMERecords(q, name)                if isEtcdNameError(err, s) {                        m = s.NameError(req)                        return                }                m.Answer = append(m.Answer, records...)        case dns.TypeMX:                records, extra, err := s.MXRecords(q, name, bufsize, dnssec)                if isEtcdNameError(err, s) {                        m = s.NameError(req)                        return                }                m.Answer = append(m.Answer, records...)                m.Extra = append(m.Extra, extra...)        default:                fallthrough // also catch other types, so that they return NODATA        case dns.TypeSRV:                records, extra, err := s.SRVRecords(q, name, bufsize, dnssec)                if err != nil {                        if isEtcdNameError(err, s) {                                m = s.NameError(req)                                return                        }                        logf("got error from backend: %s", err)                        if q.Qtype == dns.TypeSRV { // Otherwise NODATA                                m = s.ServerFailure(req)                                return                        }                }                // if we are here again, check the types, because an answer may only                // be given for SRV. All other types should return NODATA, the                // NXDOMAIN part is handled in the above code. TODO(miek): yes this                // can be done in a more elegant manor.                if q.Qtype == dns.TypeSRV {                        m.Answer = append(m.Answer, records...)                        m.Extra = append(m.Extra, extra...)                }        }        if len(m.Answer) == 0 { // NODATA response                m.Ns = []dns.RR{s.NewSOA()}                m.Ns[0].Header().Ttl = s.config.MinTtl        }}

上面代码逻辑比较复杂,细节上需要你慢慢去理解,简短的可以总结如下:

  • 如架构图中标注的线路1:如果在SkyDNS维护的cache中找到对应Msg,则从cache中读取并返回Msg给DNS client;

  • 如架构图中标注的线路2:如果在cache中没有对应的记录,并且是需要DNS forward的场景(比如name匹配到stub zones等),则将请求forward到对应的DNS servers进行处理;

  • 如架构图中标注的线路3:如果在cache中没有对应的记录,并且Question Type为A/AAAA,SRV等类型时,就通过etcd client去etcd cluster中获取对应的Rule,并构造Msg返回。

感谢各位的阅读,以上就是"SkyDNS工作原理是什么"的内容了,经过本文的学习后,相信大家对SkyDNS工作原理是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0