Kube-dns组成

之前已经了解到kube-dns是由四个容器组成的,它们扮演的角色可以通过下面这张图来理解。

image.png
其中:

  • SkyDNS是用于服务发现的开源框架,构建于etcd之上。作用是为kubernetes集群中的Pod提供DNS查询接口。项目托管于https://github.com/skynetservices/skydns
  • etcd是一种开源的分布式key-value存储,其功能与ZooKeeper类似。在kube-dns中的作用为存储SkyDNS需要的各种数据,写入方为kube2sky,读取方为SkyDNS。项目托管于https://github.com/coreos/etcd
  • exec-healthz是k8s提供的一种辅助容器,多用于side car模式中。它的原理是定期执行指定的Linux指令,从而判断当前Pod中关键容器的健康状态。在kube-dns中的作用就是通过nslookup指令检查DNS查询服务的健康状态,k8s livenessProbe通过访问exec-healthz提供的Http API了解健康状态,并在出现故障时重启容器。其源码位于https://github.com/kubernetes/contrib/tree/master/exec-healthz
  • skydns-rc.yaml中可以发现livenessProbe是设置在kube2sky容器中的,其意图应该是希望通过重启kube2sky来重新写入DNS规则。

    域名格式

    其中cluster_domain可以使用kubelet的–cluster-domain=SomeDomain参数进行设置,同时也要保证kube2sky容器的启动参数中–domain参数设置了相同的值。通常设置为cluster.local。那么之前示例中的my-nginx Service对应的完整域名就是my-nginx.default.svc.cluster.local。看到这里,相信很多人会有疑问,既然完整域名是这样的,那为什么在Pod中只通过Service名称和Namespace就能访问Service呢?下面来解释其中原因。

    配置

  • 域名解析配置
    为了在Pod中调用其他Service,kubelet会自动在容器中创建域名解析配置(/etc/resolv.conf),内容为:

kube-dns原理 - 图2

感兴趣的可以在网上查找一些resolv.conf的资料来了解具体的含义。之所以能够通过Service名称和Namespace就能访问Service,就是因为search配置的规则。在解析域名时会自动拼接成完整域名去查询DNS。
刚才提到的kubelet –cluster-domain参数与search的具体配置是相对应的。而kube2sky容器的–domain参数影响的是写入到etcd中的域名,kube2sky会获取Service的名称和Namespace,并使用–domain参数拼接完整域名。这也就是让两个参数保持一致的原因。

  • NS相关配置

kube-dns可以让Pod发现其他Service,那Pod又是如何自动发现kube-dns的呢?在上一节中的/etc/resolv.conf中可以看到nameserver,这个配置就会告诉Pod去哪访问域名解析服务器。
kube-dns原理 - 图3
orient/strip%7CimageView2/2/w/1240)
相应的,可以在之前提到的skydns-svc.yaml中看到spec.clusterIP配置了相同的值。通常来说创建一个Service并不需要指定clusterIP,k8s会自动为其分配,但kube-dns比较特殊,需要指定clusterIP使其与/etc/resolv.conf中的nameserver保持一致。
修改nameserver配置同样需要修改两个地方,一个是kubelet的–cluster-dns参数,另一个就是kube-dns Service的clusterIP。

总结

接下来重新梳理一下本文的主要内容:

  • 在k8s集群中,服务是运行在Pod中的,Pod的发现和副本间负载均衡是我们面临的问题。
  • 通过Service可以解决这两个问题,但访问Service也需要对应的IP,因此又引入了Service发现的问题。
  • 得益于kube-dns插件,我们可以通过域名来访问集群内的Service,解决了Service发现的问题。
  • 为了让Pod中的容器可以使用kube-dns来解析域名,k8s会修改容器的/etc/resolv.conf配置。

有了以上机制的保证,就可以在Pod中通过Service名称和namespace非常方便地访问对应的服务了。