StatefulSet
是用来管理有状态应用的工作负载 API 对象,也是一种工作负载资源
有状态和无状态
无状态应用:当前应用不会记录状态(网络可能会变、挂载的东西可能会变、顺序可能会变)
有状态应用:需要记录当前状态(网络不变、存储不变、顺序不变)
sts
Pod
集合的部署喝扩缩Pod
提供持久存储和持久标识符对比 deploy
都是管理着有相同容器规约的一组 Pod
sts
给这些 Pod
维护了一个有粘性的 ID,虽然这些 Pod
都是基于相同的规约来创建的,但是不能相互替换,不管怎么调度,每个 Pod
都有一个永久不变的 ID
虽然单个 Pod
会有故障的风险,但是持久的 Pod
标识符使得新旧 Pod
相匹配更加容器
一个来自官方的示例
###改了一下
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:
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: nginx:1.23
运行一下
[root@k8s-01 k8s-yaml]# kubectl apply -f sts.yaml
service/nginx created
statefulset.apps/web created
[root@k8s-01 k8s-yaml]#
StatefulSet 对于需要满足以下一个或多个需求的应用程序很有价值:
参考官方定义:https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/workload-resources/stateful-set-v1/
还是老规矩吧,通过官网加kubectl explain sts
配合着来
spec
[root@k8s-01 k8s-yaml]# 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 控制在初始规模扩展期间、替换节点上的 Pod 或缩减集合规模时如何创建 Pod。 默认策略是 “OrderedReady”,各个 Pod 按升序创建的(pod-0,然后是pod-1 等), 控制器将等到每个 Pod 都准备就绪后再继续。缩小集合规模时,Pod 会以相反的顺序移除。 另一种策略是 “Parallel”,意味着并行创建 Pod 以达到预期的规模而无需等待,并且在缩小规模时将立即删除所有 Pod
podManagementPolicy <string>
podManagementPolicy controls how pods are created during initial scale up,
when replacing pods on nodes, or when scaling down. The default policy is
`OrderedReady`, where pods are created in increasing order (pod-0, then
pod-1, etc) and the controller will wait until each pod is ready before
continuing. When scaling down, the pods are removed in the opposite order.
The alternative policy is `Parallel` which will create pods in parallel to
match the desired scale without waiting, and on scale down will delete all
pods at once.
## replicas 是给定模板的所需的副本数。之所以称作副本,是因为它们是相同模板的实例,不过各个副本也具有一致的身份。如果未指定,则默认为 1。
replicas <integer>
replicas is the desired number of replicas of the given Template. These are
replicas in the sense that they are instantiations of the same Template,
but individual replicas also have a consistent identity. If unspecified,
defaults to 1.
## revisionHistoryLimit 是在 StatefulSet 的修订历史中维护的修订个数上限。 修订历史中包含并非由当前所应用的 StatefulSetSpec 版本未表示的所有修订版本。默认值为 10。
revisionHistoryLimit <integer>
revisionHistoryLimit is the maximum number of revisions that will be
maintained in the StatefulSet's revision history. The revision history
consists of all revisions not represented by a currently applied
StatefulSetSpec version. The default value is 10.
## selector 是对 Pod 的标签查询,查询结果应该匹配副本个数。 此选择算符必须与 Pod 模板中的 label 匹配
selector s labels. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
## serviceName 是管理此 StatefulSet 服务的名称。 该服务必须在 StatefulSet 之前即已存在,并负责该集合的网络标识。 Pod 会获得符合以下模式的 DNS/主机名: pod-specific-string.serviceName.default.svc.cluster.local。 其中 “pod-specific-string” 由 StatefulSet 控制器管理。
serviceName <string> -required-
serviceName is the name of the service that governs this StatefulSet. This
service must exist before the StatefulSet, and is responsible for the
network identity of the set. Pods get DNS/hostnames that follow the
pattern: pod-specific-string.serviceName.default.svc.cluster.local where
"pod-specific-string" is managed by the StatefulSet controller.
## template 是用来描述 Pod 的对象,检测到副本不足时将创建所描述的 Pod。 经由 StatefulSet 创建的每个 Pod 都将满足这个模板,但与 StatefulSet 的其余 Pod 相比,每个 Pod 具有唯一的标识。 每个 Pod 将以 - 格式命名。 例如,名为 "web" 且索引号为 "3" 的 StatefulSet 中的 Pod 将被命名为 "web-3"。
template <Object> -required-
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.
## updateStrategy 是一个 StatefulSetUpdateStrategy,表示当对 template 进行修订时,用何种策略更新 StatefulSet 中的 Pod 集合。
updateStrategy <Object>
updateStrategy indicates the StatefulSetUpdateStrategy that will be
employed to update Pods in the StatefulSet when a revision is made to
Template.
## volumeClaimTemplates 是允许 Pod 引用的申领列表。 StatefulSet controller 负责以维持 Pod 身份不变的方式将网络身份映射到申领之上。 此列表中的每个申领至少必须在模板的某个容器中存在匹配的(按 name 匹配)volumeMount。 此列表中的申领优先于模板中具有相同名称的所有卷。
volumeClaimTemplates <[]Object>
volumeClaimTemplates is a list of claims that pods are allowed to
reference. The StatefulSet controller is responsible for mapping network
identities to claims in a way that maintains the identity of a pod. Every
claim in this list must have at least one matching (by name) volumeMount in
one container in the template. A claim in this list takes precedence over
any volumes in the template, with the same name.
###headless Services
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
下面是来自官方的一个 yaml
示例
apiVersion: v1 ##这是一个Headless Services的示例:名为nginx,来控制网络域名,写法都差不多
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet ## 下面是一个sts的写法
metadata:
name: web ## stsd的名字为web
spec:
selector:
matchLabels:
app: nginx ## 必须匹配 .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 ## 默认值是 1:副本数
minReadySeconds: 10
template: ##pod模板
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
StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。 该标识和 Pod 是绑定的,与该 Pod 调度到哪个节点上无关
有序索引
对于具有 N 个副本的 StatefulSet,该 StatefulSet 中的每个 Pod 将被分配一个整数序号, 该序号在此 StatefulSet 上是唯一的。默认情况下,这些 Pod 将被从 0 到 N-1 的序号。就比如我上面的部署例子
稳定的网络 ID
我们准备一次部署
apiVersion: v1 ##这是一个Headless Services的示例:名为nginx,来控制网络域名,写法都差不多
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet ## 下面是一个sts的写法
metadata:
name: web ## stsd的名字为web
spec:
selector:
matchLabels:
app: nginx ## 必须匹配 .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 ## 默认值是 1:副本数
template: ##pod模板
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx:1.23
我们进容器修改一下默认页面
$(服务名称).$(名字空间).svc.cluster.local
,其中 cluster.local
是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为: $(pod 名称).$(所属服务的 DNS 域名)
,其中所属服务由 StatefulSet 的 serviceName
域来设定Pod
:web-0 来验证稳定的存储
对于 StatefulSet 中定义的每个 VolumeClaimTemplate,每个 Pod 接收到一个 PersistentVolumeClaim。 在上面的 nginx 示例中,每个 Pod 将会得到基于 StorageClass
my-storage-class
制备的 1 Gib 的 PersistentVolume。 如果没有声明 StorageClass,就会使用默认的 StorageClass。 当一个 Pod 被调度(重新调度)到节点上时,它的volumeMounts
会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。 请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。
这个我后面学习存储在进行一个学习
部署和扩缩保证
0..N-1
。N-1..0
。StatefulSet 不应将
pod.Spec.TerminationGracePeriodSeconds
设置为 0。照 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 管理策略
OrderedReady Pod 管理
并行 Pod 管理:让 StatefulSet 控制器并行的启动或终止所有的 Pod, 启动或者终止其他 Pod 前,无需等待 Pod 进入 Running 和 Ready 或者完全停止状态。 这个选项只会影响扩缩操作的行为,更新则不会被影响。
更新策略
StatefulSet 的 .spec.updateStrategy
字段让你可以配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。有两个允许的值:
OnDelete
当 StatefulSet 的 .spec.updateStrategy.type
设置为 OnDelete
时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod,以此来对 StatefulSet 的 .spec.template
的变动作出反应。
RollingUpdate
RollingUpdate
更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。这是默认的更新策略。
滚动更新
更新策略:RollingUpdate( StatefulSet 控制器会删除和重建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。)
分区滚动更新
通过声明
.spec.updateStrategy.rollingUpdate.partition
的方式,RollingUpdate
更新策略可以实现分区。 如果声明了一个分区,当 StatefulSet 的.spec.template
被更新时, 所有序号大于等于该分区序号的 Pod 都会被更新。 所有序号小于该分区序号的 Pod 都不会被更新,并且,即使它们被删除也会依据之前的版本进行重建。 如果 StatefulSet 的.spec.updateStrategy.rollingUpdate.partition
大于它的.spec.replicas
,则对它的.spec.template
的更新将不会传递到它的 Pod。 在大多数情况下,你不需要使用分区,但如果你希望进行阶段更新、执行金丝雀或执行分阶段上线,则这些分区会非常有用。
剩下的特性内容我将在具体使用中操作体验
我们创建一个demo
####这里是一个Headless Service
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:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.23
追踪一下Pod启动状态
kubectl get pod,sts,svc
我们从上面追踪的一个Pod启动顺序可以看到
对于一个拥有 n 个副本的 StatefulSet,Pod 被部署时是按照 {0…n-1} 的序号顺序创建的
web-0
Pod 处于 Running 并 Ready 状态后,web-1
Pod 才会被启动-<序号索引>
。 web
StatefulSet 拥有三个副本,所以它创建了两个 Pod:web-0
和 web-1
、web-2
每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用
kubectl exec
在每个 Pod 中执行hostname
验证一下
进入 web-2,安装一下 dnsutils
包,这个包含有 nslookup
命令
在容器内部进行一个验证
nslookup web-0.nginx
nslookup web-1.nginx
nslookup web-2.nginx
headless service 的 CNAME 指向 SRV 记录(记录每个 Running 和 Ready 状态的 Pod)。 SRV 记录指向一个包含 Pod IP 地址的记录表项。
可以看到重新拉起的一个pod,仍可以通过统一的网络标识进行访问,即使它的内部IP地址发生了改变
对于稳定的网络标识
Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变,这就是为什么不要在其他应用中使用 StatefulSet 中 Pod 的 IP 地址进行连接,这点很重要。
扩容/缩容 StatefulSet 指增加或减少它的副本数。这通过更新
replicas
字段完成。
扩
修改一个demo文件
replicas: 5
更新一下:由3个副本扩到了5个
缩
在修改一下demo文件
replicas: 2
更新一下:由5个缩到2个副本
更新策略由 StatefulSet API 对象的 spec.updateStrategy
字段决定。这个特性能够用来更新一个 StatefulSet 中 Pod 的的容器镜像、资源请求和限制、标签和注解。
RollingUpdate
更新策略是 StatefulSet 默认策略。滚动更新
RollingUpdate
更新策略会更新一个 StatefulSet 中的所有 Pod,采用与序号索引相反的顺序并遵循 StatefulSet 的保证
继续修改demo
##先把副本数改回5,这样状态观察明显一点
##再更新一下镜像
...
image: nginx:1.23.3
...
更新一下试试
这是默认的更新策略效果
StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器终止每个 Pod 并等待它们变成 Running 和 Ready。 请注意,虽然在顺序后继者变成 Running 和 Ready 之前 StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod,使用的是它们当前的版本。
已经接收到更新请求的 Pod 将会被恢复为更新的版本,没有收到请求的 Pod 则会被恢复为之前的版本。 像这样,控制器尝试继续使应用保持健康并在出现间歇性故障时保持更新的一致性。
#更新记录过程
[root@k8s-01 ~]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 22m
web-1 1/1 Running 0 48m
web-2 1/1 Running 0 99s
web-3 1/1 Running 0 97s
web-4 1/1 Running 0 96s
web-4 1/1 Terminating 0 104s ##停止最后一个
web-4 0/1 Terminating 0 104s
web-4 0/1 Terminating 0 105s
web-4 0/1 Terminating 0 105s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 0/1 ContainerCreating 0 1s
web-4 1/1 Running 0 2s ##最后一个更新好了,再停止倒数第二个
web-3 1/1 Terminating 0 108s ##依次类推
web-3 0/1 Terminating 0 109s
web-3 0/1 Terminating 0 110s
web-3 0/1 Terminating 0 110s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 0/1 ContainerCreating 0 1s
web-3 1/1 Running 0 1s
web-2 1/1 Terminating 0 113s
web-2 0/1 Terminating 0 114s
web-2 0/1 Terminating 0 2m8s
web-2 0/1 Terminating 0 2m8s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 0s
web-1 1/1 Terminating 0 48m
web-1 0/1 Terminating 0 48m
web-1 0/1 Terminating 0 48m
web-1 0/1 Terminating 0 48m
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 1s
web-0 1/1 Terminating 0 23m
web-0 0/1 Terminating 0 23m
web-0 0/1 Terminating 0 23m
web-0 0/1 Terminating 0 23m
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 1s
查看滚动更新状态
kubectl rollout status sts web
可以使用
RollingUpdate
更新策略的partition
参数来分段更新一个 StatefulSet。 分段的更新将会使 StatefulSet 中的其余所有 Pod 保持当前版本的同时允许改变 StatefulSet 的.spec.template
修改一下demo文件
####这里是一个Headless Service
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:
serviceName: "nginx"
replicas: 5
selector:
matchLabels:
app: nginx
updateStrategy: ## 这里设置的就是分段更新
type: RollingUpdate
rollingUpdate:
partition: 3 ##更新大于等于这个索引的pod,这里是3,就更新的是web-04\web-03
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.23
更新一下:这次更新的镜像版本回到1.23了
###更新追踪记录
[root@k8s-01 ~]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 35m
web-1 1/1 Running 0 35m
web-2 1/1 Running 0 35m
web-3 1/1 Running 0 36m
web-4 1/1 Running 0 36m
web-4 1/1 Terminating 0 37m
web-4 0/1 Terminating 0 37m
web-4 0/1 Terminating 0 38m
web-4 0/1 Terminating 0 38m
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 0/1 ContainerCreating 0 1s
web-4 1/1 Running 0 2s
web-3 1/1 Terminating 0 37m
web-3 0/1 Terminating 0 37m
web-3 0/1 Terminating 0 38m
web-3 0/1 Terminating 0 38m
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 1/1 Running 0 1s
非级联删除
kubectl delete statefulset web --cascade=orphan
级联删除
kubectl delete statefulset web
有关例子的更多场景可以参考:https://kubernetes.io/zh-cn/docs/tutorials/stateful-application/basic-stateful-set/