理论基础

StatefulSet 是用来管理有状态应用的工作负载 API 对象。
StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。

StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和缩放。
  • 有序的、自动的滚动更新。

在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用 由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 或者 ReplicaSet 可能更适用于你的无状态应用部署需要。

有状态与无状态

有人将web应用中有无状态的情况,比着顾客逛商店的情景。
顾客:浏览器访问方;
商店:web服务器;
一次购买:一次http访问
我们知道,上一次顾客购买,并不代表顾客下一个小时一定会买(当然也不能代表不会)。 也就是说同一个顾客的不同购买之间的关系是不定的。所以说实在的,这种情况下,让商店保存所有的顾客购买的信息,等到下一次购买可以知道这个顾客以前购买 的内容代价非常大的。所以商店为了避免这个代价,索性就认为每次的购买都是一次独立的新的购买。浅台词:商店不区分对待老顾客和新过客。这就是无状态的。

但是,商店为了提高收益。她是想鼓励顾客购买的。所以告诉你,只要你在一个月内购买了5瓶以上的啤酒,就送你一个酒杯。
这种情况下有两种办法实现:

  • A, 给顾客发放一个磁卡,里面放有顾客过去的购买信息。这样商店就可以知道了。这就是cookie.
  • B, 给顾客发放一个唯一号码,号码制定的顾客的消费信息,存储在商店的服务器中。这就是session。

最后,商店可以全局的决定,是5瓶为送酒杯还是6瓶。这就是application。
其实,这些机制都是在无状态的传统购买过程中加入了一点东西,使整个过程变得有状态。Web应用就是这样的

创建StatefulSet

准备yaml

参考文档: https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/

  1. [root@clientvm ~]# cat statefulset.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: nginx
  6. labels:
  7. app: nginx
  8. namespace: mytest
  9. spec:
  10. ports:
  11. - port: 80
  12. name: web
  13. clusterIP: None
  14. selector:
  15. app: nginx
  16. ---
  17. apiVersion: apps/v1
  18. kind: StatefulSet
  19. metadata:
  20. labels:
  21. app: nginx
  22. name: mystatefulset
  23. namespace: mytest
  24. spec:
  25. replicas: 3
  26. serviceName: nginx
  27. selector:
  28. matchLabels:
  29. app: nginx
  30. template:
  31. metadata:
  32. labels:
  33. app: nginx
  34. spec:
  35. containers:
  36. - image: nginx
  37. name: nginx
  38. imagePullPolicy: IfNotPresent

创建与查看

  1. [root@clientvm ~]# kubectl apply -f statefulset.yaml
  2. service/nginx created
  3. statefulset.apps/mystatefulset created
  4. [root@clientvm ~]# kubectl get all -n mytest
  5. NAME READY STATUS RESTARTS AGE
  6. pod/mystatefulset-0 1/1 Running 0 94s
  7. pod/mystatefulset-1 1/1 Running 0 92s
  8. pod/mystatefulset-2 1/1 Running 0 90s
  9. NAME READY AGE
  10. statefulset.apps/mystatefulset 3/3 94s

删除Pod,自动重建,Pod名字不变

  1. [root@clientvm ~]# kubectl delete pod -n mytest mystatefulset-1
  2. pod "mystatefulset-1" deleted
  3. [root@clientvm ~]# kubectl get pod -n mytest
  4. NAME READY STATUS RESTARTS AGE
  5. mystatefulset-0 1/1 Running 0 2m18s
  6. mystatefulset-1 1/1 Running 0 8s
  7. mystatefulset-2 1/1 Running 0 2m14s

减少Pod

  1. [root@clientvm ~]# kubectl scale statefulset -n mytest mystatefulset --replicas=2
  2. statefulset.apps/mystatefulset scaled
  3. [root@clientvm ~]# kubectl get pod -n mytest
  4. NAME READY STATUS RESTARTS AGE
  5. mystatefulset-0 1/1 Running 0 6m29s
  6. mystatefulset-1 1/1 Running 0 4m19s
  7. [root@clientvm ~]#
  8. [root@clientvm ~]#
  9. [root@clientvm ~]# kubectl scale statefulset -n mytest mystatefulset --replicas=1
  10. statefulset.apps/mystatefulset scaled
  11. [root@clientvm ~]# kubectl get pod -n mytest
  12. NAME READY STATUS RESTARTS AGE
  13. mystatefulset-0 1/1 Running 0 6m36s

Service

statefulset通常需要与无头服务合用。无头服务无虚拟IP,任何访问请求都使用DNS方式指向后端Pod。

[root@clientvm yaml]# kubectl run busyboxcc --image=tutum/dnsutils -n mytest --image-pull-policy=IfNotPresent -- sleep 1000

[root@clientvm yaml]# kubectl exec -n mytest busyboxcc -it -- bash
root@busyboxcc:/# nslookup nginx.mytest.svc.example.com
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   nginx.mytest.svc.example.com
Address: 10.244.102.132
Name:   nginx.mytest.svc.example.com
Address: 10.244.71.199
Name:   nginx.mytest.svc.example.com
Address: 10.244.71.198

root@busyboxcc:/# nslookup mystatefulset-0.nginx.mytest.svc.example.com
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   mystatefulset-0.nginx.mytest.svc.example.com
Address: 10.244.71.198

root@busyboxcc:/# nslookup mystatefulset-1.nginx.mytest.svc.example.com
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   mystatefulset-1.nginx.mytest.svc.example.com
Address: 10.244.102.132


root@busyboxcc:/# nslookup mystatefulset-2.nginx.mytest.svc.example.com
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   mystatefulset-2.nginx.mytest.svc.example.com
Address: 10.244.71.199


root@busyboxcc:/# dig @10.96.0.10 -t a mystatefulset-0.nginx.mytest.svc.example.com
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;mystatefulset-0.nginx.mytest.svc.example.com. IN A

;; ANSWER SECTION:
mystatefulset-0.nginx.mytest.svc.example.com. 30 IN A 10.244.71.198

删除StatefulSet

[root@clientvm ~]# kubectl delete -f statefulset.yaml
service "nginx" deleted
statefulset.apps "mystatefulset" deleted