HeadlessService
下面是一个标准的定义 Headless Service的 YAML 文件:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
当Service的clusterIP字段的值为None时,就说明该Service是Headless Service。
当你按照这样的方式创建了一个 Headless Service 之后,它所代理的所有 Pod 的 IP 地址,都会被绑定一个这样格式的 DNS 记录,如下所示:
<pod-name>.<svc-name>.<namespace>.svc.cluster.local
这个 DNS 记录,正是 Kubernetes 项目为 Pod 分配的唯一的“可解析身份”(Resolvable Identity)。这个“可解析身份”包含svc-name字段,所以只要你知道了一个 Pod 的名字,以及它对应的 Service 的名字,你就可以非常确定地通过这条 DNS 记录访问到 Pod 的 IP 地址。
在StatefulSet中,由于Pod的名字是不变的(通过编号来维护,编号还能保证Deployment下的多个Pod在部署的时候是阻塞的有顺序的),所以<pod-name>.<svc-name>.<namespace>.svc.cluster.local这个DNS记录就不会改变,这样就可以保证Pod重建后,还是可以重新访问到之前的域名。
StatefulSet
如果实例之间存在依赖关系,比如:主从关系、主备关系,这个时候就无法再使用Deployment了。
k8s是第一个解决有状态应用(比如数据库)这个编排难题的,它使用了StatefulSet这个API对象,它把真实世界里的应用状态,抽象为两种情况:
- 拓扑状态。这种情况意味着,应用的多个实例之间不是完全对等的关系。这些应用实例,必须按照某些顺序启动,比如应用的主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个 Pod 删除掉,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的 Pod,必须和原来 Pod 的网络标识一样,这样原先的访问者才能使用同样的方法,访问到这个新 Pod。 拓扑状态是通过Headless Service来维护的。
- 存储状态。这种情况意味着,应用的多个实例分别绑定了不同的存储数据。对于这些应用实例来说,Pod A 第一次读取到的数据,和隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间 Pod A 被重新创建过。这种情况最典型的例子,就是一个数据库应用的多个存储实例。 存储状态是通过PVC(Persistent Volume Claim)来维护的。
所以,StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态。
StatefulSet会使用到HeadlessService,而HeadlessService会给它所代理的Pod的IP地址绑定如下格式的DNS记录:
<pod-name>.<svc-name>.<namespace>.svc.cluster.local
StatefulSet 又是如何使用这个 DNS 记录来维持 Pod 的拓扑状态的呢?
|
|
从上面的StatefulSet的yaml中可以看出,它和Deployment的区别是,多了一个serviceName=nginx的字段。 这个字段的作用,就是告诉 StatefulSet 控制器,在执行控制循环(Control Loop)的时候,请使用名为 nginx的 这个 Headless Service 来保证 Pod 的“可解析身份”。所以在创建StatefulSet之前,需要先创建Headless Service
执行如下操作:
这时候,如果你手比较快的话,还可以通过 kubectl 的 -w 参数,即:Watch 功能,实时查看 StatefulSet 创建两个有状态实例的过程:
通过上面这个 Pod 的创建过程,我们不难看到,StatefulSet 给它所管理的所有 Pod 的名字,进行了编号,编号规则是:-。 而且这些编号都是从 0 开始累加,与 StatefulSet 的每个 Pod 实例一一对应,绝不重复。
更重要的是,这些 Pod 的创建,也是严格按照编号顺序进行的。比如,在 web-0 进入到 Running 状态、并且细分状态(Conditions)成为 Ready 之前,web-1 会一直处于 Pending 状态。
当这两个 Pod 都进入了 Running 状态之后,你就可以使用kubectl exec命令来查看到它们各自唯一的“网络身份”了。
接下来,我们再试着以 DNS 的方式,访问一下这个 Headless Service:
$ kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
$ nslookup web-0.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.244.1.7
$ nslookup web-1.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-1.nginx
Address 1: 10.244.2.7
从 nslookup 命令的输出结果中,我们可以看到,在访问 web-0.nginx 的时候,最后解析到的,正是 web-0 这个 Pod 的 IP 地址;而当访问 web-1.nginx 的时候,解析到的则是 web-1 的 IP 地址。
这时候,如果你在另外一个 Terminal 里把这两个“有状态应用”的 Pod 删掉: 然后,再在当前 Terminal 里 Watch 一下这两个 Pod 的状态变化,就会发现一个有趣的现象:
可以看到,当我们把这两个 Pod 删除之后,Kubernetes 会按照原先编号的顺序,创建出了两个新的 Pod。并且,Kubernetes 依然为它们分配了与原来相同的“网络身份”:web-0.nginx 和 web-1.nginx。
通过这种严格的对应规则(而不是使用hash算法生成一个字符串来标识),StatefulSet 就保证了 Pod 网络标识的稳定性。
所以,如果我们再用 nslookup 命令,查看一下这个新 Pod 对应的 Headless Service 的话,你就会发现原来的域名还能用:
|
|
不过,相信你也已经注意到了,尽管 web-0.nginx 这条记录本身不会变,但它解析到的 Pod 的 IP 地址,并不是固定的。这就意味着,对于“有状态应用”实例的访问,你必须使用 DNS 记录或者 hostname 的方式,而绝不应该直接访问这些 Pod 的 IP 地址。
为了维护存储状态,需要用到PVC,所以将上面的StatefulSet的yaml改为下面这个样子:
|
|
注意,不能单独的创建PVC之后,在挂载在StatefulSet中。而是使用StatefulSet的volumeClaimTemplates字段来维护PVC。
这样,凡是被这个 StatefulSet 管理的 Pod,都会声明一个对应的 PVC;而这个 PVC 的定义,就来自于 volumeClaimTemplates 这个模板字段,而且使用与这个 Pod 完全一致的编号(<PVC名字>-<StatefulSet名字>-<编号>)。
所以在部署StatefulSet之前,需要先让运维人员创建好PV。除非你的 Kubernetes 集群运行在公有云上,这样 Kubernetes 就会通过 Dynamic Provisioning 的方式,自动为你创建与 PVC 匹配的 PV。
只更新序号大于或者等于2的Pod:
|
|
本文发表于 0001-01-01,最后修改于 0001-01-01。
本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。

