上一篇介绍服务怎样存储和同步,现在说一下怎样提供DNS服务的。startSkyDNSServer启动DNS域名解析服务
func (d *KubeDNSServer) startSkyDNSServer() {
glog.V(0).Infof("Starting SkyDNS server (%v:%v)", d.dnsBindAddress, d.dnsPort)
skydnsConfig := &server.Config{
Domain: d.domain,
DnsAddr: fmt.Sprintf("%s:%d", d.dnsBindAddress, d.dnsPort),
}
if d.nameServers != "" {
for _, nameServer := range strings.Split(d.nameServers, ",") {
r, _ := regexp.Compile(":\\d+$")
if !r.MatchString(nameServer) {
nameServer = nameServer + ":53"
}
if err := validateHostAndPort(nameServer); err != nil {
glog.Fatalf("nameserver is invalid: %s", err)
}
skydnsConfig.Nameservers = append(skydnsConfig.Nameservers, nameServer)
}
}
server.SetDefaults(skydnsConfig)
s := server.New(d.kd, skydnsConfig)
if err := metrics.Metrics(); err != nil {
glog.Fatalf("Skydns metrics error: %s", err)
} else if metrics.Port != "" {
glog.V(0).Infof("Skydns metrics enabled (%v:%v)", metrics.Path, metrics.Port)
} else {
glog.V(0).Infof("Skydns metrics not enabled")
}
go s.Run()
}
server启动后启动监听vendor/github.com/skynetservices/skydns/server/server.go
s.group.Add(1)
go func() {
defer s.group.Done()
if err := dns.ListenAndServe(s.config.DnsAddr, "tcp", mux); err != nil {
fatalf("%s", err)
}
}()
dnsReadyMsg(s.config.DnsAddr, "tcp")
s.group.Add(1)
go func() {
defer s.group.Done()
if err := dns.ListenAndServe(s.config.DnsAddr, "udp", mux); err != nil {
fatalf("%s", err)
}
}()
dnsReadyMsg(s.config.DnsAddr, "udp")
具体执行DNS解析的方式是func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) 由于代码较长我只列出部分:
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
这些都是组织Answer的部分,eg:如果服务请求类型是TypeSRV,则通过SRVRecords这个方法去获取,方法里面又通过services, err := s.backend.Records(name, false)获取Record,这个backend就是之前kd,pkg/dns/dns.go
`func (kd *KubeDNS) Records(name string, exact bool) (retval []skymsg.Service, err error) {
glog.V(3).Infof("Query for %q, exact: %v", name, exact)
trimmed := strings.TrimRight(name, ".")
segments := strings.Split(trimmed, ".")
isFederationQuery := false
federationSegments := []string{}
if !exact && kd.isFederationQuery(segments) {
glog.V(3).Infof("Received federation query, trying local service first")
// Try querying the non-federation (local) service first. Will try
// the federation one later, if this fails.
isFederationQuery = true
federationSegments = append(federationSegments, segments...)
// To try local service, remove federation name from segments.
// Federation name is 3rd in the segment (after service name and
// namespace).
segments = append(segments[:2], segments[3:]...)
}
path := util.ReverseArray(segments)
records, err := kd.getRecordsForPath(path, exact)
if err != nil {
return nil, err
}
if isFederationQuery {
return kd.recordsForFederation(records, path, exact, federationSegments)
} else if len(records) > 0 {
glog.V(4).Infof("Records for %v: %v", name, records)
return records, nil
}
glog.V(3).Infof("No record found for %v", name)
return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
}
当查询到Record之后,最终返回到通过下面代码返回到服务请求者:
defer func() {
metrics.ReportRequestCount(req, metrics.Auth)
metrics.ReportDuration(m, start, metrics.Auth)
metrics.ReportErrorCount(m, metrics.Auth)
if m.Rcode == dns.RcodeServerFailure {
if err := w.WriteMsg(m); err != nil {
logf("failure to return reply %q", err)
}
return
}
// Set TTL to the minimum of the RRset and dedup the message, i.e. remove identical RRs.
m = s.dedup(m)
minttl := s.config.Ttl
if len(m.Answer) > 1 {
for _, r := range m.Answer {
if r.Header().Ttl < minttl {
minttl = r.Header().Ttl
}
}
for _, r := range m.Answer {
r.Header().Ttl = minttl
}
}
if dnssec {
if s.config.PubKey != nil {
m.AuthenticatedData = true
s.Denial(m)
s.Sign(m, bufsize)
}
}
if send := s.overflowOrTruncated(w, m, int(bufsize), metrics.Auth); send {
return
}
s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), m)
if err := w.WriteMsg(m); err != nil {
logf("failure to return reply %q", err)
}
}()
ok,这就是kubedns里面内置的skydns,提供域名解析服务的。但每次都要这样查询,系统的性能会有问题,所以kubernetes又设计了dnsmasq,接下来的blog会介绍这个缓存组件,它是DNS解析的第一站,是放在kubedns的最前端,提供服务。