StatefulSet控制器

1、概念

StatefulSet是为了管理有状态服务的问题而设计的

有状态服务?

StatefulSet是有状态的集合,管理有状态的服务,它所管理的Pod的名称不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。比如MySQL主从、redis集群等。

无状态服务?

RC、Deployment、DaemonSet都是管理无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。个体对整体无影响,所有pod都是共用一个数据卷的,部署的tomcat就是无状态的服务,tomcat被删除,在启动一个新的tomcat,加入到集群即可,跟tomcat的名字无关。

2、组成

  1. Headless Service:用来定义pod网路标识,生成可解析的DNS记录

  2. volumeClaimTemplates:存储卷申请模板,创建pvc,指定pvc名称大小,自动创建pvc,且pvc由存储类供应。

  3. StatefulSet:管理pod的

什么是Headless service?

Headless service不分配clusterIP,headless service可以通过解析service的DNS,返回所有Pod的dns和ip地址 (statefulSet部署的Pod才有DNS),普通的service,只能通过解析service的DNS返回service的ClusterIP。

在使用Deployment时,创建的Pod名称是没有顺序的,是随机字符串,在用statefulset管理pod时要求pod名称必须是有序的 ,每一个pod不能被随意取代,pod重建后pod名称还是一样的。因为pod IP是变化的,所以要用Pod名称来识别。pod名称是pod唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个Pod一个唯一的名称。

headless service会为service分配一个域名

.$.svc.cluster.local

K8s中资源的全局FQDN格式:
 Service_NAME.NameSpace_NAME.Domain.LTD.
 Domain.LTD.=svc.cluster.local.     ##这是默认k8s集群的域名。

FQDN 全称 Fully Qualified Domain Name

即全限定域名:同时带有主机名和域名的名称

FQDN = Hostname + DomainName

如 主机名是 xianchao

域名是 baidu.com

FQDN= xianchao.baidu.com

2.StatefulSet会为关联的Pod保持一个不变的Pod Name
statefulset中Pod的名字格式为$(StatefulSet name)-$(pod序号)

3.StatefulSet会为关联的Pod分配一个dnsName
$.$.$.svc.cluster.local

为什么要用volumeClaimTemplate?

对于有状态应用都会用到持久化存储,比如mysql主从,由于主从数据库的数据是不能存放在一个目录下的,每个mysql节点都需要有自己独立的存储空间。而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClaimTemplate,当在使用statefulset创建pod时,volumeClaimTemplates会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。Pod、PVC和PV对应的关系图如下:

image-20220828144739859

3、statefulset资源清单编写技巧

1、查看定义statefulset资源清单所需字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master2 ~]# kubectl explain statefulset
KIND: StatefulSet
VERSION: apps/v1
DESCRIPTION:
StatefulSet represents a set of pods with consistent identities. Identities
are defined as:
- Network: A single stable DNS and hostname.
- Storage: As many VolumeClaims as requested. The StatefulSet guarantees
that a given network identity will always map to the same storage identity.
FIELDS:
apiVersion <string> #定义statefulset资源需要使用的api版本
kind <string> #定义的资源类型
metadata <Object> #元数据
spec <Object> #定义容器相关的信息

2、查看statefulset.spec字段定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@master2 ~]# kubectl explain statefulset.spec
KIND: StatefulSet
VERSION: apps/v1
RESOURCE: spec <Object>
DESCRIPTION:
Spec defines the desired identities of pods in this set.
A StatefulSetSpec is the specification of a StatefulSet.
FIELDS:
podManagementPolicy <string> #pod管理策略
replicas <integer> #副本数
revisionHistoryLimit <integer> #保留的历史版本
selector <Object> -required- #标签选择器,选择它所关联的pod
serviceName <string> -required- #headless service的名字
template <Object> -required- #生成pod的模板
updateStrategy <Object> #更新策略
volumeClaimTemplates <[]Object> #存储卷申请模板

3、查看statefulset的spec.template字段定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#对于template而言,其内部定义的就是pod,pod模板是一个独立的对象
[root@xianchaomaster1 ~]# kubectl explain statefulset.spec.template
KIND: StatefulSet
VERSION: apps/v1
RESOURCE: template <Object>
DESCRIPTION:
template is the object that describes the pod that will be created if
insufficient replicas are detected. Each pod stamped out by the StatefulSet
will fulfill this Template, but have a unique identity from the rest of the
StatefulSet.
PodTemplateSpec describes the data a pod should have when created from a
template
FIELDS:
metadata <Object>
spec <Object> #定义容器属性的

statefulset资源中有两个spec字段。第一个spec声明的是statefulset定义多少个Pod副本(默认将仅部署1个Pod)、匹配Pod标签的选择器、创建pod的模板、存储卷申请模板,第二个spec是spec.template.spec:主要用于Pod里的容器属性等配置。

.spec.template里的内容是声明Pod对象时要定义的各种属性,所以这部分也叫做PodTemplate(Pod模板)。还有一个值得注意的地方是:在.spec.selector中定义的标签选择器必须能够匹配到spec.template.metadata.labels里定义的Pod标签,否则Kubernetes将不允许创建statefulset

