Loading

Kubernetes StatefulSet 笔记

StatefulSet

StatefulSet 用来管理有状态应用的工作负载 API 对象。

和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

使用 StatefulSet

StatefulSet 对于需要满足以下一个或多个需求的应用程序很有价值:

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和扩缩。
  • 有序的、自动的滚动更新

限制

  • 给定 Pod 的存储必须由 PersistentVolume Provisioner 基于所请求的 storage class 来制备,或者由管理员预先制备。
  • 删除或者扩缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
  • StatefulSet 当前需要无头服务来负责 Pod 的网络标识。你需要负责创建此服务。
  • 当删除一个 StatefulSet 时,该 StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序且体面地终止,可以在删除之前将 StatefulSet 缩容到 0。
  • 在默认 Pod 管理策略(OrderedReady) 时使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。

nginx-sts.yaml

[root@master01 stateful-set]# vim nginx-sts.yaml
[root@master01 stateful-set]# cat nginx-sts.yaml 
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.15.2
        ports:
        - containerPort: 80
          name: web
  • 名为 nginx 的 Headless Service 用来控制网络域名
  • 名为 web 的 StatefulSet 有一个 Spec,它表明将在独立的 3 个 Pod 副本中启动 nginx 容器。
  • volumeClaimTemplates 将通过 PersistentVolume 制备程序所准备的 PersistentVolumes 来提供稳定的存储

创建

[root@master01 stateful-set]# kubectl create  -f nginx-sts.yaml 
service/nginx created
statefulset.apps/web created

查看状态

[root@master01 stateful-set]# kubectl get po
NAME    READY   STATUS              RESTARTS   AGE
nginx   1/1     Running             2          3d20h
web-0   1/1     Running             0          5s
web-1   0/1     ContainerCreating   0          1s
[root@master01 stateful-set]# kubectl get svc 
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   8d
nginx        ClusterIP   None         <none>        80/TCP    13s
[root@master01 stateful-set]# kubectl get po
NAME    READY   STATUS              RESTARTS   AGE
nginx   1/1     Running             2          3d20h
web-0   1/1     Running             0          17s
web-1   1/1     Running             0          13s
web-2   0/1     ContainerCreating   0          9s

扩容

[root@master01 stateful-set]# kubectl scale --replicas=5 sts web 
statefulset.apps/web scaled
[root@master01 stateful-set]# kubectl get po
NAME    READY   STATUS              RESTARTS   AGE
nginx   1/1     Running             2          3d20h
web-0   1/1     Running             0          95s
web-1   1/1     Running             0          91s
web-2   1/1     Running             0          87s
web-3   0/1     ContainerCreating   0          3s

Pod 选择算符

必须设置 StatefulSet 的 .spec.selector 字段,使之匹配其在 .spec.template.metadata.labels 中设置的标签。 未指定匹配的 Pod 选择算符将在创建 StatefulSet 期间导致验证错误。

VolumeClaimTemplates

可以设置 .spec.volumeClaimTemplates, 它可以使用 PersistentVolume 制备程序所准备的 PersistentVolumes 来提供稳定的存储

最短就绪秒数

特性状态: Kubernetes v1.25 [stable]

.spec.minReadySeconds 是一个可选字段。 它指定新创建的 Pod 应该在没有任何容器崩溃的情况下运行并准备就绪,才能被认为是可用的。 这用于在使用滚动更新策略时检查滚动的进度。 该字段默认为 0(Pod 准备就绪后将被视为可用)

Pod 标识

StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。 该标识和 Pod 是绑定的,与该 Pod 调度到哪个节点上无关。

有序索引

对于具有 N 个副本的 StatefulSet,该 StatefulSet 中的每个 Pod 将被分配一个从 0 到 N-1 的整数序号,该序号在此 StatefulSet 上是唯一的。

稳定的网络 ID

StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。 组合主机名的格式为$(StatefulSet 名称)-$(序号)。 上例将会创建三个名称分别为 web-0、web-1、web-2 的 Pod

StatefulSet 可以使用 Headless Service 控制它的 Pod 的网络域。管理域的这个服务的格式为: $(服务名称).$(名字空间).svc.cluster.local,其中 cluster.local 是集群域。

一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为: $(pod 名称).$(所属服务的 DNS 域名),其中所属服务由 StatefulSet 的 serviceName 域来设定。

测试
  1. 启动busybox
cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF
  1. 查询
[root@master01 stateful-set]# kubectl exec -it busybox -- sh 
/ # nslookup web-0.nginx
Server:		10.96.0.10
Address:	10.96.0.10:53

** server can't find web-0.nginx: NXDOMAIN

取决于集群域内部 DNS 的配置,有可能无法查询一个刚刚启动的 Pod 的 DNS 命名。 当集群内其他客户端在 Pod 创建完成前发出 Pod 主机名查询时,就会发生这种情况。 负缓存 (在 DNS 中较为常见) 意味着之前失败的查询结果会被记录和重用至少若干秒钟, 即使 Pod 已经正常运行了也是如此。
如果需要在 Pod 被创建之后及时发现它们,可使用以下选项:

