AI写作智能体 自主规划任务,支持联网查询和网页读取,多模态高效创作各类分析报告、商业计划、营销方案、教学内容等。 广告
[TOC] # StatefulSet (针对有状态服务)`StatefulSet`是用来解决`Deployment`无法满足有状态服务的需求,在有状态服务时,`Deployment`会遇到以下这个问题: **使用Deployment创建的Pod是无状态、不稳定的,常常会不停的创建和消亡,所以当挂载Volume之后,新创建的Pod是不能重新挂载到这个Volume上,导致新Pod不能正常读取到业务数据。 另一方面,由于用户对Pod的消亡是无感知,所以不易察觉后台Pod异常** ### 特点 StatefulSet 具有以下特点: * **稳定的持久化存储**,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现 * **稳定的网络标志**,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现 * **有序部署,有序扩展**,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现 * **有序收缩,有序删除**(即从N-1到0) (StatefulSet中的Pod是倒序删除) ### 应用场景构成 一个完整的StatefulSet的应用场景通常由以下几个部分构成: * **网络无头服务(Headless Service)**:为Pod生成可解析的DNS记录,即没有cluster IP,使用DNS通讯 * **volumeClaimTemplates(卷申请模版)**:提供专有且固定的存储 * **StatefulSet**:用于管理Pod #### 关于无头服务说明 在StatefulSet场景中,并不需要有头服务那样提供集群负载或者proxy代理,所以使用无头服务 ##### 用途 * **有头服务**:虽然service通常对对应多个endpoint,但是dns查询时只会返回service的地址。具体client访问的是哪个Real Server,是由iptables来决定的。 * **无头服务**:dns查询会返回真实的endpoint。 #### 关于volumeClaimTemplate说明 在StatefulSet场景中,每个Pod都是有状态的,而每一个Pod的状态或数据可能会不一样,所以每个节点有自已的专用存储,所以当Pod消亡重建后,能重新用回原来的PV存储,这个就是`volumeClaimTemplate`,用来实现各个Pod使用专用存储 #### Pod的DNS格式 StatefulSet中每个Pod的DNS格式为:`statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local`其中 * serviceName:为无头服务的名字 * 0..N-1:为Pod所在的序号,从0开始到N-1 * statefulSetName:为StatefulSet的名字 * namespace:为服务所在的namespace (Headless Servic和StatefulSet必须在相同的namespace) * svc.cluster.local为Cluster Domain ### 动态PV申请应用场景示例 #### 本地loca-PV.yaml ~~~ apiVersion: v1 kind: PersistentVolume metadata: name: example-pv spec: capacity: storage: 3Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - worker-1 # 这里根据官网例子,要设置亲和性,改成节点名 ~~~ ![](https://img.kancloud.cn/e2/23/e2239f9d67146e785bb659c110cafb1e_848x91.png) #### 设置StorageClass 根据官网提示,需要创建一个`Storage Class`,用于延迟什么鬼东西,具体看官网翻译吧 ~~~ # 官网有有每种StorageClass的示例,这个示例是创建本地磁盘的示例 kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer ~~~ ![](https://img.kancloud.cn/13/67/13673aec32de06f114f1a1ae21921617_478x94.png) #### StatefulSet创建 ~~~ apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None # 无头 headless Service selector: app: nginx --- apiVersion: apps/v1beta2 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 1 #我只创建了一个local_PV template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: data mountPath: /data volumeClaimTemplates: - metadata: name: data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "local-storage" resources: requests: storage: 1Gi ~~~ ![](https://img.kancloud.cn/69/a8/69a88eab991f3974fc9f25c028dc87cb_884x93.png) > 集群内部通过DNS的方式进行访问,格式为: $(pod-name).$(service\_name).$(namespace).svc.cluster.local svc.cluster.local为Cluster 为集群默认域名 ### 集群外部访问StatefulSet的Pod 因为Pod使用了无头服务的service,即没有Cluster IP,要通过DNS的方式进行访问 **解决方法:**在这种情况下,给 pod 打上 label,然后使用`kubectl expose`的方式,将其以NodePort的方式暴露到集群外部 为RC的nginx创建service,并通过Service的80端口转发至容器的8000端口上。 ~~~ #打标签 kubectl label pod web-0 pod=0 #使用标签暴露服务 kubectl expose pod web-0 --port=80 --target-port=80 --name=web-0 --selector=pod=0 --type=NodePort #不使用标签暴露服务 kubectl expose pod web-0 --port=80 --target-port=8080 --type=NodePort ~~~ *我自己做的测试,貌似不用打label,也能访问,要用的时候看情况吧* `kubectl expose`还可以使用以资源 pod、service、replication controller(rc)、deployment、replica set(rs) ### 滚动更新 滚动更新由 spec.updateStrategy 字段定义,默认为 RollingUpdate,另一个策略为OnDelete,即删除Pod资源重建以完成更新 ##### 更新镜像 更新镜像可以用: ~~~ kubectl set image statefulset myapp myapp=XXXXXX ~~~ ##### 暂停更新操作 由 spec.update- Strategy.rollingUpdate.partition 字段的值设置为Pod资源的副本数量(至少保证多少个Pod副本可用) ~~~ # 至少保证3个副本可用 kubectl patch statefulset myapp -p '{"spec":{" updateStrategy":{" rollingUpdate":{" partition": 3}}}}' ~~~