Kubernetes中kube-dns服务发现介绍

社区编辑2018-05-18 10:43

随着容器技术的流行,越来越多的微服务技术被使用在云中部署应用和服务,运行在网易云上的各种互联网业务,也越来越多地采用微服务架构建立,这催生出更多细小服务单元,以及更好的服务发现能力的需求。

 

作为网易云基础服务采用的容器集群管理平台(也是业界最流行的容器编排工具),Kubernetes为了满足各个服务之间或者服务内部的资源访问,提供了两套服务发现的机制:

  • 环境变量:kubelet创建pod时会自动添加所有可用的service环境变量到该pod中去,本质是添加到pod中的容器当中。这些环境变量诸如{SVCNAME}_SERVICE_HOST和{SVCNAME}_SERVICE_PORT。这时pod只需要通过环境变量连接相应HOST上的PORT即可访问services。
  • DNS:通过内部组件kube-dns实时监测service的变化情况。通过从APIServer获取的service信息创建DNS 记录。如果DNS在整个集群范围内可用,那么所有pod都能够自动解析service的域名。即只需要通过域名即可访问到该service。

 

Kubernetes提供的两套服务发现机制各有优缺点:

  • DNS机制是一直以来的缓存问题,即很多APP都是进行一次域名查找然后将结果缓存起来。如果service对应的资源在DNS TTL时间内故障被删除,客户端还会继续使用缓存失效的DNS记录;
  • 环境变量机制带来的问题会更多一点。第一是service必须要先于pod创建。环境变量的注入只发生在pod创建于service之后,如果pod创建在service之前则访问不到,因为环境变量不会注入到先于service创建的pod中。第二是环境变量泛滥。就目前微服务会创建出更多的service,更多的service导致更多的环境变量,因为一个service不止对应一条环境变量,一般至少3至4条。所以维护起来更复杂。第三是对于容器中使用环境变量的限制。容器只能通过Dockerfile中显示的使用,而例如在容器终端使用env命令访问则无效;

 

目前在Kubernetes上的服务发现应用比较广的还是DNS,因而Kubernetes 1.3正式版把kube-dns作为一个内置的组件合进来,在部署方式上也更方便。所以接下来我们将集中讨论Kubernetes中的DNS服务发现方案的设计。

 

Kubernetes 1.3之前实现整个DNS服务发现总共涉及到三个组件:kube2sky,skyDNS,etcd。结构图如下:


  • kube2sky:用来通过kube-apiserver实时监测集群中service的变更。并把变更信息通告给etcd
  • etcd:保存kube2sky发送过来的service记录,作为服务发现仓库
  • SkyDNS:DNS服务器,通过etcd的service记录处理DNS域名请求

 

Kubernetes1.3实现整个DNS服务发现只用到了一个组件:Kube-dns。其结构图如下:


形如etcd,kube2sky,SkyDNS这三个组件实现的功能都包含在了kube-dns上。etcd的功能由Tree-Cache代替,而Tree-cache只不过是kube-dns申请的内存结构。而SkyDNS服务也作为Kubernetes的一种库来供kube-dns来调用,而不需要作为一个组件来单独创建维护。Kube2sky实现的监测和向etcd推送service信息也已完全包含在kube-dns中。由三个完全单独的组件合并到一个组件来实现,维护起来将更方便。

服务发现应用流程

 下面将介绍一条正常的DNS记录产生到被DNS请求的过程。


对应图中①。通过yaml文件创建一个名为my-nginx-service的service:

apiVersion: v1

kind: Service

metadata:

  name: my-nginx-service

spec:

  ports:

  – port: 80

    name: http

    protocol: TCP

  selector:

name: service-nginx

 

执行kubectl create –f my-nginx-service.yaml即可向APIServer请求创建出一个service。APIServer获取到这个请求后调用相应的api创建出一个service对象,并写入etcd保存。

 

对应图中的②。Kube-dns通过List/Watch操作向APIServer发送Get请求。这时因为有service的创建,所以APIServer会响应这个请求并把service信息回复给kube-dns。

 

对应图中的③。APIServer将创建的my-nginx-service信息回复给kube-dns,这时还会附带一个APIServer分配给service的portal IP(这个IP是从配置中的clusterIP范围中给出的)。

 

对应图中的④。Kube-dns通过监测并得到APIServer回复的service信息,会生成类似这样的DNS条目:{my-nginx-service.default.svc.cluster.local,x.x.x.x,port}。并把这个DNS条目存储在Tree-Cache中。

 

对应图中的⑤。Kunernetes集群中需要访问该my-nginx-service的服务只需要通过http://my-niginx-service或者添加该service对应的端口即可访问。当提交该访问请求的时候会首先向kube-dns中的skyDNS server请求该域名对应的IP地址。而skyDNS server的IP:Port则是通过在kubelet启动的时候配置的,随后kubelet会在每个pod容器创建的时候把相应的skyDNS server和cluster domain注入到容器系统的/etc/resolv.conf文件中。

 

对应图中的⑥。skyDNS server接收到了DNS请求,根据相应的DNS-lib库对请求进行解析。然后根据解析出的域名向Tree-Cache中进行查找。因为my-nginx-service已经存入Tree-Cache,所以这里会返回my-nginx-service对应的IP地址。

 

对应图中的⑦。skyDNS server根据查找的my-nginx-service对应的DNS记录,返回相应的IP地址给请求方。这时即可完成后面的http://my-nginx-service的实际服务请求。

kube-dns的最新变化

kube-dns核心原理一直没变,只是现在在Kubernetes 1.6版本中被单独弄成Addon了,即把代码抽离出来弄成另外一个项目。

 

将kube-dns独立出来,主要基于如下三个方面的考虑:

  1. dns属于kubernetes networking的一部分(dns、ingress、CNI、kube-proxy之类的),和ingress一样,Kubernetes将其解耦出来;
  2. 因为dns项目只使用Kubernetes公共的API;
  3. dns使用的组件版本独立于Kubernetes主版本之外,例如其前端DNSmasq用的是外部软件版本。

 

作者丨毛迎春,赖冬林   网易云工程师

网易云原创投稿,未经许可,谢绝转载