StatefulSet 基础知识

本教程介绍了使用 StatefulSets 管理应用程序。它演示了如何创建、删除、扩展和更新 StatefulSets 的 Pod。

开始之前

在开始本教程之前,您应该熟悉以下 Kubernetes 概念

您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,可以使用 minikube 创建一个,或者您可以使用以下 Kubernetes 游乐场之一

您应该将 kubectl 配置为使用使用 default 命名空间的上下文。如果您使用的是现有集群,请确保可以使用该集群的默认命名空间进行练习。理想情况下,在不运行任何实际工作负载的集群中进行练习。

阅读有关 StatefulSets 的概念页面也很有用。

目标

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 具有唯一的序号索引和稳定的网络标识。

检查 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-0web-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 execkubectl 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.localweb-1.nginx.default.svc.cluster.local),因为它们是稳定的,并且您的应用程序将能够在 Pod 过渡到运行状态和就绪状态时发现 Pod 的地址。

如果您的应用程序想要在 StatefulSet 中找到任何健康的 Pod,因此不需要跟踪每个特定的 Pod,您也可以连接到由该 StatefulSet 中的 Pod 支持的 type: ClusterIP 服务的 IP 地址。您可以使用与跟踪 StatefulSet 的相同服务(在 StatefulSet 的 serviceName 中指定)或选择正确 Pod 集的单独服务。

写入稳定存储

获取 web-0web-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

在一个终端中,观察 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-0web-1 已重新调度,它们仍会继续提供其主机名,因为与其 PersistentVolumeClaims 关联的 PersistentVolumes 会重新挂载到其 volumeMounts。无论 web-0web-1 调度到哪个节点,其 PersistentVolumes 都将挂载到相应的挂载点。

扩展 StatefulSet

扩展 StatefulSet 指的是增加或减少副本数量(水平扩展)。这可以通过更新 replicas 字段来实现。您可以使用 kubectl scalekubectl 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-4web-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 现在都运行着以前的容器镜像。

暂存更新

您可以通过指定 .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 的后继者完全终止。

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-0web-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-0app-1app-2,Kubernetes 将首先更新 app-0 并检查它。一旦检查通过,Kubernetes 就会更新 app-1,最后更新 app-2

如果您添加了两个 Pod,Kubernetes 将设置 app-3 并等待它变为健康状态,然后再部署 app-4

由于这是默认设置,因此您已经练习过使用它。

Parallel Pod 管理

另一种方法,Parallel Pod 管理,告诉 StatefulSet 控制器并行启动或终止所有 Pod,并且在启动或终止另一个 Pod 之前,不要等待 Pod 变为 RunningReady 或完全终止。

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.
最后修改时间:2024 年 6 月 4 日下午 11:10 PST:将 registry.k8s.io/nginx-slim 版本从 0.8 修改为 0.24 (e9c7b069d5)