StatefulSet
文章目录
一、介绍与特点
1.1、介绍
- RC、Deployment、DaemonSet都是面向无状态的服务,它们所管理的Pod的IP、名字、启停顺序等都是随机的,而StatefulSet是什么?顾名思义,有状态的集合,管理所有有状态服务,比如MySQL、MongoDB集群等。
- StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序,在StatefulSet中,Pod名称为网络标识(hostname),还必须要用到共享存储。
- 在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的是headless service即无头服务,与service的与别是它没有ClusterIP,解析它的名称时将返回该Headless Service对印的全部Pod的Endpoint(端点)列表。
- 除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为$(podname).(headless server name)FQDN: (podname).(headless servername).namespace.svc.cluster.local
1.2、特点
-
Pod的一致性:包含次序(启停、停止次序)、网络一致性。此一致性与Pod相关,与 呗调度到哪个node节点无关。
-
稳定的次序:对于N个副本的StatefulSet,每个Pod都在[0-N]的范围内分配一个数字序号,且是唯一的。
-
稳定的网络:Pod的hostname模式为(StatefulSet名称)-(序号)。
-
稳定的存储:通过VolumeClaimTemplate为每个Pod创建一个PV。删除、减少副本,不会删除相关的卷。
1.3、组成部分
-
Headless Service:用来定义Po的网络标识(DNS domain)
-
volumeClaimTemplates:存储卷申请模板,创建PVC,指定pvc名称大小,将自动创建pvc,且pvc必须由存储类供应
1.4、为什么需要无头服务
- 在用Deployment时,每一个Pod名称是没有顺序的,是随机字符串,因此Pod名称是无序的,但是在StatefulSet中要求必须是有序的,每个Pod不能随意取代,Pod重建后Pod名称还是一样的。而Pod IP是变化的,所以是以Pod名称来识别,Pod名称是Pod唯一性的标识符,必须持久稳定有效,这时候要用到无头服务,它可以给每个Pod一个唯一的名称。
1.5、为什么需要volumeClaimTemplate
- 对于有状态的副本集都会用到持久存储,对于分布式系统来讲,它的最大特点是数据的不一样的,所以各个节点不能使用同一存储卷,每个节点有自己的专用存储,但是如果在Deployment中的Pod template里定义存储卷,是所有副本共有一个存储卷,数据是相同的,因为是基于模板来创建的,而StatefulSet中每个Pod都要自己的专有存储卷,所以StatefulSet的存储卷就不能再用Pod模板来创建了,于是StatefulSet使用volumeClaimTemplate,称为卷申请模板,它会为每个Pod生成不同的pvc,并绑定pv,从而实现各pod有专用存储。这就是为什么要用volumeClaminTemplate的原因。
二、教程
- 开始之前确保你已经正常部署Kubernetes集群
2.1、创建StatefulSet
- 作为开始,使用如下示例创建了一个StatefulSet(以及它所依赖的Service)。它和StatefulSet概念中的示例相似。它创建了一个Headless Service
nginx
用来发布StatefulSetweb
中的Pod的IP地址。
bash
[root@master ~]# vim nginx-statefulset.yaml
apiVersion: "v1"
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None # Service的类型设置为None将标识这是个Headless Service即无头服务
selector:
app: nginx # 表示这个Service关联的Pod,将会选择标签带有app=nginx的Pod
---
apiVersion: "apps/v1"
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx" # 声明它属于哪个Headless Service无头服务
replicas: 2
selector:
matchLabels:
app: nginx # 这个StatefulSet将会管理标签是app=nginx的Pod
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
# 定义挂载卷,这只是一个演示,生产环境一定不会这样用的
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
hostPath:
# 每个运行Pod的宿主机挂载容器中的目录路径
path: /mnt
bash
# 使用此命令加载资源
[root@master ~]# kubectl apply -f nginx-statefulset.yaml
2.2、查看部署资源
- StatefulSet默认以严格的顺序创建其Pod。
- 对于一个拥有
n
个副本的StatefulSet,Pod被部署时是按照{0...n-1}的序号顺序创建的 - 请注意,直到
web-0
Pod处于Running 并Ready 状态后,web-1
Pod才会被启动。
bash
# 查看Pod
[root@master ~]# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m50s
web-1 1/1 Running 0 2m48s
bash
# 查看StatefulSet
[root@master ~]# kubectl get statefulset web
NAME READY AGE
web 2/2 2m6s
bash
# 查看Service无头服务
[root@master ~]# kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 3m29s
三、StatefulSet中的Pod
- StatefulSet中的每个Pod拥有一个唯一的顺序索引和稳定的网络身份标识。
3.1、检查Pod的顺序索引
- 如果StatefulSet盖尼奥中所提到的,StatefulSet中的每个Pod拥有一个具有黏性的、独一无二的身份标志,这个标志基于StatefulSet控制器分配给每个Pod的唯一顺序索引。Pod名称的格式为
<statefulset 名称>-<序号索引>
。Web
StatefulSet拥有两个副本,所以它创建了两个Pod:web-0
和web-1
bash
[root@master ~]# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 10m
web-1 1/1 Running 0 10m
3.2、使用稳定的网络身份标识
- 每个Pod都拥有一个基于顺序索引的稳定的主机名。使用
kubectl exex
在每个Pod中执行hostname
bash
[root@master ~]# for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1
3.3、查看主机明内部DNS地址
- 使用
kubectl run
运行一个提供nslookup
命令的容器,该命令来自于dnsutils
包。通过对Pod的主机名执行nslookup
,你可以检查这些主机名在集群内部的DNS地址
bash
# 命令终端中运行一个Pod终端
[root@master ~]# kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
# 使用以下命令解析web-0Pod的内部DNS地址
/ # 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.244.1.2 web-0.nginx.default.svc.cluster.local
四、扩容/缩容StatefulSet
- 扩容/缩容StatefulSet指增加或减少它的副本数。这通过更新
replicas
字段完成(水平缩放)。你也可以使用kubectl sacale
或者kubectl pathch
来缩容/缩容一个StatefulSet。
4.1、扩容
-
扩容意味着添加更多副本。如果你的应用程序能够在整个StatefulSet范围内分派工作,则新的更大的Pod及可以执行更多的工作。
-
StatefulSet控制器扩展了副本的数量。如同创建StatefulSet所述,StatefulSet按序号索引扩容各个Pod,并且会等待前一个Pod变为Running和Ready才会启动下一个Pod。
bash
# 将Pod容扩容为5个
[root@master ~]# kubectl scale statefulset web --replicas=5
bash
# 可以提前执行效果更加明显
# 可以看出,前一个Pod创建完成并且成功运行之后才会去创建下一个Pod,而且创建Pod顺序也有要求,仔细观察可以看出顺序是由低到高。
[root@master ~]# kubectl get pod -l app=nginx --watch
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 27m
web-1 1/1 Running 0 26m
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 2s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 1/1 Running 0 1s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 1/1 Running 0 1s
bash
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 27m
web-1 1/1 Running 0 27m
web-2 1/1 Running 0 22s
web-3 1/1 Running 0 20s
web-4 1/1 Running 0 19s
4.2、缩容
- 缩容意味着减少副本数量。例如,你可能因为服务的流量水平已降低并且在当前规模下存在空闲资源的原因执行缩容操作。
bash
# 将Pod缩容为3个
[root@master ~]# kubectl patch sts web -p '{"spec":{"replicas":3}}'
bash
# 缩容的同时可以看出,一共5个Pod,缩容为3个之后,是由从高到底的顺序去缩容的
[root@master ~]# kubectl get pod -l app=nginx --watch
web-4 1/1 Terminating 0 3m43s
web-4 0/1 Terminating 0 3m44s
web-4 0/1 Terminating 0 3m44s
web-4 0/1 Terminating 0 3m44s
web-3 1/1 Terminating 0 3m45s
web-3 0/1 Terminating 0 3m46s
web-3 0/1 Terminating 0 3m46s
web-3 0/1 Terminating 0 3m46s
五、更新StatefulSet
- StatefulSet控制器支持自动更新。更新策略由StatefulSet API对象的
spec.updateStrategy
字段决定。这个特性能够用更新一个StatefulSet中Pod的容器镜像、资源请求和限制、标签和注解。 - 有两个有效的更新策略:
RollingUpdate(默认)
和OnDelete
5.1、滚动更新
RollingUpdate
更新策略会更新一个StatefulSet中的所有Pod,采用与序号索引相反的顺序并遵循StatefulSet的保证- StatefulSet里的Pod采用和序号相反的顺序更新。在更新下一个Pod前,StatefulSet控制器终止每个Pod并等待它们变成Running和Ready。请注意,虽然在顺序后继者变成Running和Ready之前StatefulSet控制器不会更新下一个Pod
bash
# 执行patch操作再次该表容器镜像
[root@master ~]# kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx:1.20"}]'
bash
# 查看滚动更新状态
[root@master ~]# kubectl rollout status statefulset web
partitioned roll out complete: 3 new pods have been updated...
bash
# 查看pod状态
[root@master ~]# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 64s
web-1 1/1 Running 0 66s
web-2 1/1 Running 0 9m17s
bash
# 查看滚动更新后的Nginx版本
[root@master ~]# for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx:1.20
registry.k8s.io/nginx:1.20
registry.k8s.io/nginx:1.20