官网:StatefulSets
StatefulSet就是为了解决有状态服务的问题而设计的,对应Deployments和ReplicaSets是为无状态服务而设计一样。
有状态服务?
有状态服务是指需要数据存储功能的服务、或者指多线程类型的服务,队列等。StatefulSet就是用来管理有状态的服务的,它所管理的Pod的名称不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。比如MySQL主从、redis集群等。
无状态服务?
ReplicaSet、DaemonSet、Deployment等,由于是无状态服务,所以这些控制器创建的pod序号都是随机值。并且在缩容的时候并不会明确缩容某一个pod,而是随机的,因为所有实例对于同一个请求响应的结果是完全一致的,所以缩容任何一个pod都可以,比如nginx实例,tomcat实例等。
Statefulset典型的应用场景包括:
1、稳定的不共享持久化存储:即每个pod的存储资源是不共享的,且pod重新调度后还是能访问到相同的持久化数据,基于pvc实现。
2、稳定的网络标志:即pod重新调度后其PodName和HostName不变,且PodName和HostName是相同的,基于Headless Service来实现的。
3、有序部署,有序扩展:即pod是有顺序的,在部署或者扩展的时候是根据定义的顺序依次依序部署的(即从0到N-1,在下一个Pod运行之前所有之前的pod必都是Running状态或者Ready状态),是基于init containers来实现的。
4、有序收缩:在pod删除时是从最后一个依次往前删除,即从N-1到0。
在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
从上面的应用场景可以发现,StatefulSet由以下几个部分组成:
1、Headless Service(无头服务)用于为Pod资源标识符生成可解析的DNS记录。
2、volumeClaimTemplates (存储卷申请模板)基于静态或动态PV供给方式为Pod资源提供专有的固定存储。
3、StatefulSet,用于管控Pod资源。
Headless service不分配clusterIP,headless service可以通过解析service的DNS,返回所有Pod的dns和ip地址 (statefulSet部署的Pod才有DNS),普通的service,只能通过解析service的DNS返回service的ClusterIP。
headless service会为service分配一个域名
$<service name>.$<namespace name>.svc.cluster.local
StatefulSet会为关联的Pod分配一个dnsName
$<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local
FQDN全称Fully Qualified Domain Name
即全限定域名:同时带有主机名和域名的名称
FQDN = Hostname + DomainName
例如:
主机名是 k8s-worker-16,域名是baidu.com,则FQDN=k8s-worker-16.baidu.com
在deployment中,每一个pod是没有名称,是随机字符串,是无序的。而statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。这时候要用到无头服务,它可以给每个Pod一个唯一的名称。
大部分有状态副本集都会用到持久存储,比如分布式系统来说,由于数据是不一样的,每个节点都需要自己专用的存储节点。而在deployment中pod模板中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,而statefulset定义中的每一个pod都不能使用同一个存储卷,由此基于pod模板创建pod是不适应的,这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷。Pod名称、PVC和PV关系图如下:
[root@k8s-client-17 ~]# kubectl explain sts.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> #存储卷申请模板
pod管理策略
1、OrderedReady:默认方式,按照pod的次序依次创建每个pod并等待ready之后才创建后面的pod。
2、Parallel:并行创建或删除pod,和deployment类型的pod一样,不等待前面的pod ready就开始创建所有的pod。
在Kubernetes 1.7之后,运行通过配置StatefulSet的.spec.updateStrategy,实现Pod的容器、标签、资源请求/限制和注释自动更新。
1、OnDelete更新策略是1.6之前版本的行为。默认的向后兼容更新策略. 只有当你手动删除老的DaemonSet pods时,新的DaemonSet pods 才会被自动创建。
2、RollingUpdate更新策略将实现StatefulSet中Pod的自动滚动更新,老的DaemonSet pods会被自动杀死,新的DaemonSet pods会自动创建。,这是StatefulSet的默认更新模式。如果.spec.updateStrategy.type设置为 RollingUpdate,则StatefulSet控制器将会删除和重建StatefulSet中的每一Pod。它将会按照从最大到最小的序数终止Pod,并按照从小到大顺序重建Pod。
使用nfs-client实现storageclass《第七章》
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
labels:
app: nginx
spec:
ports:
- port: 8088
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sts-nginx
spec:
selector:
matchLabels:
app: nginx
serviceName: "svc-nginx" #必须与上面定义的service名称保持一致
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: webport
volumeMounts:
- name: html #必须与下面定义的volumeClaimTemplates.metadata.name字段的html保持一致
mountPath: /usr/share/nginx/html #容器里挂载目录
volumeClaimTemplates:
- metadata:
name: html
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
storageClassName: managed-nfs-storage #这里定义的storageClass要与之前创建的class名称保持一致
注意:该headless类型service和clusterIp类型的serivice有明显区别,spec下是clusterIp: None
1、观察pod的创建,会发现是有序创建,从0-N的顺序开始创建,pod的名称是:StatefulSet名称+【0-N】序号。
2、观察pod的删除,会发现是有序删除,从N-0的倒序顺序开始删除。
3、查看storageclass,pv,pvc,pod,statefulset,svc
4、每个Pod都拥有一个基于其顺序索引的稳定的主机名
使用带有nslookup命令的busybox镜像启动一个Pod,检查集群内的DNS地址设置。
kubectl run busybox --image busybox:1.28.4 --restart=Never --rm -it busybox -- sh
注意:这里使用busybox的镜像版本建议小于或等于1.28.4,查了相关资料说是官方的bug,否则会出现Can’t find svc-nginx.default.svc.cluster.local: No answer类似报错。
查询service dns,会把对应的pod ip解析出来,如下图所示:
statefulset创建的pod也是有dns记录的,如下图所示:
将pod 的主机名写入它们的index.html文件并验证nginx web 服务器使用该主机名提供服务
[root@k8s-client-17 state]# kubectl exec -it sts-nginx-0 -- /bin/sh
# echo $(hostname) > /usr/share/nginx/html/index.html
# exit
[root@k8s-client-17 state]# kubectl exec -it sts-nginx-1 -- /bin/sh
# echo $(hostname) > /usr/share/nginx/html/index.html
# exit
[root@k8s-client-17 state]# kubectl exec -it sts-nginx-2 -- /bin/sh
# echo $(hostname) > /usr/share/nginx/html/index.html
查看pod的ip地址
通过curl访问pod中nginx服务,结果如下:
重启pod,pod的ip发生了变化
再次通过curl访问pod中nginx服务,访问结果还是一样,结果如下:
总结:虽然sts-nginx-0 、sts-nginx-1和sts-nginx-2被重新调度了,但它们仍然继续监听各自的主机名,因为和它们的PersistentVolumeClaim相关联的PersistentVolume被重新挂载到了各自的volumeMount上。不管sts-nginx-0 、sts-nginx-1和sts-nginx-2被调度到了哪个节点上,它们的PersistentVolumes将会被挂载到合适的挂载点上。
方法一:修改配置文件statefulset.yaml里的replicas的值即可,原来replicas: 3,现在变成replicaset: 4,修改之后,执行如下命令更新
[root@k8s-client-17 state]# kubectl apply -f statefulset.yaml
pod的数量从3个变成4个,pod的名称按照【0-N】规则递增,如下图所示:
方法二:直接编辑控制器实现扩容,修改replicas的值即可,原来replicas: 3,现在变成replicaset: 4,这个是我们把请求提交给了apiserver,实现实时修改
[root@k8s-client-17 state]# kubectl get statefulset
NAME READY AGE
sts-nginx 3/3 16h
[root@k8s-client-17 state]# kubectl edit statefulset sts-nginx
pod的数量从3个变成4个,pod的名称按照【0-N】规则递增,如下图所示:
方法三:使用kubectl scale 或者kubectl patch来扩容一个 StatefulSet
[root@k8s-client-17 state]# kubectl scale statefulset sts-nginx --replicas=4 -n default
statefulset.apps/sts-nginx scaled
或
[root@k8s-client-17 state]# kubectl patch statefulset sts-nginx -p '{"spec":{"replicas":4}}' -n default
statefulset.apps/sts-nginx patch
pod的数量从3个变成4个,pod的名称按照【0-N】规则递增,如下图所示:
方法一:修改配置文件statefulset.yaml里的replicas的值即可,原来replicas: 3,现在变成replicaset: 2,修改之后,执行如下命令更新
[root@k8s-client-17 state]# kubectl apply -f statefulset.yaml
pod的数量从3个变成2个,pod的名称按照【N-0】规则递j减,如下图所示:
方法二:直接编辑控制器实现扩容,修改replicas的值即可,原来replicas: 3,现在变成replicaset: 2,这个是我们把请求提交给了apiserver,实现实时修改
[root@k8s-client-17 state]# kubectl get statefulset
NAME READY AGE
sts-nginx 3/3 16h
[root@k8s-client-17 state]# kubectl edit statefulset sts-nginx
pod的数量从3个变成2个,pod的名称按照【N-0】规则递j减,如下图所示:
方法三:使用kubectl scale或者kubectl patch来缩容一个 StatefulSet
[root@k8s-client-17 state]# kubectl scale statefulset sts-nginx --replicas=2 -n default
statefulset.apps/sts-nginx scaled
或
[root@k8s-client-17 state]# kubectl patch statefulset sts-nginx -p '{"spec":{"replicas":2}}' -n default
statefulset.apps/sts-nginx patch
pod的数量从3个变成2个,pod的名称按照【N-0】规则递j减,如下图所示:
总结:扩容时,如果pv是动态的storageclass的话,pvc同样会增加,但当缩容时,pvc并不会自定的删除,如下图所示:
方法一:修改配置文件statefulset.yaml里的image的值即可,原来image: nginx:1.14.1,现在变成image: nginx:1.16.1,修改之后,执行如下命令更新
[root@k8s-client-17 state]# kubectl apply -f statefulset.yaml
更多详细内容请参考:企业级K8s集群运维实战