在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都是面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Kafka集群、Zookeeper集群等。
这些应用集群有以下一些共同点:
- 每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并且通信。
- 集群的规模是比较固定的,集群规模不能随意变动。
- 集群里的每个节点都是有状态的,通常会持久化数据到永久存储中。
- 如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。
如果用 RC/Deployment 控制 Pod 副本数的方式来实现上述有状态的集群,则我们会发现第一点是无法满足的,因为Pod
的名字是随机产生的,Pod的IP地址也是在运行期才确定且可能有变动的,我们事先无法为每个Pod确定唯一不变的ID,为了能够在其他节点上恢复某个失败的节点,这种集群中的Pod需要挂接某种共享存储,为了解决这个问题,Kubernetes从v1.4版本开始引入了PetSet这个新的资源对象,并且在v1.5版本时更名为StatefulSet
,StatefulSet从本质上来说,可以看作 Deployment/RC 的一个特殊变种,它有如下一些特性。
StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名字叫kafka,那么第一个Pod叫
kafak-0
,第二个Pod叫kafak-1
,第三个叫kafka-2
,以此类推。StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经时运行且准备好的状态。
StatefulSet里的Pod采用稳定的持久化存储卷,通过 PV/PVC 来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。
StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service(无头服务)
配合使用,即在每个StatefulSet的定义中要声明它属于哪个Headless Service。Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例创建了一个DNS域名,这个域名的格式为:
$(podname).$(headless service name)
比如一个3节点的 StatefulSet 集群,对应的 Headless Service 的名字为nginx
,StatefulSet的名字为web
,则 StatefulSet 里面的 3 个 Pod 的 DNS 名称分别为web-0.nginx
、web-1.nginx
、web-2.nginx
,这些DNS名称可以直接在集群的配置文件中固定下来。
示例
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:
selector:
matchLabels:
app: nginx # 必须匹配 .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # 默认值是 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
上述例子中:
- 名为
nginx
的 Headless Service 用来控制网络域名。 volumeClaimTemplates
提供存储。
部署和扩缩保证
对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1。
当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。
在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
在一个 Pod 终止之前,所有的继任者必须完全关闭。
StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds
设置为 0。 这种做法是不安全的。
在上面的 nginx 示例被创建后,会按照 web-0、web-1、web-2 的顺序部署三个 Pod。 在 web-0 进入 Running
和 Ready
状态前不会部署 web-1。在 web-1 进入 Running 和 Ready 状态前不会部署 web-2。 如果 web-1 已经处于 Running 和 Ready 状态,而 web-2 尚未部署,在此期间发生了 web-0 运行失败,那么 web-2 将不会被部署,要等到 web-0 部署完成并进入 Running 和 Ready 状态后,才会部署 web-2。
如果用户想将示例中的 StatefulSet 扩缩为 replicas=1,首先被终止的是 web-2。 在 web-2 没有被完全停止和删除前,web-1 不会被终止。 当 web-2 已被终止和删除、web-1 尚未被终止,如果在此期间发生 web-0 运行失败, 那么就不会终止 web-1,必须等到 web-0 进入 Running 和 Ready 状态后才会终止 web-1。
Pod 管理策略
StatefulSet 允许你放宽其排序保证,通过 .spec.podManagementPolicy
来设置。
顺序 Pod 管理
podManagementPolicy: "OrderedReady"
是默认设置。它保证了Pod顺序。并行 Pod 管理
podManagementPolicy: "Parallel"
让 StatefulSet 控制器并行的启动或终止所有的 Pod,启动或者终止其他 Pod 前,无需等待 Pod 进入 Running 和 ready 或者完全停止状态。 这个选项只会影响扩缩操作的行为,更新不会被影响。
更新策略
StatefulSet 的 .spec.updateStrategy
字段让你可以配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。有两个允许的值:
OnDelete
当 StatefulSet 的.spec.updateStrategy.type
设置为 OnDelete 时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod。RollingUpdate
RollingUpdate 更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。这是默认的更新策略。
最大不可用 Pod
特性状态: Kubernetes v1.24
你可以通过指定 .spec.updateStrategy.rollingUpdate.maxUnavailable
字段来控制更新期间不可用的 Pod 的最大数量。 该值可以是绝对值(例如,“5”)或者是期望 Pod 个数的百分比(例如,10%)。 绝对值是根据百分比值四舍五入计算的。 该字段不能为 0。默认设置为 1。
该字段适用于 0 到 replicas - 1 范围内的所有 Pod。 如果在 0 到 replicas - 1 范围内存在不可用 Pod,这类 Pod 将被计入 maxUnavailable 值。
可以更详细一点吗
好的,我尽快补充,我以为人看的少 :(
感谢感谢
如果能够像deployment介绍一样配个yml就更好了
好的,我今天补全一下这篇文章。
看完了docker再看,思路清晰很多了,这个系列文章看了三四遍才理解了
书读百遍其义自见。
有点看不懂了,只是大概明天他是区别于RC这些无状态服务的,有状态服务的pod的
你的回复有错别字,我也没太理解额。
你可以参考一下:kubernetes有状态和无状态怎么理解?