Deployment 假设了一个应用的所有Pod都是一样的,他们之间没有顺序。但在实际场景中,多个实例之间通常会存在依赖关系,例如分布式应用中存在主从、主备关系;数据存储类应用,它的多个实例,往往都会在本地磁盘上保存一份数据。而这些实例一旦被杀掉,即便重建出来,实例与数据之间的对应关系也已经丢失,从而导致应用失败。
当实例之间有不对等关系时,以及实例对外部数据有依赖关系时,被称为“有状态应用”(Stateful Application)
StatefulSet将应用状态抽象为两种:
(1)拓扑状态(顺序)
(2)存储状态
StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态
Service 是 Kubernetes 项目中用来将一组 Pod 暴露给外界访问的一种机制,访问Service 即可访问到具体的Pod。
有两种访问方式:
一:以 Service 的 VIP(Virtual IP,即:虚拟 IP)方式。
二:就是以 Service 的 DNS 方式。比如:访问“my-svc.my-namespace.svc.cluster.local”这条 DNS 记录,就可以访问到名叫 my-svc 的 Service 所代理的某一个 Pod。
在第二个方式下,又可以分为两种处理方法:
第一,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 地址。
例: Headless Service 对应的 YAML 文件
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
Headless Service,其实仍是一个标准 Service 的 YAML 文件。只不过,它的 clusterIP 字段的值是:None,即:这个 Service,没有一个 VIP 作为“头”。这也就是 Headless 的含义。所以,这个 Service 被创建后并不会被分配一个 VIP,而是会以 DNS 记录的方式暴露出它所代理的 Pod。所代理的 Pod,通过Label Selector 机制选择。
按照这样的方式创建了一个 Headless Service 之后,它所代理的所有 Pod 的 IP 地址,都会被绑定一个这样格式的 DNS 记录
...svc.cluster.local
这个 DNS 记录,正是 Kubernetes 项目为 Pod 分配的唯一的“可解析身份”(Resolvable Identity)
StatefulSet 是如何使用这个 DNS 记录来维持 Pod 的拓扑状态的呢
例:StatefulSet 的 YAML 文件
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
这个 YAML 文件,和前面文章中用到的 nginx-deployment 的唯一区别,就是多了一个 serviceName=nginx 字段。
这个字段的作用,就是告诉 StatefulSet 控制器,在执行控制循环(Control Loop)的时候,请使用nginx 这个 Headless Service 来保证 Pod 的“可解析身份”。
kubectl create -f svc.yaml
kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None 80/TCP 109s
kubectl create -f statefulset.yaml
kubectl get statefulset web
NAME READY AGE
web 2/2 2m19s
这些 Pod 的创建,也是严格按照编号顺序进行的。比如,在 web-0 进入到 Running 状态、并且细分状态(Conditions)成为 Ready 之前,web-1 会一直处于 Pending 状态。
当这两个 Pod 都进入了 Running 状态之后,你就可以查看到它们各自唯一的“网络身份”了。
使用 kubectl exec 命令进入到容器中查看它们的 hostname:
kubectl exec web-0 -- sh -c 'hostname'
web-0
kubectl exec web-1 -- sh -c 'hostname'
web-1
这两个 Pod 的 hostname 与 Pod 名字是一致的,都被分配了对应的编号。
以 DNS 的方式,访问一下这个 Headless Service:
kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh
这条命令,启动了一个一次性的 Pod,因为–rm 意味着 Pod 退出后就会被删除掉。然后,在这个 Pod 的容器里面,尝试用 nslookup 命令,解析一下 Pod 对应的 Headless Service:
//nslookup命令 是常用域名查询工具,就是查DNS信息用的命令。
$ kubectl run -i --tty --image busybox 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