kubenetes源码分析之DNS(四)

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

//service
kcache.ResourceEventHandlerFuncs{
            AddFunc:    kd.newService,
            DeleteFunc: kd.removeService,
            UpdateFunc: kd.updateService,
        }
//endpoint
kcache.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 Records
for 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] = reverseRecord
kd.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。其它的方法也是类似的,再次不再赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳清风09

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值