上一篇文章介绍了Deployment,在Deployment处理的编排问题中,有这样一个问题,在Deployment面前,所有的pod都是一样的,但是在实际的生产环境中,pod的启动顺序、作用等都是不一样的(主从关系、主备关系),因此,就需要StatefulSet(有状态
应用)
StatefulSet 的设计其实非常容易理解。它把真实世界里的应用状态,抽象为了两种情况:
- 拓扑状态。这种情况意味着,应用的多个实例之间不是完全对等的关系。这些应用实例,必须按照某些顺序启动,比如应用的主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个Pod 删除掉,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的Pod,必须和原来 Pod 的网络标识一样,这样原先的访问者才能使用同样的方法,访问到这个新 Pod。
- 存储状态。这种情况意味着,应用的多个实例分别绑定了不同的存储数据。对于这些应用实例来说,Pod A 第一次读取到的数据,和隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间 Pod A 被重新创建过。这种情况最典型的例子,就是一个数据库应用的多个存储实例。
所以,StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态
Headless Service
Service 是 Kubernetes 项目中用来将一组 Pod 暴露给外界访问的一种机制(在我看来,把他当作为反向代理的架构更容易理解)
访问service的方式
- 是以 Service 的 VIP(Virtual IP,即:虚拟 IP)方式。比如:当我访问 10.0.23.1,这个 Service 的 IP 地址时,10.0.23.1 其实就是一个 VIP,它会把请求转发到该 Service 所代理的某一个 Pod 上。
- 是以 Service 的 DNS 方式。比如:这时候,只要我访问“my-svc.my�namespace.svc.cluster.local”这条 DNS 记录,就可以访问到名叫 my-svc 的 Service 所代理的某一个 Pod
而在Service DNS的方式下,还会有两种处理方式
- Normal Service。这种情况下,你访问“my-svc.my�namespace.svc.cluster.local”解析到的,正是 my-svc 这个 Service 的 VIP,后面的流程就跟VIP 方式一致了。
- Headless Service。这种情况下,你访问“my-svc.my�namespace.svc.cluster.local”解析到的,直接就是 my-svc 代理的某一个 Pod 的 IP 地址。可以看到,这里的区别在于,Headless Service 不需要分配一个 VIP,而是可以直接以 DNS 记录的方式解析出被代理 Pod 的 IP 地址
实例:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.9.1
ports:
- containerPort: 80
name: web
当你按照这样的方式创建了一个 Headless Service 之后,它所代理的所有 Pod 的 IP 地址,都
会被绑定一个这样格式的 DNS 记录
<pod-name>.<svc-name>.<namespace>.svc.cluster.loc
#这个 DNS 记录,正是 Kubernetes 项目为 Pod 分配的唯一的“可解析身份”
#查看创建的service的状态
[root@k8s-master pods]# kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 26h
#查看创建的pods的状态
[root@k8s-master pods]# kubectl get statefulsets.apps web
NAME READY AGE
web 2/2 26h
#确定容器的主机名
[root@k8s-master pods]# kubectl exec web-0 -- sh -c 'hostname '
web-0
[root@k8s-master pods]# kubectl exec web-1 -- sh -c 'hostname '
web-1
#创建一个一次性容器(最新般的busybox会出现无法解析的问题)
kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
/ # nslookup web-0.nginx
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.100.4.119 web-0.nginx.default.svc.cluster.local
#另启一个终端
[root@k8s-master pods]# kubectl get pods -w -l app=nginx
#之前的终端,退出容器
kubectl delete pod -l app=nginx
#切换到新建的终端,观察输出:
[root@k8s-master pods]# kubectl get pods -w -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 1 25h
web-1 1/1 Running 1 25h
web-0 1/1 Terminating 1 26h
web-1 1/1 Terminating 1 26h
web-0 0/1 Terminating 1 26h
web-1 0/1 Terminating 1 26h
web-1 0/1 Terminating 1 26h
web-0 0/1 Terminating 1 26h
web-0 0/1 Terminating 1 26h
web-0 0/1 Terminating 1 26h
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 1s
web-0 0/1 ContainerCreating 0 2s
web-0 1/1 Running 0 3s
web-1 0/1 Terminating 1 26h
web-1 0/1 Terminating 1 26h
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 0/1 ContainerCreating 0 1s
web-1 1/1 Running 0 2s
再次使用一次性容器,查看DNS解析的结果,发现没有变化
kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
/ # nslookup web-0.nginx
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.100.4.119 web-0.nginx.default.svc.cluster.local
可以看到,当我们把这两个 Pod 删除之后,Kubernetes 会按照原先编号的顺序,创建出了两
个新的 Pod。并且,Kubernetes 依然为它们分配了与原来相同的“网络身份”:web-0.nginx
和 web-1.nginx。通过这种严格的对应规则,StatefulSet 就保证了 Pod 网络标识的稳定性。