kubenetes源码分析之DNS(四)

来源:互联网 发布:网络歌手如何赚钱 编辑:程序博客网 时间:2024/06/05 18:30

上一篇 介绍了kubedns服务启动过程,现在看服务怎样运行。分为两个主要部分,第一个是怎么提供DNS解析服务,第二是怎样同步更新sevice信息。那下面开始讲解。
服务怎么同步呢?必须是listwatch,继承kubernetes一套API体系,我以后要说的ingress同步,也是这样的。下面看代码:

//servicekcache.ResourceEventHandlerFuncs{            AddFunc:    kd.newService,            DeleteFunc: kd.removeService,            UpdateFunc: kd.updateService,        }//endpointkcache.ResourceEventHandlerFuncs{            AddFunc:    kd.handleEndpointAdd,            UpdateFunc: kd.handleEndpointUpdate,            DeleteFunc: kd.handleEndpointDelete,        },

读者可能有疑惑,DNS是提供service域名解析,服务的,应该是只要将service的域名解析到clusterIP就可以,为啥还要同步endpoint呢?以为有个特殊的服务headless service(不依赖k8s负载均衡,直接映射到后端容器endpoint),所以这里必须还需要有一个endpoint的同步服务。
那么下面具体看看各种情况下service和endpoint是怎样变化的:

服务创建的的时候

func (kd *KubeDNS) newService(obj interface{}) {    if service, ok := assertIsService(obj); ok {        glog.V(2).Infof("New service: %v", service.Name)        glog.V(4).Infof("Service details: %v", service)        // 外部服务使用CNAME records        if service.Spec.Type == v1.ServiceTypeExternalName {            kd.newExternalNameService(service)            return        }        // 如果没有clusterip创建headless service        if !v1.IsServiceIPSet(service) {            kd.newHeadlessService(service)            return        }        if len(service.Spec.Ports) == 0 {            glog.Warningf("Service with no ports, this should not have happened: %v",                service)        }        kd.newPortalService(service)    }}

上面代码创建服务,具体看newPortalService这个方法实现
“`
func (kd *KubeDNS) newPortalService(service *v1.Service) {
subCache := treecache.NewTreeCache()
recordValue, recordLabel := util.GetSkyMsg(service.Spec.ClusterIP, 0)
subCache.SetEntry(recordLabel, recordValue, kd.fqdn(service, recordLabel))

// 创建 SRV Recordsfor i := range service.Spec.Ports {    port := &service.Spec.Ports[i]    if port.Name != "" && port.Protocol != "" {        srvValue := kd.generateSRVRecordValue(service, int(port.Port))        l := []string{"_" + strings.ToLower(string(port.Protocol)), "_" + port.Name}        glog.V(2).Infof("Added SRV record %+v", srvValue)        subCache.SetEntry(recordLabel, srvValue, kd.fqdn(service, append(l, recordLabel)...), l...)    }}subCachePath := append(kd.domainPath, serviceSubdomain, service.Namespace)host := getServiceFQDN(kd.domain, service)reverseRecord, _ := util.GetSkyMsg(host, 0)kd.cacheLock.Lock()defer kd.cacheLock.Unlock()kd.cache.SetSubCache(service.Name, subCache, subCachePath...)kd.reverseRecordMap[service.Spec.ClusterIP] = reverseRecordkd.clusterIPServiceMap[service.Spec.ClusterIP] = service

}
“`
这里面有个很有意思的东西就是treecache,他是替换etcd存储service的数据结构代码在:pkg/dns/treecache/treecache.go里面。它是一个逆向存储service的是一个数据结构,这个设计的原因是由于域名结构就是这样设计的,多级域名,后面是顶级域名,前面可能是2级(如:www.baidu.com)、3级(www.hi.baidu.com)甚至更多级域名。所以反过来存储能够更快的检索到域名。通过fqdn返回完整域名FQDN,所以在末尾会多出一个点(.),并且这是已经通过ReverseArray将域名颠倒,如下:

func (kd *KubeDNS) fqdn(service *v1.Service, subpaths ...string) string {    domainLabels := append(append(kd.domainPath, serviceSubdomain, service.Namespace, service.Name), subpaths...)    return dns.Fqdn(strings.Join(util.ReverseArray(domainLabels), "."))}

在通过SetEntry创建entity

func (cache *treeCache) SetEntry(key string, val *skymsg.Service, fqdn string, path ...string) {    node := cache.ensureChildNode(path...)    val.Key = skymsg.Path(fqdn)    node.Entries[key] = val}

将这个sevice的域名保存到treecache里面。eg:nginx.default.svc.cluster.local
这里写图片描述

删除服务

和创建服务相反的,删除服务代码如下:

 func (kd *KubeDNS) removeService(obj interface{}) {    if s, ok := assertIsService(obj); ok {        subCachePath := append(kd.domainPath, serviceSubdomain, s.Namespace, s.Name)        kd.cacheLock.Lock()        defer kd.cacheLock.Unlock()        success := kd.cache.DeletePath(subCachePath...)        glog.V(2).Infof("removeService %v at path %v. Success: %v",            s.Name, subCachePath, success)        // ExternalName services have no IP        if v1.IsServiceIPSet(s) {            delete(kd.reverseRecordMap, s.Spec.ClusterIP)            delete(kd.clusterIPServiceMap, s.Spec.ClusterIP)        }    }}

通过kd.cache.DeletePath删除路径,并且删除reverseRecordMap和clusterIPServiceMap记录。具体删除方法DeletePath如下:

 func (cache *treeCache) DeletePath(path ...string) bool {    if len(path) == 0 {        return false    }    if parentNode := cache.getSubCache(path[:len(path)-1]...); parentNode != nil {        name := path[len(path)-1]        if _, ok := parentNode.ChildNodes[name]; ok {            delete(parentNode.ChildNodes, name)            return true        }        // ExternalName services are stored with their name as the leaf key        if _, ok := parentNode.Entries[name]; ok {            delete(parentNode.Entries, name)            return true        }    }    return false}

删除节点,先找到父节点,然后删除父节点以下的对应的子节点。

添加endpoint

这时会执行,

func (kd *KubeDNS) addDNSUsingEndpoints(e *v1.Endpoints) error {    svc, err := kd.getServiceFromEndpoints(e)    if err != nil {        return err    }    if svc == nil || v1.IsServiceIPSet(svc) {        // No headless service found corresponding to endpoints object.        return nil    }    return kd.generateRecordsForHeadlessService(e, svc)}

创建一个headless service。其它的方法也是类似的,再次不再赘述。

0 0
原创粉丝点击