Kubernetes - StatefulSet

Table of Contents

Deployment 和 ReplicaSet 是针对无状态服务部署的;与此对应,StatefulSet 是专门针对有状态服务部署。

StatefulSet 管理一组 Pod 的部署和扩展,并保证 Pod 有序和名称唯一性。

1. 应用场景

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

综上,稳定是 Pod 重新调度的代名词。如果应用不要求不变的标识符和有序部署、删除或者扩展,那么你应该使用无状态部署 (Deployment/ReplicaSet 是更好的选择)。

2. 局限性

  • Pod 给定的存储必须是根据要求 storage class 由 PersistentVolumen Provisioner 提供的,或者是管理员预先设置好的。
  • 删除或者缩小 StatefulSet 不会 删除关联的存储卷。这么做是为了数据安全,通常,它比自动清除所有相关的 StatefulSet 资源更有价值。
  • StatefulSets 当前需要 Headless Service 负责 Pod 的网络 ID。你需要创建此服务。
  • 删除 StatefulSet 时,StatefulSet 不提供有关 Pod 终止的任何保证。为了实现 StatefulSet 中的 Pod 有序且优雅终止,可以在 删除之前将 StatefulSet 缩小为 0
  • 当使用默认的 Pod 管理策略( OrderReady )滚动更新时,可能会进入故障状态,需要人工干预才能修复。

3. 组件

下面的例子展示了 StatefulSet 组件:

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 # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.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,用于控制网络域。
  • 名为 web 的 StatefulSet,规格中表明有三个 nginx 容器副本会被生成唯一的 Pod。
  • volumeClaimTemplates 会提供稳定的存储(使用 PersistentVolumes)。

4. Pod Selector

必须设置 .spec.selector 字段以匹配 .spec.template.metadata.labels 标签。

1.8 之前的版本, .spec.selector 字段默认是省略的。1.8 和之后的版本,未指定匹配的 Pod 选择器,会在 StatefulSet 创建期间 导致验证错误。

5. Pod ID

StatefulSet Pod 由唯一的身份:不变的序号,稳定的网络 ID 和稳定的存储组成。身份与 Pod 绑定,无论被(重新)调度在哪个节点上。

5.1. 有序索引

N 个副本的 StatefulSet,Pod 的编号从 0 到 N-1。如果 StatefulSet 的名字是 nginx ,那么 Pod 名字为 nginx-0 nginx-1 以此类推。

5.2. 稳定的网络 ID

主机名的构造方法为 $(statefulset name)-$(ordinal) 。比如 web-1, web-2, web-3

Headless Service 控制 Pod 域,此服务管理的域才用以下形式: $(service name).$(namespace).svc.cluster.local 。 "cluster.local" 是集群的域。每个 Pod 创建之后,会获得一个匹配的 DNS 子域,格式是: $(podname).$(governing service domain) , 其中控制服务由 StatefulSet 上的 serviceName 字段定义。

下面是示例:

Cluster Domain Service (ns/name) StatefulSet (ns/name) StatefulSet Domain Pod DNS Pod Hostname
cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}
kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1}

注意: 集群的域默认是 cluster.local 除非进行了其它配置。

5.3. 稳定的存储

Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolume。 在上面的例子中,每个 Pod 将会收到一个具有 my-storage-class 的 StorageClass 和 1 Gib 预设的存储 PersistentVolume。 如果没有指定 StorageClass,会使用默认的。当 Pod(重新)调度到一个节点时,会使用 PersistentVolume Claims 挂载持久卷。 注意,关联的存储卷不会随着 Pod 或者 StatefulSet 的删除而删除,必须要手动操作。

5.4. Pod Name Label

StatefulSet 控制器创建 Pod 时,会添加一个标签: statefulset.kubernetes.io/pod-name ,值是 Pod 名称。

6. 部署和扩展保证

  • 部署时,按照 Pod 的序号依次创建,{0..N-1}
  • 删除时,按照 Pod 的序号逆序删除,{N-1..0}
  • 在对 Pod 进行缩放之前,前面的所有都必须要运行且就绪
  • 在终止 Pod 之前,必须关闭其所有的后继产品

StatefulSet 不应该 pod.Spec.TerminationGracePeriodSeconds 为 0。这种做法是不安全的,强烈不建议这么做。

6.1. Pod 管理方针

Kubernetes 1.7 及之后的版本,StatefulSet 通过 .spec.podManagementPolicy 字段允许你放宽顺序保证,同时又保留唯一性和身份保证。

OrderedReady
默认的配置,就是上面描述的行为。
Parallel
并行创建/终止 Pod,不会等待前一个 Pod 运行就绪或者完全终止。这只会影响扩展操作行为,不影响更新。

6.2. 更新策略

Kubernetes 1.7 及之后的版本,StatefulSet 的 .spec.updateStrategy 字段允许你配置或者禁掉自动滚动更新 (在容器标签、资源请求/限制,和注释发生变化时 这么理解不确定是否对 )。

OnDelete
实现旧版(1.6 及以前版本)的行为。当 .spec.updateStrategy.type 设置为 OnDelete 时,控制器不会 自动更新 Pods。用户必须手动删掉 Pods 触发控制器创建新的 Pods。
RollingUpdate

自动滚动更新,未指定 .spec.updateStrategy 时,这是默认策略。 当 .spec.updateStrategy.type 设置为 RollingUpdate 时,控制器会删除然后重新创建每一个 Pod。它会按照与 Pod 终止的顺序 相同顺序进行(从大到小),一次更新一个 Pod。而且会等到 Pod 运行且就绪才会更新下一个。

滚动更新策略允许设置分区(partition),指定分区之后,可以批量更新,而不是一次更新一个。

First created: 2020-04-08 11:48:30
Last updated: 2022-12-11 Sun 12:49
Power by Emacs 29.0.91 (Org mode 9.6.6)