一、Statefulset控制器:概念、原理解读
StatefulSet是为了管理有状态服务的问题而设计的。用于管理有状态应用程序的部署。与无状态应用程序不同,有状态应用程序在运行时通常要求稳定的网络标识和持久性存储。
1.1、有状态服务?
StatefulSet是有状态的集合,管理有状态的服务,它所管理的Pod的名称不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。比如MySQL主从、redis集群等。
1.2、无状态服务?
RC、Deployment、DaemonSet都是管理无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。个体对整体无影响,所有pod都是共用一个数据卷的,部署的tomcat就是无状态的服务,tomcat被删除,在启动一个新的tomcat,加入到集群即可,跟tomcat的名字无关。
二、Statefulset资源清单文件编写技巧
2.1、查看定义Statefulset资源需要的字段
帮助命令:kubectl explain statefulset
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl explain statefulset |grep '<*>'
apiVersion <string> #定义statefulset资源需要使用的api版本
kind <string> #定义的资源类型
metadata <ObjectMeta> #元数据
spec <StatefulSetSpec> #定义容器相关的信息
status <StatefulSetStatus>
2.2、查看statefulset.spec字段如何定义?
帮助命令:kubectl explain statefulset.spec
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl explain statefulset.spec |grep '<*>'
FIELD: spec <StatefulSetSpec>
minReadySeconds <integer>
ordinals <StatefulSetOrdinals>
persistentVolumeClaimRetentionPolicy <StatefulSetPersistentVolumeClaimRetentionPolicy>
podManagementPolicy <string> #pod管理策略
replicas <integer> #副本数
revisionHistoryLimit <integer> #保留的历史版本
selector <LabelSelector> -required- #标签选择器,选择它所关联的pod
serviceName <string> -required- #headless service的名字
template <PodTemplateSpec> -required- #生成pod的模板
updateStrategy <StatefulSetUpdateStrategy> #更新策略
volumeClaimTemplates <[]PersistentVolumeClaim> #存储卷申请模板
2.3、查看statefulset的spec.template字段如何定义?
对于template而言,其内部定义的就是pod,pod模板是一个独立的对象
帮助命令:kubectl explain statefulset.spec.template
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl explain statefulset.spec.template |grep '<*>'
FIELD: template <PodTemplateSpec>
metadata <ObjectMeta>
spec <PodSpec> #定义容器属性的
通过上面可以看到,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。
三、Statefulset使用案例:部署web站点
创建statefulset资源:
查看yaml资源清单文件:Eg-StatefulSet.yaml
root@k8s-master:~/K8sStudy/Chapter2-12# cat Eg-StatefulSet.yaml
apiVersion: v1 #定义api版本
kind: Service #定义要创建的资源:service
metadata:
name: nginx #定义service的名字
labels:
app: nginx #service的标签
spec:
ports:
- port: 80
name: web
clusterIP: None #创建一个没有ip的service
selector:
app: nginx #选择拥有app=nginx标签的pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx" #headless service的名字
replicas: 2 #副本数
template: #定义pod的模板
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: #存储卷申请模板
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: nfs-storageclass #指定从哪个存储类申请pv
resources:
requests:
storage: 20M #需要20M的pvc,会自动跟符合条件的pv绑定
应用/更新资源清单文件:Eg-StatefulSet.yaml
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl apply -f Eg-StatefulSet.yaml
service/nginx created
statefulset.apps/web created
root@k8s-master:~/K8sStudy/Chapter2-12#
查看statefulset是否创建成功
查看pod
通过上面可以看到创建的pod是有序的.
查看headless service
查看pvc
查看pv
备注:
StatefulSet由以下几个部分组成:
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地址,普通的service,只能通过解析service的DNS返回service的ClusterIP。
1、headless service会为service分配一个域名:<service name>.$<namespace name>.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
如 主机名是rshine
域名是:lucky.com
FQDN= rshine.lucky.com
2、StatefulSet会为关联的Pod保持一个不变的Pod Name;statefulset中Pod的名字格式为$(StatefulSet name)-$(pod序号)
3、StatefulSet会为关联的Pod分配一个dnsName;$<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local
为什么要用volumeClaimTemplate?
对于有状态应用都会用到持久化存储,比如mysql主从,由于主从数据库的数据是不能存放在一个目录下的,每个mysql节点都需要有自己独立的存储空间。而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClainTemplate,当在使用statefulset创建pod时,volumeClainTemplate会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。
验证域名:
使用kubectl run运行一个提供nslookup命令的容器的,这个命令来自于dnsutils包,通过对pod主机名执行nslookup,可以检查pod和service在集群内部的DNS地址:kubectl run busybox --image docker.io/library/busybox:1.28 --image-pull-policy=IfNotPresent --restart=Never --rm -it busybox -- /bin/sh
这个命令之后会进入到容器中,我们可以通过nslookup命令验证域名。
1、验证statefulset创建的pod是有域名的。
可以看到pod的域名web-0.nginx.default.svc.cluster.local是由pod名称.service名称.名称空间名称.svc.cluster.local。通过nslookup命令pod域名各自解析成了各自的pod的ip地址。
2、验证service的域名
可以看到service的域名nginx.default.svc.cluster.local是由service名称.名称空间名称.svc.cluster.local。因为本示例service没有配置ip地址,通过nslookup命令service域名解析到了所有关联的pod的ip地址。如果创建的service有ip,那对这个service做dns解析,会解析到service本身ip。
Statefulset总结:
1、Statefulset管理的pod,pod名字是有序的,由statefulset的名字-0、1、2这种格式组成
2、创建statefulset资源的时候,必须事先创建好一个service,如果创建的service没有ip,那对这个service做dns解析,会找到它所关联的pod ip,如果创建的service有ip,那对这个service做dns解析,会解析到service本身ip。
3、statefulset管理的pod,删除pod,新创建的pod名字跟删除的pod名字是一样的
4、statefulset具有volumeclaimtemplate这个字段,这个是卷申请模板,会自动创建pv,pvc也会自动生成,跟pv进行绑定,那如果创建的statefulset使用了volumeclaimtemplate这个字段,那创建pod,数据目录是独享的
5、ststefulset创建的pod,是有域名的(域名组成:pod-name.svc-name.svc-namespace.svc.cluster.local)
四、Statefulset管理pod:扩容、缩容、更新
4.1、Statefulset实现pod的动态扩容
如果我们觉得两个副本太少了,想要增加,只需要修改配置文件Eg-StatefulSet.yaml里的replicas的值即可,原来replicas: 3,现在变成replicaset: 2,修改之后,执行命令kubectl apply -f Eg-StatefulSet.yaml
更新
root@k8s-master:~/K8sStudy/Chapter2-12# grep replicas Eg-StatefulSet.yaml
replicas: 2
root@k8s-master:~/K8sStudy/Chapter2-12# vim Eg-StatefulSet.yaml
root@k8s-master:~/K8sStudy/Chapter2-12#
root@k8s-master:~/K8sStudy/Chapter2-12#
root@k8s-master:~/K8sStudy/Chapter2-12# grep replicas Eg-StatefulSet.yaml
replicas: 3
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl apply -f Eg-StatefulSet.yaml
service/nginx unchanged
statefulset.apps/web configured
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 25m
web-1 1/1 Running 0 25m
web-2 1/1 Running 0 20s
root@k8s-master:~/K8sStudy/Chapter2-12#
也可以直接编辑控制器实现扩容: kubectl edit statefulset web
把上面的spec下的replicas 后面的值改成4,保存退出.
查看更新后的statefulset的pod状态:kubectl get pod -l app=nginx
可以看到pod的数量扩容到4个了,两种方法都可行,一般建议使用修改yaml文件的方式扩容。
4.2、Statefulset实现pod的动态缩容
如果我们觉得4个Pod副本太多了,想要减少,只需要修改配置文件Eg-StatefulSet.yaml里的replicas的值即可,把replicaset:4变成replicas: 2,修改之后,执行命令kubectl apply -f Eg-StatefulSet.yaml
更新
root@k8s-master:~/K8sStudy/Chapter2-12# grep replicas Eg-StatefulSet.yaml
replicas: 3
root@k8s-master:~/K8sStudy/Chapter2-12# vim Eg-StatefulSet.yaml
root@k8s-master:~/K8sStudy/Chapter2-12#
root@k8s-master:~/K8sStudy/Chapter2-12#
root@k8s-master:~/K8sStudy/Chapter2-12# grep replicas Eg-StatefulSet.yaml
replicas: 2
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl apply -f Eg-StatefulSet.yaml
service/nginx unchanged
statefulset.apps/web configured
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 36m
web-1 1/1 Running 0 36m
root@k8s-master:~/K8sStudy/Chapter2-12#
4.3、Statefulset实现pod的更新
4.3.1、查看Statefulset更新策略sts.spec.updateStrategy有哪些字段?
帮助命令:kubectl explain sts.spec.updateStrategy
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl explain sts.spec.updateStrategy |grep '<*>'
FIELD: updateStrategy <StatefulSetUpdateStrategy>
rollingUpdate <RollingUpdateStatefulSetStrategy>
type <string>
rollingUpdate: 滚动更新,默认更新策略
type:更新方式,默认值为rollingUpdate,可选值:rollingUpdate、OnDelete
4.3.2、查看Statefulset滚动更新策略.spec.updateStrategy.rollingUpdate有哪些字段?
帮助命令:kubectl explain sts.spec.updateStrategy.rollingUpdate
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl explain sts.spec.updateStrategy.rollingUpdate |grep '<*>'
FIELD: rollingUpdate <RollingUpdateStatefulSetStrategy>
maxUnavailable <IntOrString> # 最多有多少个pod不可用
partition <integer> # 将大于这个数字的pod序号的pod删除
修改Eg-StatefulSet.yaml,添加更新策略,和修改image镜像,如下:
查看yaml资源清单文件:Eg-StatefulSet.yaml
root@k8s-master:~/K8sStudy/Chapter2-12# cat Eg-StatefulSet.yaml
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: web
spec:
# 添加更新策略
updateStrategy:
rollingUpdate:
maxUnavailable: 0
partition: 1
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
# 修改镜像由nginx改为tomcat
image: tomcat:latest
imagePullPolicy: IfNotPresent
ports:
# 修改容器内部端口80改为8080
- containerPort: 8080
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: nfs-storageclass
resources:
requests:
storage: 20M
另起一个Master终端动态查看pod:kubectl get pods -l app=nginx -w
应用/更新yaml资源清单文件:Eg-StatefulSet.yaml
root@k8s-master:~/K8sStudy/Chapter2-12# kubectl apply -f Eg-StatefulSet.yaml
service/nginx unchanged
statefulset.apps/web configured
另一个终端动态查看pod结果如下:
从上面结果可以看出来,pod在更新的时候,只是更新了web-1这个pod, partition: 1表示更新的时候会把pod序号大于等于1的进行更新,如果想全部都更新,就把这个值设置为0。
如果更新策略是OnDelete,那不会自动更新pod,需要手动删除,重新常见的pod才会实现更新