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。其它的方法也是类似的,再次不再赘述。
- kubenetes源码分析之DNS(四)
- kubenetes源码分析之DNS(一)
- kubenetes源码分析之DNS(二)
- kubenetes源码分析之DNS(三)
- kubenetes源码分析之DNS(五)
- kubenetes源码分析之DNS(六)
- kubenetes源码分析之DNS(七)
- kubenetes源码分析之DNS(八)
- elasticsearch源码分析之服务端(四)
- elasticsearch源码分析之服务端(四)
- Harbor 源码分析之API(四)
- memcached源码分析之四
- FBReader源码分析(四)---数据之源Model分析
- PDNS-Recursor源码分析之dns server的选择原理
- BT客户端源码分析之四:PiecePicker 类(1)
- BT客户端源码分析之四:PiecePicker 类(2)
- Android FM模块学习之四源码分析(五)
- Android FM模块学习之四源码分析(七)
- Delphi异常处理总结
- Python错误: SyntaxError: Non-ASCII character
- 7. Reverse Integer
- angularjs运行前准备工作
- 腾讯 一面 二面 2017暑期实习招聘
- kubenetes源码分析之DNS(四)
- Shell显示系统时间的全年日
- Java查询IP地址所在地
- java-mybatis、springmvc 整合,使用 @Transactional 时遇到的事务相关问题
- 数组排序
- Maven手动导入jar IDEA
- Servlet与JSP九大内置对象的对应关系
- 时间控件
- php curl 抓取页面几种方法介绍