4、statefulset配置文件

1、storage_web.yml

1
2
3
4
5
6
[root@master1 statefulset]# cat storage_web.yml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-web
provisioner: example.com/nfs

2、statefulset.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[root@master1 statefulset]# cat statefulset.yml 
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nfs-web
namespace: default
labels:
app: statefulset-nginx
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
volumeMounts:
- name: web-pvc
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: web-pvc
spec:
accessModes: ["ReadWriteOnce","ReadWriteMany"]
storageClassName: nfs-web
resources:
requests:
storage: 2Gi

3、查看pv、pvc、pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@master1 statefulset]# kubectl get pods | grep nfs-web
nfs-web-0 1/1 Running 0 12m
nfs-web-1 1/1 Running 0 12m
nfs-web-2 1/1 Running 0 12m


[root@master1 statefulset]# kubectl get pv | grep nfs-web
pvc-25a5b88a-c131-472b-bc8a-76acecd9f719 2Gi RWO,RWX Delete Bound default/web-pvc-nfs-web-1 nfs-web 22m
pvc-cc8e5814-a794-4fe0-80c5-a3b052e0e249 2Gi RWO,RWX Delete Bound default/web-pvc-nfs-web-0 nfs-web 22m
pvc-eb313078-e6c2-4f6c-b619-466706aa8d8d 2Gi RWO,RWX Delete Bound default/web-pvc-nfs-web-2 nfs-web


[root@master1 statefulset]# kubectl get pvc | grep nfs-web
web-pvc-nfs-web-0 Bound pvc-cc8e5814-a794-4fe0-80c5-a3b052e0e249 2Gi RWO,RWX nfs-web 22m
web-pvc-nfs-web-1 Bound pvc-25a5b88a-c131-472b-bc8a-76acecd9f719 2Gi RWO,RWX nfs-web 22m
web-pvc-nfs-web-2 Bound pvc-eb313078-e6c2-4f6c-b619-466706aa8d8d 2Gi RWO,RWX nfs-web 22m


[root@master1 statefulset]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.255.0.1 <none> 443/TCP 11d
nginx ClusterIP None <none> 80/TCP 22m


[root@master1 statefulset]# kubectl get statefulset -owide
NAME READY AGE CONTAINERS IMAGES
nfs-web 3/3 23m nginx nginx


#查看主机内部的主机名
[root@master1 statefulset]# for i in 0 1 2 ; do kubectl exec nfs-web-$i -- sh -c hostname;done
nfs-web-0
nfs-web-1
nfs-web-2

4、资源清单详细解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[root@master1 statefulset]# cat statefulset.yml 
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None #创建一个没有ip的service
selector:
app: nginx #选择拥有app=nginx标签的pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nfs-web
namespace: default
labels:
app: statefulset-nginx
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx" #headless service的名字
replicas: 3
template: #定义pod的模板
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
volumeMounts:
- name: web-pvc
mountPath: /usr/share/nginx/html
volumeClaimTemplates: #存储卷申请模板
- metadata:
name: web-pvc
spec:
accessModes: ["ReadWriteOnce","ReadWriteMany"]
storageClassName: nfs-web #指定从哪个存储类申请pv
resources:
requests:
storage: 2Gi #需要2G的pvc,会自动跟符合条件的pv绑定

5、statefulset管理pod:扩容、缩容、更新

建议更新的时候直接更改yaml文件,直接edit会导致再次应用yaml文件的时候参数还是和原来的

1、动态扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#修改replicas参数即可
[root@master1 statefulset]# cat statefulset.yml | grep replicas
replicas: 5

#重新应用配置文件
[root@master1 statefulset]# kubectl apply -f statefulset.yml
service/nginx unchanged
statefulset.apps/nfs-web configured


[root@master1 statefulset]# kubectl get pods | grep web
nfs-web-0 1/1 Running 0 49m
nfs-web-1 1/1 Running 0 49m
nfs-web-2 1/1 Running 0 49m
nfs-web-3 1/1 Running 0 12s
nfs-web-4 1/1 Running 0 8s

2、动态缩容

1
2
3
4
5
6
7
8
9
10
11
12
13
#修改replicas参数即可
[root@master1 statefulset]# cat statefulset.yml | grep replicas
replicas: 2


[root@master1 statefulset]# kubectl apply -f statefulset.yml
service/nginx unchanged
statefulset.apps/nfs-web configured


[root@master1 statefulset]# kubectl get pods | grep web
nfs-web-0 1/1 Running 0 53m
nfs-web-1 1/1 Running 0 53m

3、更新

1
2
3
4
5
#修改statefulset.yml配置文件中的image字段
[root@master1 statefulset]# cat statefulset.yml | grep image:
image: ikubernetes/myapp:v2

[root@master1 statefulset]# kubectl apply -f statefulset.yml

statefulset.spec.updateStrategy.rollingUpdate.partition:这个参数默认为0,如果为1,则生成的pod名字序列号大于等于1的进行更新,也就是说除了0都进行更新了。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!