直接查询 Kubernetes API(比如,利用 watch 机制)而不是依赖于 DNS 查询
缩短 Kubernetes DNS 驱动的缓存时长(通常这意味着修改 CoreDNS 的 ConfigMap,目前缓存时长为 30 秒)

[root@master01 ~]# kubectl get po -owide
NAME      READY   STATUS    RESTARTS   AGE     IP               NODE       NOMINATED NODE   READINESS GATES
busybox   1/1     Running   0          96s     172.18.71.23     master03   <none>           <none>
nginx     1/1     Running   3          3d22h   172.31.112.150   master01   <none>           <none>
web-0     1/1     Running   1          65m     172.21.231.149   node02     <none>           <none>
web-1     1/1     Running   1          64m     172.20.59.210    master02   <none>           <none>
web-2     1/1     Running   1          64m     172.31.112.152   master01   <none>           <none>


[root@master01 ~]# kubectl exec  busybox -n default -- nslookup web-0.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 172.21.231.149 web-0.nginx.default.svc.cluster.local

稳定的存储

对于 StatefulSet 中定义的每个 VolumeClaimTemplate,每个 Pod 接收到一个 PersistentVolumeClaim

Pod 名称标签

当 StatefulSet 控制器创建 Pod 时, 它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签值设置为 Pod 名称。 这个标签允许你给 StatefulSet 中的特定 Pod 绑定一个 Service。

部署和扩缩保证

  • 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1
  • 当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。
  • 在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
  • 在一个 Pod 终止之前,所有的继任者必须完全关闭。

Pod 管理策略

StatefulSet 允许你放宽其排序保证, 同时通过它的 .spec.podManagementPolicy 域保持其唯一性和身份保证。

OrderedReady Pod 管理

OrderedReady Pod 管理是 StatefulSet 的默认设置。 它实现了上面描述的功能

并行 Pod 管理

Parallel 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 执行自动的滚动更新。这是默认的更新策略

滚动更新

当 StatefulSet 的 .spec.updateStrategy.type 被设置为 RollingUpdate 时, StatefulSet 控制器会删除和重建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。

Kubernetes 会等到被更新的 Pod 进入 Running 和 Ready 状态,然后再更新其前身。 如果你设置了 .spec.minReadySeconds(查看最短就绪秒数), 控制平面在 Pod 就绪后会额外等待一定的时间再执行下一步

分区滚动更新

通过声明 .spec.updateStrategy.rollingUpdate.partition 的方式,RollingUpdate 更新策略可以实现分区。

如果声明了一个分区,当 StatefulSet 的 .spec.template 被更新时, 所有序号大于等于该分区序号的 Pod 都会被更新。 所有序号小于该分区序号的 Pod 都不会被更新,并且,即使它们被删除也会依据之前的版本进行重建。

如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于它的 .spec.replicas,则对它的 .spec.template 的更新将不会传递到它的 Pod。

在大多数情况下,你不需要使用分区,但如果你希望进行阶段更新、执行金丝雀或执行分阶段上线,则这些分区会非常有用。

强制回滚

在默认 Pod 管理策略(OrderedReady) 下使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。

如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如, 由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。

在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。 由于已知问题,StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。

恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样, StatefulSet 才会开始使用被还原的模板来重新创建 Pod。

删除

级联删除

删除 sts 同时删除 pod

默认为级联删除

[root@master01 stateful-set]# kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          6m45s
nginx     1/1     Running   3          3d22h
web-0     1/1     Running   1          70m
web-1     1/1     Running   1          70m
web-2     1/1     Running   1          70m
[root@master01 stateful-set]# kubectl get svc 
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   8d
nginx        ClusterIP   None         <none>        80/TCP    70m
[root@master01 stateful-set]# kubectl get sts 
NAME   READY   AGE
web    3/3     70m
[root@master01 stateful-set]# kubectl delete sts web 
statefulset.apps "web" deleted
[root@master01 stateful-set]# kubectl get po 
NAME      READY   STATUS        RESTARTS   AGE
busybox   1/1     Running       0          7m12s
nginx     1/1     Running       3          3d22h
web-1     0/1     Terminating   1          70m
web-2     0/1     Terminating   1          70m
[root@master01 stateful-set]# kubectl get po 
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          7m24s
nginx     1/1     Running   3          3d22h

非级联删除

删除 sts 不同时删除 pod

[root@master01 stateful-set]# kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          8m32s
nginx     1/1     Running   3          3d22h
web-0     1/1     Running   0          14s
web-1     1/1     Running   0          11s
web-2     1/1     Running   0          7s
[root@master01 stateful-set]# kubectl get sts 
NAME   READY   AGE
web    3/3     26s
[root@master01 stateful-set]# kubectl delete sts web --cascade=false #非级联删除 
warning: --cascade=false is deprecated (boolean value) and can be replaced with --cascade=orphan.
statefulset.apps "web" deleted
[root@master01 stateful-set]# kubectl get po 
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          9m25s
nginx     1/1     Running   3          3d22h
web-0     1/1     Running   0          67s
web-1     1/1     Running   0          64s
web-2     1/1     Running   0          60s
[root@master01 stateful-set]# kubectl get sts 
No resources found in default namespace.
posted @ 2022-11-02 23:44  平凡键客  阅读(182)  评论(0编辑  收藏  举报