StatefulSet 基础知识
本教程介绍了使用 StatefulSets 管理应用程序。它演示了如何创建、删除、扩展和更新 StatefulSets 的 Pod。
开始之前
在开始本教程之前,您应该熟悉以下 Kubernetes 概念
您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,可以使用 minikube 创建一个,或者您可以使用以下 Kubernetes 游乐场之一
您应该将 kubectl
配置为使用使用 default
命名空间的上下文。如果您使用的是现有集群,请确保可以使用该集群的默认命名空间进行练习。理想情况下,在不运行任何实际工作负载的集群中进行练习。
阅读有关 StatefulSets 的概念页面也很有用。
注意
本教程假设您的集群已配置为动态供应持久卷。您还需要拥有一个 默认存储类。如果您的集群未配置为动态供应存储,则必须在本教程开始之前手动供应两个 1 GiB 卷,并设置您的集群,以便这些持久卷映射到 StatefulSet 定义的持久卷声明模板。目标
StatefulSets 旨在与有状态应用程序和分布式系统一起使用。但是,在 Kubernetes 上管理有状态应用程序和分布式系统是一个广泛而复杂的话题。为了演示 StatefulSet 的基本功能,并且不将前者与后者混淆,您将使用 StatefulSet 部署一个简单的 Web 应用程序。
在本教程之后,您将熟悉以下内容。
- 如何创建 StatefulSet
- StatefulSet 如何管理其 Pod
- 如何删除 StatefulSet
- 如何扩展 StatefulSet
- 如何更新 StatefulSet 的 Pod
创建 StatefulSet
首先使用下面的示例创建 StatefulSet(以及它依赖的服务)。它类似于 StatefulSets 概念中介绍的示例。它创建一个 无头服务,nginx
,以发布 StatefulSet 中 Pod 的 IP 地址,web
。
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: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.21
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
您需要使用至少两个终端窗口。在第一个终端中,使用 kubectl get
观察 StatefulSet 的 Pod 的创建。
# use this terminal to run commands that specify --watch
# end this watch when you are asked to start a new watch
kubectl get pods --watch -l app=nginx
在第二个终端中,使用 kubectl apply
创建无头服务和 StatefulSet
kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created
上面的命令创建了两个 Pod,每个 Pod 运行一个 NGINX Web 服务器。获取 nginx
服务...
kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 12s
...然后获取 web
StatefulSet,以验证两者是否已成功创建
kubectl get statefulset web
NAME READY AGE
web 2/2 37s
有序 Pod 创建
StatefulSet 默认以严格的顺序创建其 Pod。
对于具有 n 个副本的 StatefulSet,当 Pod 部署时,它们会按顺序创建,从 {0..n-1} 排序。检查第一个终端中 kubectl get
命令的输出。最终,输出将类似于下面的示例。
# Do not start a new watch;
# this should already be running
kubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 19s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 18s
请注意,web-1
Pod 只有在 web-0
Pod 处于 运行 状态(请参阅 Pod 阶段)且 就绪 状态(请参阅 Pod 条件 中的 type
)时才会启动。
在本教程的后面,您将练习 并行启动。
注意
要配置分配给 StatefulSet 中每个 Pod 的整数序号,请参阅 开始序号。StatefulSet 中的 Pod
StatefulSet 中的 Pod 具有唯一的序号索引和稳定的网络标识。
检查 Pod 的序号索引
获取 StatefulSet 的 Pod
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 1m
web-1 1/1 Running 0 1m
如 StatefulSets 概念中所述,StatefulSet 中的 Pod 具有粘性的唯一标识。此标识基于 StatefulSet 控制器 分配给每个 Pod 的唯一序号索引。
Pod 的名称采用 <statefulset name>-<ordinal index>
的形式。由于 web
StatefulSet 具有两个副本,因此它创建了两个 Pod,web-0
和 web-1
。
使用稳定的网络标识
每个 Pod 都有一个基于其序号索引的稳定主机名。使用 kubectl exec
在每个 Pod 中执行 hostname
命令
for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1
使用 kubectl run
执行一个容器,该容器从 dnsutils
包中提供 nslookup
命令。在 Pod 的主机名上使用 nslookup
,您可以检查其集群内 DNS 地址
kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
这将启动一个新的 shell。在那个新的 shell 中,运行
# Run this in the dns-test container shell
nslookup web-0.nginx
输出类似于
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.244.1.6
nslookup web-1.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-1.nginx
Address 1: 10.244.2.6
(现在退出容器 shell:exit
)
无头服务的 CNAME 指向 SRV 记录(每个处于运行状态且就绪状态的 Pod 一个)。SRV 记录指向包含 Pod 的 IP 地址的 A 记录条目。
在一个终端中,观察 StatefulSet 的 Pod
# Start a new watch
# End this watch when you've seen that the delete is finished
kubectl get pod --watch -l app=nginx
在另一个终端中,使用 kubectl delete
删除 StatefulSet 中的所有 Pod
kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted
等待 StatefulSet 重新启动它们,并等待两个 Pod 都过渡到运行状态和就绪状态
# This should already be running
kubectl get pod --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 0s
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 34s
使用 kubectl exec
和 kubectl run
查看 Pod 的主机名和集群内 DNS 条目。首先,查看 Pod 的主机名
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1
然后,运行
kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
这将启动一个新的 shell。
在那个新的 shell 中,运行
# Run this in the dns-test container shell
nslookup web-0.nginx
输出类似于
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.244.1.7
nslookup web-1.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-1.nginx
Address 1: 10.244.2.8
(现在退出容器 shell:exit
)
Pod 的序号、主机名、SRV 记录和 A 记录名称没有改变,但与 Pod 关联的 IP 地址可能已改变。在本教程中使用的集群中,它们已经改变了。这就是为什么不要将其他应用程序配置为通过特定 Pod 的 IP 地址连接到 StatefulSet 中的 Pod 很重要的原因(通过解析其主机名连接到 Pod 是可以的)。
在 StatefulSet 中发现特定 Pod
如果您需要查找并连接到 StatefulSet 的活动成员,您应该查询无头服务的 CNAME(nginx.default.svc.cluster.local
)。与 CNAME 关联的 SRV 记录将仅包含 StatefulSet 中处于运行状态且就绪状态的 Pod。
如果您的应用程序已经实现了测试活动性和就绪性的连接逻辑,您可以使用 Pod 的 SRV 记录(web-0.nginx.default.svc.cluster.local
、web-1.nginx.default.svc.cluster.local
),因为它们是稳定的,并且您的应用程序将能够在 Pod 过渡到运行状态和就绪状态时发现 Pod 的地址。
如果您的应用程序想要在 StatefulSet 中找到任何健康的 Pod,因此不需要跟踪每个特定的 Pod,您也可以连接到由该 StatefulSet 中的 Pod 支持的 type: ClusterIP
服务的 IP 地址。您可以使用与跟踪 StatefulSet 的相同服务(在 StatefulSet 的 serviceName
中指定)或选择正确 Pod 集的单独服务。
写入稳定存储
获取 web-0
和 web-1
的持久卷声明
kubectl get pvc -l app=nginx
输出类似于
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
www-web-0 Bound pvc-15c268c7-b507-11e6-932f-42010a800002 1Gi RWO 48s
www-web-1 Bound pvc-15c79307-b507-11e6-932f-42010a800002 1Gi RWO 48s
StatefulSet 控制器创建了两个 PersistentVolumeClaims,它们绑定到两个 PersistentVolumes。
由于本教程中使用的集群配置为动态配置 PersistentVolumes,因此 PersistentVolumes 会自动创建和绑定。
NGINX Web 服务器默认情况下会从 /usr/share/nginx/html/index.html
提供索引文件。StatefulSet 的 spec
中的 volumeMounts
字段确保 /usr/share/nginx/html
目录由 PersistentVolume 支持。
将 Pod 的主机名写入其 index.html
文件,并验证 NGINX Web 服务器是否提供主机名
for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; done
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
注意
如果您看到上述 curl 命令的 403 Forbidden 响应,则需要修复 volumeMounts
挂载的目录的权限(由于 使用 hostPath 卷时的错误),方法是运行
for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
然后重试上述 curl
命令。
在一个终端中,观察 StatefulSet 的 Pod
# End this watch when you've reached the end of the section.
# At the start of "Scaling a StatefulSet" you'll start a new watch.
kubectl get pod --watch -l app=nginx
在第二个终端中,删除所有 StatefulSet 的 Pod
kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted
检查第一个终端中 kubectl get
命令的输出,并等待所有 Pod 转变为 Running 和 Ready。
# This should already be running
kubectl get pod --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 0s
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 34s
验证 Web 服务器是否继续提供其主机名
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
即使 web-0
和 web-1
已重新调度,它们仍会继续提供其主机名,因为与其 PersistentVolumeClaims 关联的 PersistentVolumes 会重新挂载到其 volumeMounts
。无论 web-0
和 web-1
调度到哪个节点,其 PersistentVolumes 都将挂载到相应的挂载点。
扩展 StatefulSet
扩展 StatefulSet 指的是增加或减少副本数量(水平扩展)。这可以通过更新 replicas
字段来实现。您可以使用 kubectl scale
或 kubectl patch
来扩展 StatefulSet。
向上扩展
向上扩展意味着添加更多副本。如果您的应用程序能够在 StatefulSet 中分配工作,那么新的更大的 Pod 集可以执行更多工作。
在一个终端窗口中,观察 StatefulSet 中的 Pod
# If you already have a watch running, you can continue using that.
# Otherwise, start one.
# End this watch when there are 5 healthy Pods for the StatefulSet
kubectl get pods --watch -l app=nginx
在另一个终端窗口中,使用 kubectl scale
将副本数量扩展到 5
kubectl scale sts web --replicas=5
statefulset.apps/web scaled
检查第一个终端中 kubectl get
命令的输出,并等待三个额外的 Pod 转变为 Running 和 Ready。
# This should already be running
kubectl get pod --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2h
web-1 1/1 Running 0 2h
NAME READY STATUS RESTARTS AGE
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 19s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 1/1 Running 0 18s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 1/1 Running 0 19s
StatefulSet 控制器扩展了副本数量。与 StatefulSet 创建 一样,StatefulSet 控制器按顺序创建每个 Pod,并等待每个 Pod 的前一个 Pod 处于 Running 和 Ready 状态,然后再启动后续 Pod。
向下扩展
向下扩展意味着减少副本数量。例如,您可能这样做是因为服务流量水平下降,并且在当前规模下存在闲置资源。
在一个终端中,观察 StatefulSet 的 Pod
# End this watch when there are only 3 Pods for the StatefulSet
kubectl get pod --watch -l app=nginx
在另一个终端中,使用 kubectl patch
将 StatefulSet 缩减回三个副本
kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched
等待 web-4
和 web-3
转变为 Terminating。
# This should already be running
kubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 3h
web-1 1/1 Running 0 3h
web-2 1/1 Running 0 55s
web-3 1/1 Running 0 36s
web-4 0/1 ContainerCreating 0 18s
NAME READY STATUS RESTARTS AGE
web-4 1/1 Running 0 19s
web-4 1/1 Terminating 0 24s
web-4 1/1 Terminating 0 24s
web-3 1/1 Terminating 0 42s
web-3 1/1 Terminating 0 42s
有序 Pod 终止
控制平面一次删除一个 Pod,并按其序号索引的相反顺序进行,并且它会等待每个 Pod 完全关闭,然后再删除下一个 Pod。
获取 StatefulSet 的 PersistentVolumeClaims
kubectl get pvc -l app=nginx
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
www-web-0 Bound pvc-15c268c7-b507-11e6-932f-42010a800002 1Gi RWO 13h
www-web-1 Bound pvc-15c79307-b507-11e6-932f-42010a800002 1Gi RWO 13h
www-web-2 Bound pvc-e1125b27-b508-11e6-932f-42010a800002 1Gi RWO 13h
www-web-3 Bound pvc-e1176df6-b508-11e6-932f-42010a800002 1Gi RWO 13h
www-web-4 Bound pvc-e11bb5f8-b508-11e6-932f-42010a800002 1Gi RWO 13h
仍然有五个 PersistentVolumeClaims 和五个 PersistentVolumes。当探索 Pod 的 稳定存储 时,您会看到,当 StatefulSet 的 Pod 被删除时,挂载到 StatefulSet 的 Pod 的 PersistentVolumes 不会被删除。当 Pod 删除是由缩减 StatefulSet 引起的时,这一点仍然成立。
更新 StatefulSets
StatefulSet 控制器支持自动更新。使用的策略由 StatefulSet API 对象的 spec.updateStrategy
字段决定。此功能可用于升级 StatefulSet 中 Pod 的容器镜像、资源请求和/或限制、标签和注释。
有两种有效的更新策略,RollingUpdate
(默认)和 OnDelete
。
RollingUpdate
RollingUpdate
更新策略将更新 StatefulSet 中的所有 Pod,并按相反的序号顺序进行,同时尊重 StatefulSet 保证。
您可以通过指定 .spec.updateStrategy.rollingUpdate.partition
将使用 RollingUpdate
策略的 StatefulSet 的更新拆分为分区。您将在本教程的后面练习这一点。
首先,尝试一个简单的滚动更新。
在一个终端窗口中,修补 web
StatefulSet 以再次更改容器镜像
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.24"}]'
statefulset.apps/web patched
在另一个终端中,观察 StatefulSet 中的 Pod
# End this watch when the rollout is complete
#
# If you're not sure, leave it running one more minute
kubectl get pod -l app=nginx --watch
输出类似于
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 7m
web-1 1/1 Running 0 7m
web-2 1/1 Running 0 8m
web-2 1/1 Terminating 0 8m
web-2 1/1 Terminating 0 8m
web-2 0/1 Terminating 0 8m
web-2 0/1 Terminating 0 8m
web-2 0/1 Terminating 0 8m
web-2 0/1 Terminating 0 8m
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 19s
web-1 1/1 Terminating 0 8m
web-1 0/1 Terminating 0 8m
web-1 0/1 Terminating 0 8m
web-1 0/1 Terminating 0 8m
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 6s
web-0 1/1 Terminating 0 7m
web-0 1/1 Terminating 0 7m
web-0 0/1 Terminating 0 7m
web-0 0/1 Terminating 0 7m
web-0 0/1 Terminating 0 7m
web-0 0/1 Terminating 0 7m
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 10s
StatefulSet 中的 Pod 按相反的序号顺序更新。StatefulSet 控制器会终止每个 Pod,并等待它转变为 Running 和 Ready 状态,然后再更新下一个 Pod。请注意,即使 StatefulSet 控制器在它的序号后继者处于 Running 和 Ready 状态之前不会继续更新下一个 Pod,它也会将更新过程中失败的任何 Pod 还原到该 Pod 的现有版本。
已经收到更新的 Pod 将恢复到更新后的版本,尚未收到更新的 Pod 将恢复到以前的版本。通过这种方式,控制器试图在出现间歇性故障的情况下继续保持应用程序健康和更新一致。
获取 Pod 以查看其容器镜像
for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.24
registry.k8s.io/nginx-slim:0.24
registry.k8s.io/nginx-slim:0.24
StatefulSet 中的所有 Pod 现在都运行着以前的容器镜像。
注意
您还可以使用kubectl rollout status sts/<name>
查看对 StatefulSet 的滚动更新的状态暂存更新
您可以通过指定 .spec.updateStrategy.rollingUpdate.partition
将使用 RollingUpdate
策略的 StatefulSet 的更新拆分为分区。
有关更多上下文,您可以阅读 StatefulSet 概念页面中的 分区滚动更新。
您可以通过在 .spec.updateStrategy.rollingUpdate
中使用 partition
字段来暂存对 StatefulSet 的更新。对于此更新,您将保持 StatefulSet 中的现有 Pod 不变,同时更改 StatefulSet 的 Pod 模板。然后,您可以(或者,在教程之外,一些外部自动化)触发该准备好的更新。
首先,修补 web
StatefulSet 以将分区添加到 updateStrategy
字段
# The value of "partition" determines which ordinals a change applies to
# Make sure to use a number bigger than the last ordinal for the
# StatefulSet
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched
再次修补 StatefulSet 以更改此 StatefulSet 使用的容器镜像
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.21"}]'
statefulset.apps/web patched
删除 StatefulSet 中的 Pod
kubectl delete pod web-2
pod "web-2" deleted
等待替换的 web-2
Pod 处于 Running 和 Ready 状态
# End the watch when you see that web-2 is healthy
kubectl get pod -l app=nginx --watch
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m
web-1 1/1 Running 0 4m
web-2 0/1 ContainerCreating 0 11s
web-2 1/1 Running 0 18s
获取 Pod 的容器镜像
kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24
请注意,即使更新策略是 RollingUpdate
,StatefulSet 也会使用原始容器镜像还原 Pod。这是因为 Pod 的序号小于 updateStrategy
指定的 partition
。
推出金丝雀
您现在将尝试对该暂存更改进行 金丝雀推出。
您可以通过减少您在 上面 指定的 partition
来推出金丝雀(以测试修改后的模板)。
修补 StatefulSet 以减少分区
# The value of "partition" should match the highest existing ordinal for
# the StatefulSet
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched
控制平面触发 web-2
的替换(通过优雅的 删除 实现,并在删除完成后创建新的 Pod)。等待新的 web-2
Pod 处于 Running 和 Ready 状态。
# This should already be running
kubectl get pod -l app=nginx --watch
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m
web-1 1/1 Running 0 4m
web-2 0/1 ContainerCreating 0 11s
web-2 1/1 Running 0 18s
获取 Pod 的容器
kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.21
当您更改 partition
时,StatefulSet 控制器会自动更新 web-2
Pod,因为 Pod 的序号大于或等于 partition
。
删除 web-1
Pod
kubectl delete pod web-1
pod "web-1" deleted
等待 web-1
Pod 处于 Running 和 Ready 状态。
# This should already be running
kubectl get pod -l app=nginx --watch
输出类似于
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 6m
web-1 0/1 Terminating 0 6m
web-2 1/1 Running 0 2m
web-1 0/1 Terminating 0 6m
web-1 0/1 Terminating 0 6m
web-1 0/1 Terminating 0 6m
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 18s
获取 web-1
Pod 的容器镜像
kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24
web-1
已恢复到其原始配置,因为 Pod 的序号小于分区。当指定分区时,所有序号大于或等于分区的 Pod 都将在 StatefulSet 的 .spec.template
更新时更新。如果序号小于分区的 Pod 被删除或以其他方式终止,它将恢复到其原始配置。
分阶段推出
您可以使用分区滚动更新以类似于推出 金丝雀 的方式执行分阶段推出(例如,线性、几何或指数推出)。要执行分阶段推出,请将 partition
设置为您希望控制器暂停更新的序号。
分区当前设置为 2
。将分区设置为 0
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched
等待 StatefulSet 中的所有 Pod 处于 Running 和 Ready 状态。
# This should already be running
kubectl get pod -l app=nginx --watch
输出类似于
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 3m
web-1 0/1 ContainerCreating 0 11s
web-2 1/1 Running 0 2m
web-1 1/1 Running 0 18s
web-0 1/1 Terminating 0 3m
web-0 1/1 Terminating 0 3m
web-0 0/1 Terminating 0 3m
web-0 0/1 Terminating 0 3m
web-0 0/1 Terminating 0 3m
web-0 0/1 Terminating 0 3m
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 3s
获取 StatefulSet 中 Pod 的容器镜像详细信息
for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.21
registry.k8s.io/nginx-slim:0.21
registry.k8s.io/nginx-slim:0.21
通过将 partition
移动到 0
,您允许 StatefulSet 继续更新过程。
OnDelete
您可以通过将 .spec.template.updateStrategy.type
设置为 OnDelete
来为 StatefulSet 选择此更新策略。
修补 web
StatefulSet 以使用 OnDelete
更新策略
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
statefulset.apps/web patched
当您选择此更新策略时,StatefulSet 控制器不会在对 StatefulSet 的 .spec.template
字段进行修改时自动更新 Pod。您需要自己管理推出 - 手动或使用单独的自动化。
删除 StatefulSets
StatefulSet 支持非级联和级联删除。在非级联 删除 中,当 StatefulSet 被删除时,StatefulSet 的 Pod 不会被删除。在级联 删除 中,StatefulSet 及其 Pod 都将被删除。
阅读 在集群中使用级联删除 以了解级联删除的一般知识。
非级联删除
在一个终端窗口中,观察 StatefulSet 中的 Pod。
# End this watch when there are no Pods for the StatefulSet
kubectl get pods --watch -l app=nginx
使用 kubectl delete
删除 StatefulSet。确保为命令提供 --cascade=orphan
参数。此参数告诉 Kubernetes 仅删除 StatefulSet,并且 不 删除其任何 Pod。
kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted
获取 Pod 并检查其状态
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 6m
web-1 1/1 Running 0 7m
web-2 1/1 Running 0 5m
即使 web
已删除,所有 Pod 仍然处于运行和就绪状态。删除 web-0
kubectl delete pod web-0
pod "web-0" deleted
获取 StatefulSet 的 Pod
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-1 1/1 Running 0 10m
web-2 1/1 Running 0 7m
由于 web
StatefulSet 已删除,web-0
未重新启动。
在一个终端中,观察 StatefulSet 的 Pod。
# Leave this watch running until the next time you start a watch
kubectl get pods --watch -l app=nginx
在第二个终端中,重新创建 StatefulSet。请注意,除非您删除了 nginx
服务(您不应该这样做),否则您将看到一个错误,指示该服务已存在。
kubectl apply -f https://k8s.io/examples/application/web/web.yaml
statefulset.apps/web created
service/nginx unchanged
忽略该错误。它只表示尝试创建 nginx 无头服务,即使该服务已存在。
检查第一个终端中运行的 kubectl get
命令的输出。
# This should already be running
kubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 2m
NAME READY STATUS RESTARTS AGE
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 18s
web-2 1/1 Terminating 0 3m
web-2 0/1 Terminating 0 3m
web-2 0/1 Terminating 0 3m
web-2 0/1 Terminating 0 3m
当 web
StatefulSet 重新创建时,它首先重新启动 web-0
。由于 web-1
已经处于运行和就绪状态,当 web-0
转变为运行和就绪状态时,它采用了此 Pod。由于您使用 replicas
等于 2 重新创建了 StatefulSet,因此一旦 web-0
重新创建,并且一旦确定 web-1
已经处于运行和就绪状态,web-2
就会被终止。
现在再次查看 Pod 的 Web 服务器提供的 index.html
文件的内容
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
即使您删除了 StatefulSet 和 web-0
Pod,它仍然提供最初输入其 index.html
文件的主机名。这是因为 StatefulSet 从未删除与 Pod 关联的持久卷。当您重新创建 StatefulSet 并重新启动 web-0
时,其原始持久卷被重新挂载。
级联删除
在一个终端窗口中,观察 StatefulSet 中的 Pod。
# Leave this running until the next page section
kubectl get pods --watch -l app=nginx
在另一个终端中,再次删除 StatefulSet。这次,省略 --cascade=orphan
参数。
kubectl delete statefulset web
statefulset.apps "web" deleted
检查第一个终端中运行的 kubectl get
命令的输出,并等待所有 Pod 转变为终止状态。
# This should already be running
kubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 11m
web-1 1/1 Running 0 27m
NAME READY STATUS RESTARTS AGE
web-0 1/1 Terminating 0 12m
web-1 1/1 Terminating 0 29m
web-0 0/1 Terminating 0 12m
web-0 0/1 Terminating 0 12m
web-0 0/1 Terminating 0 12m
web-1 0/1 Terminating 0 29m
web-1 0/1 Terminating 0 29m
web-1 0/1 Terminating 0 29m
正如您在 缩容 部分中看到的那样,Pod 是按照其序数索引的逆序依次终止的。在终止 Pod 之前,StatefulSet 控制器会等待 Pod 的后继者完全终止。
注意
虽然级联删除会删除 StatefulSet 及其 Pod,但级联不会删除与 StatefulSet 关联的无头服务。您必须手动删除nginx
服务。kubectl delete service nginx
service "nginx" deleted
再次重新创建 StatefulSet 和无头服务
kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created
当 StatefulSet 的所有 Pod 转变为运行和就绪状态时,检索其 index.html
文件的内容
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
即使您完全删除了 StatefulSet 及其所有 Pod,Pod 也会使用其挂载的持久卷重新创建,并且 web-0
和 web-1
继续提供其主机名。
最后,删除 nginx
服务...
kubectl delete service nginx
service "nginx" deleted
...以及 web
StatefulSet
kubectl delete statefulset web
statefulset "web" deleted
Pod 管理策略
对于某些分布式系统,StatefulSet 排序保证是不必要和/或不可取的。这些系统只需要唯一性和标识。
您可以指定 Pod 管理策略 来避免这种严格的排序;可以是 OrderedReady
(默认值)或 Parallel
。
OrderedReady Pod 管理
OrderedReady
Pod 管理是 StatefulSet 的默认值。它告诉 StatefulSet 控制器尊重上面演示的排序保证。
当您的应用程序需要或期望更改(例如推出应用程序的新版本)按 StatefulSet 提供的序数(Pod 编号)的严格顺序发生时,请使用此方法。换句话说,如果您有 Pod app-0
、app-1
和 app-2
,Kubernetes 将首先更新 app-0
并检查它。一旦检查通过,Kubernetes 就会更新 app-1
,最后更新 app-2
。
如果您添加了两个 Pod,Kubernetes 将设置 app-3
并等待它变为健康状态,然后再部署 app-4
。
由于这是默认设置,因此您已经练习过使用它。
Parallel Pod 管理
另一种方法,Parallel
Pod 管理,告诉 StatefulSet 控制器并行启动或终止所有 Pod,并且在启动或终止另一个 Pod 之前,不要等待 Pod 变为 Running
和 Ready
或完全终止。
Parallel
Pod 管理选项仅影响缩放操作的行为。更新不受影响;Kubernetes 仍然按顺序推出更改。对于本教程,该应用程序非常简单:一个 Web 服务器,它会告诉您其主机名(因为这是一个 StatefulSet,每个 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:
serviceName: "nginx"
podManagementPolicy: "Parallel"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.24
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
此清单与您上面下载的清单相同,只是 web
StatefulSet 的 .spec.podManagementPolicy
设置为 Parallel
。
在一个终端中,观察 StatefulSet 中的 Pod。
# Leave this watch running until the end of the section
kubectl get pod -l app=nginx --watch
在另一个终端中,为 Parallel
Pod 管理重新配置 StatefulSet
kubectl apply -f https://k8s.io/examples/application/web/web-parallel.yaml
service/nginx updated
statefulset.apps/web updated
保持运行观察的终端打开。在另一个终端窗口中,缩放 StatefulSet
kubectl scale statefulset/web --replicas=5
statefulset.apps/web scaled
检查运行 kubectl get
命令的终端的输出。它可能看起来像
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 7s
web-3 0/1 ContainerCreating 0 7s
web-2 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-2 1/1 Running 0 8s
web-4 0/1 ContainerCreating 0 4s
web-3 1/1 Running 0 26s
web-4 1/1 Running 0 2s
StatefulSet 启动了三个新的 Pod,并且它没有等待第一个 Pod 变为运行和就绪状态,然后再启动第二个和第三个 Pod。
这种方法在您的工作负载具有状态元素或需要 Pod 能够使用可预测的命名相互识别的情况下很有用,尤其是在您有时需要快速提供更多容量的情况下。如果本教程的这个简单的 Web 服务突然每分钟收到 1,000,000 个额外的请求,那么您将希望运行更多 Pod - 但您也不希望等待每个新 Pod 启动。并行启动额外的 Pod 可以缩短请求额外容量与可用容量之间的时间。
清理
您应该有两个终端打开,准备让您运行 kubectl
命令作为清理的一部分。
kubectl delete sts web
# sts is an abbreviation for statefulset
您可以观察 kubectl get
以查看这些 Pod 被删除。
# end the watch when you've seen what you need to
kubectl get pod -l app=nginx --watch
web-3 1/1 Terminating 0 9m
web-2 1/1 Terminating 0 9m
web-3 1/1 Terminating 0 9m
web-2 1/1 Terminating 0 9m
web-1 1/1 Terminating 0 44m
web-0 1/1 Terminating 0 44m
web-0 0/1 Terminating 0 44m
web-3 0/1 Terminating 0 9m
web-2 0/1 Terminating 0 9m
web-1 0/1 Terminating 0 44m
web-0 0/1 Terminating 0 44m
web-2 0/1 Terminating 0 9m
web-2 0/1 Terminating 0 9m
web-2 0/1 Terminating 0 9m
web-1 0/1 Terminating 0 44m
web-1 0/1 Terminating 0 44m
web-1 0/1 Terminating 0 44m
web-0 0/1 Terminating 0 44m
web-0 0/1 Terminating 0 44m
web-0 0/1 Terminating 0 44m
web-3 0/1 Terminating 0 9m
web-3 0/1 Terminating 0 9m
web-3 0/1 Terminating 0 9m
在删除期间,StatefulSet 会同时删除所有 Pod;它不会等待 Pod 的序数后继者终止,然后再删除该 Pod。
关闭运行 kubectl get
命令的终端,并删除 nginx
服务
kubectl delete svc nginx
删除本教程中使用的持久卷的持久存储介质。
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-2bf00408-d366-4a12-bad0-1869c65d0bee 1Gi RWO standard 25m
www-web-1 Bound pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4 1Gi RWO standard 24m
www-web-2 Bound pvc-cba6cfa6-3a47-486b-a138-db5930207eaf 1Gi RWO standard 15m
www-web-3 Bound pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752 1Gi RWO standard 15m
www-web-4 Bound pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e 1Gi RWO standard 14m
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752 1Gi RWO Delete Bound default/www-web-3 standard 15m
pvc-2bf00408-d366-4a12-bad0-1869c65d0bee 1Gi RWO Delete Bound default/www-web-0 standard 25m
pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e 1Gi RWO Delete Bound default/www-web-4 standard 14m
pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4 1Gi RWO Delete Bound default/www-web-1 standard 24m
pvc-cba6cfa6-3a47-486b-a138-db5930207eaf 1Gi RWO Delete Bound default/www-web-2 standard 15m
kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
kubectl get pvc
No resources found in default namespace.