服务发现
kubernetes 提供了 service 的概念可以通过 VIP 访问 pod 提供的服务,但是在使用的时候还有一个问题:怎么知道某个应用的 VIP?比如我们有两个应用,一个 app,一个 是 db,每个应用使用 rc 进行管理,并通过 service 暴露出端口提供服务。app 需要连接到 db 应用,我们只知道 db 应用的名称,但是并不知道它的 VIP 地址。
最简单的办法是从 kubernetes 提供的 API 查询。但这是一个糟糕的做法,首先每个应用都要在启动的时候编写查询依赖服务的逻辑,这本身就是重复和增加应用的复杂度;其次这也导致应用需要依赖 kubernetes,不能够单独部署和运行(当然如果通过增加配置选项也是可以做到的,但这又是增加负责度)。
开始的时候,kubernetes 采用了 docker 使用过的方法——环境变量。每个 pod 启动时候,会把通过环境变量设置所有服务的 IP 和 port 信息,这样 pod 中的应用可以通过读取环境变量来获取依赖服务的地址信息。这种方式服务和环境变量的匹配关系有一定的规范,使用起来也相对简单,但是有个很大的问题:依赖的服务必须在 pod 启动之前就存在,不然是不会出现在环境变量中的。
更理想的方案是:应用能够直接使用服务的名字,不需要关心它实际的 ip 地址,中间的转换能够自动完成。名字和 ip 之间的转换就是 DNS 系统的功能,因此 kubernetes 也提供了 DNS 方法来解决这个问题。
部署 DNS 服务
DNS 服务不是独立的系统服务,而是一种 addon ,作为插件来安装的,不是 kubernetes 集群必须的(但是非常推荐安装)。可以把它看做运行在集群上的应用,只不过这个应用比较特殊而已。
DNS 有两种配置方式,在 1.3 之前使用 etcd + kube2sky + skydns 的方式,在 1.3 之后可以使用 kubedns + dnsmasq 的方式。
修改 kubelet 启动参数
不管以什么方式启动,对外的效果是一样的。要想使用 DNS 功能,还需要修改 kubelet的启动配置项,告诉 kubelet,给每个启动的 pod 设置对应的 DNS 信息,一共有两个参数:--cluster_dns=10.10.10.10 --cluster_domain=cluster.local,分别是 DNS 在集群中的 vip 和域名后缀,要和 DNS rc 中保持一致。
测试 DNS 可用性
不管那种部署很是,kubernetes 对外提供的 DNS 服务是一致的。每个 service 都会有对应的 DNS 记录,kubernetes 保存 DNS 记录的格式如下:
<service_name>.<namespace>.svc.<domain>
每个部分的字段意思:
service_name: 服务名称,就是定义 service 的时候取的名字
namespace:service 所在 namespace 的名字
domain:提供的域名后缀,比如默认的 cluster.local
在 pod 中可以通过 service_name.namespace.svc.domain 来访问任何的服务,也可以使用缩写 service_name.namespace,如果 pod 和 service 在同一个 namespace,甚至可以直接使用 service_name。 NOTE:正常的 service 域名会被解析成 service vip,而 headless service 域名会被直接解析成背后的 pods ip。
虽然不会经常用到,但是 pod 也会有对应的 DNS 记录,格式是 pod-ip-address.<namespace>.pod.<domain>,其中 pod-ip-address 为 pod ip 地址的用 - 符号隔开的格式,比如 pod ip 地址是 1.2.3.4 ,那么对应的域名就是 1-2-3-4.default.pod.cluster.local。
我们运行一个 busybox 来验证 DNS 服务能够正常工作:
kubernetes DNS 原理解析
我们前面介绍了两种不同 DNS 部署方式,这部分讲讲它们内部的原理。
kube2sky 模式
这种模式下主要有三个容器在运行:
[iyunv@localhost ~]# docker ps
CONTAINER>
919cbc006da2 172.16.1.41:5000/google_containers/kube2sky:1.12 "/kube2sky /kube2sky " About an hour ago Up About an hour k8s_kube2sky.80a41edc_kube-dns-twl0q_kube-system_ea1f5f4d-15cf-11e7-bece-080027c09e5b_1bd3fdb4
73dd11cac057 172.16.1.41:5000/jenkins/etcd:live "etcd -data-dir=/var/" About an hour ago Up About an hour k8s_etcd.4040370_kube-dns-twl0q_kube-system_ea1f5f4d-15cf-11e7-bece-080027c09e5b_b0e5a99f
0b10ae639989 172.16.1.41:5000/jenkins/skydns:20150703-113305 "bootstrap.sh" About an hour ago Up About an hour k8s_skydns.73baf3b1_kube-dns-twl0q_kube-system_ea1f5f4d-15cf-11e7-bece-080027c09e5b_2860aa6d
这三个容器的作用分别是:
etcd:保存所有的 DNS 数据
kube2sky: 通过 kubernetes API 监听 Service 的变化,然后同步到 etcd
skyDNS:根据 etcd 中的数据,对外提供 DNS 查询服务
kubeDNS 模式
这种模式下,kubeDNS 容器替代了原来的三个容器的功能,它会监听 apiserver 并把所有 service 和 endpoints 的结果在内存中用合适的数据结构保存起来,并对外提供 DNS 查询服务。
kubeDNS:提供了原来 kube2sky + etcd + skyDNS 的功能,可以单独对外提供 DNS 查询服务
dnsmasq: 一个轻量级的 DNS 服务软件,可以提供 DNS 缓存功能。kubeDNS 模式下,dnsmasq 在内存中预留一块大小(默认是 1G)的地方,保存当前最常用的 DNS 查询记录,如果缓存中没有要查找的记录,它会到 kubeDNS 中查询,并把结果缓存起来
每种模式都可以运行额外的 exec-healthz 容器对外提供 health check 功能,证明当前 DNS 服务是正常的。
exec-healthz:运行某个命令,根据结果来对外提供 /healthz 结果
总结
推荐使用 kubeDNS 的模式来部署,因为它有着以下的好处:
不需要额外的存储,省去了额外的维护和数据保存的工作
更好的性能。通过 dnsmasq 缓存和直接把 DNS 记录保存在内存中,来提高 DNS 解析的速度
参考资料
Deploy the DNS Add-on
Kubernetes Admin Docs: Using DNS Pods and Services