kube-dns 的前世今生

2016-10-31 18:38:33 +08:00
 Tenxcloud10

上一期我们以 1.2 版本为背景,介绍了 K8S 的服务发现和 kube-dns 插件的相关内容。有了上一期内容作为基础,这期了解最新版本的 kube-dns 就会容易很多。

本文主要对比 1.2 和 1.4 版本中 kube-dns 的主要变化,以及最新版本中 kube-dns 的内部实现。满满的干货来了,你准备好了吗?

大纲

● kube-dns 的主要变化

● kube-dns 的实现原理

● kubedns 容器详解

● dnsmasq 容器简介

● exechealthz 容器简介

主要变化

● 服务发现机制未变化

也就是说 kube-dns 对外的接口是基本没变的。变化主要在于 kube-dns 插件的内部组成,由原来的四个容器变为了三个。

现在回顾一下上一期所讲的 1.2 版本 kube-dns 的组成。

Kube2sky 通过 K8S API 监视 K8S Service 资源的变化,并根据 Service 的信息生成 DNS 记录写入到 etcd 中。 Skydns 为集群中的 Pod 提供 DNS 查询服务, DNS 记录从 etcd 中读取。 Exechealthz 提供健康检查功能。

接下来我们再看一下 1.4 版本 kube-dns 的组成。对比两张图,可以很直观的看到 kube-dns 对外接口没有发生变化。 Exechealthz 是唯一保留的容器,依然提供健康检查。

不同点:

1.会检查两个容器的健康状态。

2.为集群提供 DNS 查询服务的容器由 skydns 变为了 dnsmasq 。

3.Kubedns 容器替代了 kube2sky 来监视 Service 资源。

4.Etcd 容器不见了。

相信有些人会好奇 DNS 记录现在保存在哪了呢?那为了回答这个问题以及了解新版 kube-dns 的工作原理,我们就进入下一章内容来了解其实现原理。

实现原理

● kubedns 容器的实现

本着“ Talk is cheap, show me the code ”原则,我们将会以源码分析的方式介绍其原理。对另外两个容器会进行简要介绍。

先来看一下源码位置,这里列出的是 kube-dns 插件相关的源码,不仅仅是 kubedns 容器的。之前的源码是集中在 cluster/addons/dns 下面的,那么 1.4 版本中分成了三个目录:

● 第一个目录会有 K8S DNS 相关的 README 以及 kubedns 容器的 Dockerfile 。

● 第二个目录存放 kube-dns 插件的编排文件。

● 第三个是 kubedns 源码目录, kubedns 容器使用的命令行就是从这构建出来的。

● kubedns 容器的功能:

● 接入 SkyDNS ,为 dnsmasq 提供查询服务

● 替换 etcd 容器,使用树形结构在内存中保存 DNS 记录

● 通过 K8S API 监视 Service 资源变化并更新 DNS 记录

● 服务 10053 端口

对功能有了大概了解之后,我们下面就结合源码来看看各个功能是如何实现的。

● kubedns 实现—— SkyDNS 接入

下面是 kubedns 启动的部分代码,这部分显示的是 kubedns 在启动的时候会初始化一个 SkyDNS Server ,初始化的时候传入了一个 KubeDNSServer.kd 。

下面是 KubeDNSServer.kd 的初始化代码,使用的是 K8S 提供的包。使用 K8S 提供的包可以初始化一个 SkyDNS Server ?相信很多人能够想到,这个 KubeDNSServer.kd 应该实现了一个接口。

SkyDNS Server 在初始化的时候需要传入一个 Backend 接口,其定义如下。 SkyDNS 基于 Etcd 实现了该接口,也会使用它初始化 Server 。得益于 SkyDNS 的良好设计, K8S 只要实现该接口便可以接入 SkyDNS 来提供 DNS 查询服务,并定制存储功能。

skydns/server/server.go

skydns/server/backend.go

● kubedns 实现—— etcd 替换

在服务发现的流程中,主要用到了 Records 这个方法,下面我们就来看看 K8S 是如何实现这个方法。

主要步骤是先将域名按“.”拆分,并将各部分颠倒顺序生成一个 path 数组。调用 getRecordsForPath 方法获取 DNS 记录并返回。

kubernetes/pkg/dns/dns.go

getRecordsForPath 会调用 cache 的相关方法。这个 cache 会被初始化为一个 TreeCache 结构,定义如下:

kubernetes/pkg/dns/dns.go

kubernetes/pkg/dns/treecache.go

如下图所示, TreeCache 的结构类似于目录树。从根节点到叶子节点的每个路径与一个域名是相对应的,顺序是颠倒的。它的叶子节点只包含 Entries ,非叶子节点只包含 ChildNodes 。叶子节点中保存的就是 SkyDNS 定义的 msg.Service 结构,可以理解为 DNS 记录。

