StatefulSet
- StatefulSet(有状态集)常用于部署有状态的且需要有序启动的应用程序。
StatefulSet的基本概念
- StatefulSet主要用于管理有状态应用程序的工作负载API对象.
- 比如在生产环境中,可以部署ElasticSearch集群、MongoDB集群或者需要持久化的RabbitMQ集群、Redis集群、Kafka集群和ZooKeeper集群等。
- 而StatefulSet创建的Pod一般使用Headless Service(无头服务)进行通信,和普通的Service的区别在于Headless Service没有ClusterIP,它使用的是Endpoint进行互相通信,
- Headless一般的格式为
statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local。
# 说明:
- serviceName为Headless Service的名字。
- 0..N-1为Pod所在的序号,从0开始到N-1。
- statefulSetName为StatefulSet的名字。
- namespace为服务所在的命名空间。
- .cluster.local为Cluster Domain(集群域)。
- 比如,一个Redis主从架构,Slave连接Master主机配置就可以使用不会更改的Master的Headless Service,例如Redis从节点(Slave)配置文件如下:
port 6379
slaveofredis-sentinel-master-ss-0.redis-sentinel-master-ss.public-service.svc.cluster.local 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
……
- 其中,redis-sentinel-master-ss-0.redis-sentinel-master-ss.public-service.svc.cluster.local是Redis Master的Headless Service。
定义一个简单的StatefulSet
apiVersion: v1
kind: Service
metadata:
name: nginx-sts
labels:
app: nginx-sts
spec:
ports:
- port: 80
name: nginx-sts
clusterIP: None
selector:
app: nginx-sts
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-sts
spec:
serviceName: "nginx-sts"
replicas: 2
selector:
matchLabels:
app: nginx-sts
template:
metadata:
labels:
app: nginx-sts
spec:
containers:
- name: nginx-sts
image: nginx:1.15.4
ports:
- containerPort: 80
name: nginx-sts
- kind: Service:
- 定义了一个名字为Nginx的Headless Service,创建的Service格式为nginx-0.nginx.default.svc.cluster.local,其他的类似,因为没有指定Namespace(命名空间),所以默认部署在default。
- kind: StatefulSet:
- 定义了一个名字为web的StatefulSet,replicas表示部署Pod的副本数,本实例为2。
在StatefulSet中必须设置Pod选择器(.spec.selector)用来匹配其标签(.spec.template.metadata.labels)。在1.8版本之前,如果未配置该字段(.spec.selector),将被设置为默认值,在1.8版本之后,如果未指定匹配Pod Selector,则会导致StatefulSet创建错误。
当StatefulSet控制器创建Pod时,它会添加一个标签statefulset.kubernetes.io/pod-name,该标签的值为Pod的名称,用于匹配Service。
查看创建的service和pod
# service CLUSTER-IP为None
[root@k8s-master-01 k8s_test]# kubectl get svc nginx-sts
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-sts ClusterIP None <none> 80/TCP 2m53s
# pod
[root@k8s-master-01 k8s_test]# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-sts-0 1/1 Running 0 8m22s 10.96.44.201 k8s-node-02 <none> <none>
nginx-sts-1 1/1 Running 0 7m34s 10.96.89.135 k8s-node-03 <none> <none>
扩容statefulset
[root@k8s-master-01 k8s_test]# kubectl scale --replicas=3 sts nginx-sts
statefulset.apps/nginx-sts scaled
[root@k8s-master-01 k8s_test]# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-sts-0 1/1 Running 0 11m 10.96.44.201 k8s-node-02 <none> <none>
nginx-sts-1 1/1 Running 0 10m 10.96.89.135 k8s-node-03 <none> <none>
nginx-sts-2 1/1 Running 0 62s 10.96.154.201 k8s-node-01 <none> <none>
测试statefulset
- 创建一个busybox pod用于测试
[root@k8s-master-01 k8s_test]# cat busybox_test.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
[root@k8s-master-01 k8s_test]# kubectl apply -f busybox_test.yaml
pod/busybox created
[root@k8s-master-01 k8s_test]# kubectl get po busybox
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 29s
- 进入busybox 进行测试
[root@k8s-master-01 k8s_test]# kubectl exec -it busybox -- sh
# 根据podname.service可以解析出pod的ip
/ # nslookup nginx-sts-0.nginx-sts
Server: 10.244.0.10
Address 1: 10.244.0.10 kube-dns.kube-system.svc.cluster.local
Name: nginx-sts-0.nginx-sts
Address 1: 10.96.44.201 nginx-sts-0.nginx-sts.default.svc.cluster.local
/ # nslookup nginx-sts-1.nginx-sts
Server: 10.244.0.10
Address 1: 10.244.0.10 kube-dns.kube-system.svc.cluster.local
Name: nginx-sts-1.nginx-sts
Address 1: 10.96.89.135 nginx-sts-1.nginx-sts.default.svc.cluster.local
StatefulSet 扩缩容
- 当生成statefulset的pod时, 会按顺序一个生成好之后再生成下一个,
- 比如当把3个副本的nginx-sts扩容到6个副本, 会依次创建nginx-sts-3, nginx-sts-4,nginx-sts-5
- 当创建过程中前面某个pod被删除, 比如nginx-sts-0被删除了, k8s会将nginx-sts-0创建好之后再继续创建后面的pod
[root@k8s-master-01 k8s_test]# kubectl scale --replicas=6 sts nginx-sts
statefulset.apps/nginx-sts scaled
[root@k8s-master-01 k8s_test]# kubectl delete po nginx-sts-0
pod "nginx-sts-0" deleted
# 使用命令实时查看pod状态, 另开一个窗口扩容前执行
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts -w
NAME READY STATUS RESTARTS AGE
nginx-sts-0 1/1 Running 0 53s
nginx-sts-1 1/1 Running 0 55m
nginx-sts-2 1/1 Running 0 3m34s
nginx-sts-3 0/1 Pending 0 0s
nginx-sts-3 0/1 Pending 0 0s
nginx-sts-3 0/1 ContainerCreating 0 0s
nginx-sts-3 0/1 ContainerCreating 0 1s
nginx-sts-3 1/1 Running 0 1s # nginx-sts-3 创建完成
nginx-sts-4 0/1 Pending 0 0s # 开始创建nginx-sts-4
nginx-sts-4 0/1 Pending 0 0s
nginx-sts-4 0/1 ContainerCreating 0 0s
nginx-sts-0 1/1 Terminating 0 67s # 删除nginx-sts-0
nginx-sts-4 0/1 ContainerCreating 0 1s
nginx-sts-0 1/1 Terminating 0 67s
nginx-sts-0 0/1 Terminating 0 67s
nginx-sts-0 0/1 Terminating 0 67s
nginx-sts-0 0/1 Terminating 0 67s # nginx-sts-0删除完成
nginx-sts-0 0/1 Pending 0 0s # 开始创建nginx-sts-0
nginx-sts-0 0/1 Pending 0 0s
nginx-sts-0 0/1 ContainerCreating 0 0s
nginx-sts-0 0/1 ContainerCreating 0 1s
nginx-sts-4 1/1 Running 0 2s # nginx-sts-4创建完成
nginx-sts-0 0/1 ContainerCreating 0 1s
nginx-sts-0 1/1 Running 0 1s # nginx-sts-0创建完成
nginx-sts-5 0/1 Pending 0 0s
nginx-sts-5 0/1 Pending 0 0s
nginx-sts-5 0/1 ContainerCreating 0 0s
nginx-sts-5 0/1 ContainerCreating 0 1s
nginx-sts-5 1/1 Running 0 2s
- 当缩容删除pod时,会从最后一个序号的pod开始, 一个一个的删除,
- 如果删除过程中前面某个pod被删除了,
- 比如nginx-sts-0被删除了 k8s会继续当前正在删除的pod和nginx-sts-0的删除操作,
- 等当前删除的pod和nginx-sts-0完全删除, 再nginx-sts-0创建好之后再继续删除操作
# 缩容
[root@k8s-master-01 k8s_test]# kubectl scale --replicas=2 sts nginx-sts
statefulset.apps/nginx-sts scaled
# 删除nginx-sts0
[root@k8s-master-01 k8s_test]# kubectl delete po nginx-sts-0
pod "nginx-sts-0" deleted
# 查看pod状态,另开一个窗口缩容前执行
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts -w
NAME READY STATUS RESTARTS AGE
nginx-sts-0 1/1 Running 0 37m
nginx-sts-1 1/1 Running 0 37m
nginx-sts-2 1/1 Running 0 14s
nginx-sts-3 1/1 Running 0 12s
nginx-sts-4 1/1 Running 0 11s
nginx-sts-4 1/1 Terminating 0 49s
nginx-sts-4 1/1 Terminating 0 49s # nginx-sts-4 还没删除完
nginx-sts-0 1/1 Terminating 0 38m # 执行删除nginx-sts-0命令
nginx-sts-0 1/1 Terminating 0 38m
nginx-sts-4 0/1 Terminating 0 50s
nginx-sts-4 0/1 Terminating 0 50s
nginx-sts-4 0/1 Terminating 0 50s # nginx-sts-4 删除完成
nginx-sts-0 0/1 Terminating 0 38m
nginx-sts-0 0/1 Terminating 0 38m
nginx-sts-0 0/1 Terminating 0 38m # nginx-sts-0 删除完成
nginx-sts-0 0/1 Pending 0 0s # 开始创建nginx-sts-0
nginx-sts-0 0/1 Pending 0 0s
nginx-sts-0 0/1 ContainerCreating 0 0s
nginx-sts-0 0/1 ContainerCreating 0 1s
nginx-sts-0 1/1 Running 0 1s # nginx-sts-0创建完成
nginx-sts-3 1/1 Terminating 0 52s # 继续删除nginx-sts-3
nginx-sts-3 1/1 Terminating 0 53s
nginx-sts-3 0/1 Terminating 0 53s
nginx-sts-3 0/1 Terminating 0 53s
nginx-sts-3 0/1 Terminating 0 53s
nginx-sts-2 1/1 Terminating 0 55s # 继续删除nginx-sts-2
nginx-sts-2 1/1 Terminating 0 55s
nginx-sts-2 0/1 Terminating 0 56s
nginx-sts-2 0/1 Terminating 0 56s
nginx-sts-2 0/1 Terminating 0 56s
StatefulSet 更新策略
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"name":"nginx-sts","namespace":"default"},"spec":{"replicas":2,"selector":{"matchLabels":{"app":"nginx-sts"}},"serviceName":"nginx-sts","template":{"metadata":{"labels":{"app":"nginx-sts"}},"spec":{"containers":[{"image":"nginx:1.15.4","name":"nginx-sts","ports":[{"containerPort":80,"name":"nginx-sts"}]}]}}}}
creationTimestamp: "2022-05-24T14:44:47Z"
generation: 10
name: nginx-sts
namespace: default
resourceVersion: "150803"
uid: fb68f471-2b7d-4449-b8d7-b21614a1ef41
spec:
podManagementPolicy: OrderedReady
replicas: 6
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx-sts
serviceName: nginx-sts
template:
metadata:
creationTimestamp: null
labels:
app: nginx-sts
spec:
containers:
- image: nginx:1.15.4
imagePullPolicy: IfNotPresent
name: nginx-sts
ports:
- containerPort: 80
name: nginx-sts
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy: # 更新策略
type: RollingUpdate # RollingUpdate|OnDelete
rollingUpdate: # RollingUpdate选项
partition: 0 # 当type为RollingUpdate时保留不更新的副本数, 可实现灰度发布,分段更新
RollingUpdate
- RollingUpdate为默认更新策略, pod序号从大到小依次更新
- 比如一个statefulset: nginx-sts,它有3个副本, 会从nginx-sts-2开始, 然后nginx-sts-1, 最后更新nginx-sts-0
[root@k8s-master-01 ~]# kubectl set image sts nginx-sts nginx-sts=nginx:1.15.4 --record
statefulset.apps/nginx-sts image updated
# 实时查看pod状态
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts -w
NAME READY STATUS RESTARTS AGE
nginx-sts-0 1/1 Running 0 2m2s
nginx-sts-1 1/1 Running 0 2m22s
nginx-sts-2 1/1 Running 0 3m3s
nginx-sts-2 1/1 Terminating 0 3m9s # 开始删除nginx-sts-2
nginx-sts-2 1/1 Terminating 0 3m9s
nginx-sts-2 0/1 Terminating 0 3m10s
nginx-sts-2 0/1 Terminating 0 3m10s
nginx-sts-2 0/1 Terminating 0 3m10s
nginx-sts-2 0/1 Pending 0 0s # 开始创建nginx-sts-2
nginx-sts-2 0/1 Pending 0 0s
nginx-sts-2 0/1 ContainerCreating 0 0s
nginx-sts-2 0/1 ContainerCreating 0 0s
nginx-sts-2 1/1 Running 0 1s # 完成创建nginx-sts-2
nginx-sts-1 1/1 Terminating 0 2m30s # 开始删除nginx-sts-1
nginx-sts-1 1/1 Terminating 0 2m30s
nginx-sts-1 0/1 Terminating 0 2m31s
nginx-sts-1 0/1 Terminating 0 2m31s
nginx-sts-1 0/1 Terminating 0 2m31s
nginx-sts-1 0/1 Pending 0 0s # 开始创建nginx-sts-1
nginx-sts-1 0/1 Pending 0 0s
nginx-sts-1 0/1 ContainerCreating 0 0s
nginx-sts-1 0/1 ContainerCreating 0 0s
nginx-sts-1 1/1 Running 0 1s # 完成创建nginx-sts-1
nginx-sts-0 1/1 Terminating 0 2m12s # 开始删除nginx-sts-0
nginx-sts-0 1/1 Terminating 0 2m13s
nginx-sts-0 0/1 Terminating 0 2m13s
nginx-sts-0 0/1 Terminating 0 2m13s
nginx-sts-0 0/1 Terminating 0 2m13s
nginx-sts-0 0/1 Pending 0 0s # 开始创建nginx-sts-0
nginx-sts-0 0/1 Pending 0 0s
nginx-sts-0 0/1 ContainerCreating 0 0s
nginx-sts-0 0/1 ContainerCreating 0 0s
nginx-sts-0 0/1 ContainerCreating 0 0s
nginx-sts-0 1/1 Running 0 1s # 完成创建nginx-sts-
- partition
当type为RollingUpdate时保留不更新的副本数, 可实现灰度发布,分段更新
# 用命令将nginx-sts的partition修改为2
[root@k8s-master-01 ~]# kubectl edit sts nginx-sts
statefulset.apps/nginx-sts edited
# 如下
updateStrategy:
rollingUpdate:
partition: 2
type: RollingUpdate
# 修改sts的镜像版本
kubectl set image sts nginx-sts nginx-sts=nginx:1.15.1 --record
# 实时查看pod状态
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts -w
NAME READY STATUS RESTARTS AGE
nginx-sts-0 1/1 Running 0 14m
nginx-sts-1 1/1 Running 0 14m
nginx-sts-2 1/1 Running 0 6s
nginx-sts-2 1/1 Terminating 0 11s # 可以看到只有nginx-sts-2被删除重建了,
nginx-sts-2 1/1 Terminating 0 11s
nginx-sts-2 0/1 Terminating 0 11s
nginx-sts-2 0/1 Terminating 0 11s
nginx-sts-2 0/1 Terminating 0 11s
nginx-sts-2 0/1 Pending 0 0s
nginx-sts-2 0/1 Pending 0 0s
nginx-sts-2 0/1 ContainerCreating 0 0s
nginx-sts-2 0/1 ContainerCreating 0 1s
nginx-sts-2 1/1 Running 0 1s
# 用命令将nginx-sts的partition修改为1
[root@k8s-master-01 ~]# kubectl edit sts nginx-sts
statefulset.apps/nginx-sts edited
# 然后nginx-sts-1也自动删除重建了
nginx-sts-1 1/1 Terminating 0 16m
nginx-sts-1 1/1 Terminating 0 16m
nginx-sts-1 0/1 Terminating 0 16m
nginx-sts-1 0/1 Terminating 0 16m
nginx-sts-1 0/1 Terminating 0 16m
nginx-sts-1 0/1 Pending 0 0s
nginx-sts-1 0/1 Pending 0 0s
nginx-sts-1 0/1 ContainerCreating 0 0s
nginx-sts-1 0/1 ContainerCreating 0 0s
nginx-sts-1 1/1 Running 0 1s
OnDelete
当updateStrategy.type为OnDelete时, 对statefulset做的修改不会立即更新, 只有当pod被手动删除,或其他原因重启时才会触发更新
# 用命令将nginx-sts的updateStrategy.type修改为OnDelete
[root@k8s-master-01 ~]# kubectl edit sts nginx-sts
statefulset.apps/nginx-sts edited
# 如下
updateStrategy:
type: OnDelete
# 修改nginx-sts的镜像版本
kubectl set image sts nginx-sts nginx-sts=nginx:1.15.3 --record
# 此时pod并没有自动更新
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts -w
NAME READY STATUS RESTARTS AGE
nginx-sts-0 1/1 Running 0 4m35s
nginx-sts-1 1/1 Running 0 5m25s
nginx-sts-2 1/1 Running 0 7m38s
# 删除nginx-sts-0
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts -w
NAME READY STATUS RESTARTS AGE
nginx-sts-0 1/1 Running 0 4m35s
nginx-sts-1 1/1 Running 0 5m25s
nginx-sts-2 1/1 Running 0 7m38s
nginx-sts-0 1/1 Terminating 0 5m58s
nginx-sts-0 1/1 Terminating 0 5m58s
nginx-sts-0 0/1 Terminating 0 5m58s
nginx-sts-0 0/1 Terminating 0 5m58s
nginx-sts-0 0/1 Terminating 0 5m58s
nginx-sts-0 0/1 Pending 0 0s
nginx-sts-0 0/1 Pending 0 0s
nginx-sts-0 0/1 ContainerCreating 0 0s
nginx-sts-0 0/1 ContainerCreating 0 1s
nginx-sts-0 1/1 Running 0 1s # nginx-sts-0重建完成
# 检查三个pod的镜像版本 只有nginx-sts-0镜像版本更新了.
[root@k8s-master-01 ~]# kubectl get pod nginx-sts-0 -oyaml | grep 'image:'
- image: nginx:1.15.3
image: docker.io/library/nginx:1.15.3
[root@k8s-master-01 ~]# kubectl get pod nginx-sts-1 -oyaml | grep 'image:'
- image: nginx:1.15.1
image: docker.io/library/nginx:1.15.1
[root@k8s-master-01 ~]# kubectl get pod nginx-sts-2 -oyaml | grep 'image:'
- image: nginx:1.15.1
image: docker.io/library/nginx:1.15.1
statefulset级联删除与非级联删除
级联删除
默认就是级联删除,在删除statefulset时, 连同这个statefulset的pod一起删除
# 删除statefulset
[root@k8s-master-01 ~]# kubectl delete sts nginx-sts
statefulset.apps "nginx-sts" deleted
# pod也一起被自动删除了
[root@k8s-master-01 ~]# kubectl get po -l app=nginx-sts
No resources found in default namespace.
非级联删除
当删除statefulset时填加参数–cascade=orphan, 此时不会立即删除该statefulset相关的pod, 这些pod在下次被手动删除时就会被彻底删除, 不再重建
# 创建nginx-sts
[root@k8s-master-01 k8s_test]# kubectl create -f nginx-sts_test.yaml
statefulset.apps/nginx-sts created
Error from server (AlreadyExists): error when creating "nginx-sts_test.yaml": services "nginx-sts" already exists
# 非级联删除nginx-sts
[root@k8s-master-01 k8s_test]# kubectl delete sts nginx-sts --cascade=orphan
statefulset.apps "nginx-sts" deleted
# pod并没有被删除
[root@k8s-master-01 k8s_test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 10 (39m ago) 23h
nginx-sts-0 1/1 Running 0 5m21s
nginx-sts-1 1/1 Running 0 5m19s
# 手动删除这两个孤儿pod
[root@k8s-master-01 k8s_test]# kubectl delete po nginx-sts-0 nginx-sts-1
pod "nginx-sts-0" deleted
pod "nginx-sts-1" deleted
# 孤儿pod没有被重建
[root@k8s-master-01 k8s_test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 10 (41m ago) 23h