在 Records 接口方法实现中,只需根据域名查找到对应的叶子节点,并返回叶子节点中保存的所有 msg.Service 数据。 K8S 就是通过这样的一个数据结构来保存 DNS 记录的,并替换了 Etcd 。

● kubedns 实现——监视 Service

最后我们来看一下监视 Service 资源的相关代码。如下图所示,这里使用了 k8s.io/kubernetes/pkg/client/cache 包的 NewInformer 方法,这个方法在 K8S 源码里会经常看到。其参数为:

● 第一个参数需要传入 ListWatch 结构,它定义了 List 和 Watch 操作步骤。 kd.kubeClient 结构可以用来访问 K8S API ,从代码中可以看出分别访问了 List 和 Watch API

● 第二个参数为监视的资源类型,代码中指定了 Service 资源

● 第三个参数为 List 操作的执行间隔。 Watch 操作是通知机制,只要监视的资源发送变化

就会调用对应的回调函数。 List 操作会获取最新的全量资源与本地状态进行比较来产生通知,可以避免网络原因导致的 Watch 丢失通知的情况。 List 操作代价较高,因此需要通过第三个参数来设置其执行间隔。

● 最后一个参数用来设置处理事件的回调。

kubernetes/pkg/dns/dns.go

下面我们以新增 Service 事件的处理流程为例来简单了解一下事件处理的代码。

在创建一个 K8S Service 资源后, newService 方法最终会调用 newPortalService 方法,其代码如下。 getSkyMsg 函数会将 Service 的 ClusterIP 保存到 msg.Service 结构中并返回,对应 recordValue 。 recordLabel 可以理解为一个摘要值,与 ClusterIP 是一一对应的,它将作为 TreeCache 叶子节点的 key 。

最后根据 Service 信息找到对应的树枝(如不存在会构建),并设置叶子节点。这样一个新建的 Service 对应的 DNS 记录就保存到 kubedns 中了。

kubernetes/pkg/dns/dns.go

Kubedns 容器的主要原理已经讲解完了,下面我们简要介绍一下其他两个容器。

● dnsmasq 简介

● Dnsmasq 是一款小巧的 DNS 配置工具

● 在 kube-dns 插件中的作用:

● 通过 kubedns 容器获取 DNS 规则,在集群中提供 DNS 查询服务

● 提供 DNS 缓存,提高查询性能

● 降低 kubedns 容器的压力、提高稳定性

● Dockerfile 在 GitHub 上 Kubernetes 组织的 contrib 仓库中,位于 dnsmasq 目录下。

● 在 kube-dns 插件的编排文件中可以看到, dnsmasq 通过参数--server=127.0.0.1#10053 指定 upstream 为 kubedns 。

● exechealthz 简介

● 在 kube-dns 插件中提供健康检查功能

● 源码同样在 contrib 仓库中,位于 exec-healthz 目录下。

● 新版中会对两个容器都进行健康检查,更加完善。

总结

1.4 版本 kube-dns 插件的三个容器的功能如下:

● kubedns 容器

● 监视 k8s Service 资源并更新 DNS 记录

● 替换 etcd ,使用 TreeCache 数据结构保存 DNS 记录并实现 SkyDNS 的 Backend 接口

● 接入 SkyDNS ,对 dnsmasq 提供 DNS 查询服务

● dnsmasq 容器

● 对集群提供 DNS 查询服务

● 设置 kubedns 为 upstream

● 提供 DNS 缓存,降低 kubedns 负载,提高性能

● exechealthz 容器

● 定期检查 kubedns 和 dnsmasq 的健康状态

● 为 k8s 活性检测提供 HTTP API

相比于 1.2 版本,个人认为有了如下改进:

● 无状态服务。 1.2 版本中,需要将 Etcd 的数据 Volume 出来才能保证 Etcd 容器重启之后数据不会丢失,服务可以快速恢复。新版本中作为无状态服务出现,通过增加冗余来提高可靠性。即使 kubedns 容器重启, dnsmasq 缓存机制也可以保证服务的可用性。

● 优化查询效率。 SkyDNS 直接从内存中获取 DNS 记录。

● 完善健康检查。 1.2 版本中只对 kube2sky 设置了健康检查。

除了改进之外,还有一点不足,也是大家比较担心的 「 内存占用 」。目前在 kube-dns 编排文件中默认设置了内存限制为 170M ,在注释中可以看出这一数字并未在大规模集群中验证。不过相信不久的将来我们就能看到这一验证结果。

Tips :直播在哪里?嘘,悄悄告诉你,点击 http://t.cn/RVRITb3 即可查看直播回放!

扫描上方二维码。阅读更多优质文章。

2420 次点击
所在节点    云计算
1 条回复
guonning
2016-10-31 19:11:01 +08:00
先收藏了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/316863

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX