暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

kubernetes - 控制器 - StatefulSets

小朱哥的技术人生 2020-06-01
514


控制器 - StatefulSets

目标

  • StatefulSets 的使用场景
  • StatefulSets 基本信息
  • StatefulSets 部署与伸缩
  • StatefulSets 更新策略

StatefulSets 的使用场景

StatefulSets 概述

StatefulSets 顾名思义,是管理 Stateful(有状态的)应用程序

StatefulSets 管理 Pod 时,确保 Pod 有一个按照顺序增长的 ID

与 Deployment 相似,StatefulSets 基于一个 Pod 模板管理其 Pod,与 Deployment 最大的不同在于 StatefulSets 始终将一系列的名字分配给其 Pod ,这些 Pod 从同一个模板创建,但是并不能相互替换,每个 Pod 都对应一个特有的持久化标识

同其他的控制器一样,StatefulSets 也使用相同的模式运作,用户在 StatefulSets 中定义好期待的状态,StatefulSets 控制器执行需要的操作,达成最终的效果

StatefulSets 的使用场景

对于如下要求的应用程序,StatefulSets 非常合适

  • 稳定,唯一的网络标识(dnsname)
  • 每个 Pod 始终对于着各自的存储路径(PersistantVolumeClaimTemplate)
  • 按顺序的执行增加副本,减少副本,在减少副本时执行清理
  • 按顺序自动的执行滚动更新

如果一个应用程序不需要稳定的网络标识,或者不需要按顺序部署、删除、增加副本,您应该考虑使用 Deployment 这类无状态(stateless)的控制器

StatefulSets 限制

  • Pod 的存储要么由 storage class 对应的 PersistantVolumeClaimTemplate 提供,要么由管理员事先创建好
  • 删除或scale down 一个 StatefulSets 将不会对其数据卷进行清理,原因是保证数据的安全
  • 删除 StatefulSets 时,将无法保证 Pod 的终止是正常的,如果要按顺序优雅的终止 StatefulSets 的 Pod 可以在删除 StatefulSets 前将其 scale down 到 0
  • 当使用默认更新策略进行滚动更新时,可能进入一个错误的状态,需要人工介入才能修复

StatefulSets 基本信息

创建 StatefulSets

下面是个 StatefulSets 的例子,由如下的内容组成:

  • 一个名为 nginx 的 无头服务,用于控制网络域
  • 一个名为 web 的 StatefulSets 副本为 3
  • volumeClaimTemplates 提供稳定的存储(每一个 Pod ID 对应自己的存储卷,且 Pod 重连后,仍然可以找到对应的存储卷)

名为nginx-statefulsets.yaml
配置文件如下:

apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
apps: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
apps: nginx

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
apps: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
apps: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOne"]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi

Pod 标识

StatefulSets 中的 Pod 具有唯一的标识,该标识由以下部分组成

  • 序号
  • 稳定的网络标识
  • 稳定的存储

该标识始终与 Pod 绑定,无论 Pod 被调度到那个节点

序号

假设一个 StatefulSets 的副本数是 N,其中每一个 Pod 都会被分配到一个序号,序号的范围是从 0 到 N-1,并且该序号在 StatefulSets 内部是唯一的

稳定的网络标识
  • StatefulSets 中 Pod 的 hostname 格式为 $(StatefulSets name)-$(Pod 序号)
    ,上面的例子将要创建三个 Pod ,分别是 web-0,web-1,web-2
  • StatefulSets 可以用无头服务来控制其 Pod 所在的域,该域的格式为$(servicename).$(namespace).svc.cluster.local
    ,其中 “cluster.local”是集群的域
  • StatefulSets 中的每个 Pod 都会被分配一个 dnsname 格式为: $(podname).$(所在域)

你需要自行为 StatefulSets 创建无头服务

下表列出了不同的 集群域、Service name、StatefulSet name 的情况下,对应的 StatefulSet 中 Pod 的 DNS 名字

字段名组合一组合二组合三
Cluster Domaincluster.localcluster.localkube.local
Service(ns/name)default/nginxfoo/nginxfoo/nginx
StatefulSets(ns/name)default/webfoo/webfoo/web
StatefulSets Domainnginx.defalut.svc.cluster.localnginx.foo.svc.cluster.localnginx.foo.svc.kube.local
Pod DNSweb-{0..N-1}.nginx.defalut.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.kube.local
Pod Hostnameweb-{0..N-1}web-{0..N-1}web-{0..N-1}
稳定的存储

Kubernetes 为每一个 VolumeClaimTemplate 创建一份 PersistentVolume(存储卷)在上面的例子中,每一个 Pod 都将由 StorageClass(存储类)my-storage-class
为其创建一个 1Gib 大小的 PersistentVolume(存储卷)当 Pod 被调度(或重新调度)到一个节点上,其挂载点将挂载该存储卷声明(关联到该 PersistentVolume)

  • 当 Pod 或者 StatefulSets 被删除时,其关联的 PersistentVolumeClaim (存储卷声明) 以及其背后的 PersistentVolume (存储卷)仍然存在
  • 如果相同的 Pod 和 StatefulSets 被再次创建,则新建的名为 web-0 的 Pod 仍然挂载到原来名为 web-0 所挂载的存储卷声明及存储卷
  • 这个确保了 Pod 无论被删除重建多少次,都将稳定的使用各自对应的存储内容
Pod name 标签

当 StatefulSets 控制器创建一个 Pod 时,会为 Pod 添加一个标签 (label) statefulset.kubernetes.io/pod-name
且标签的值为 Pod 的名字,你可以利用这个名字,为 StatefulSets 中的某一个特定的 Pod 关联 Service

实际操作中,你无需为 StatefulSet 中的一个特定 Pod 关联 Service,因为你可以直接通过该 Pod 的 DNS Name 访问到 Pod

StatefulSets 部署与伸缩

部署和伸缩 StatefulSets 时的执行顺序

  • 在创建一个副本为 N 的 StatefulSets 时,其 Pod 将被按照 {0..N-1}的顺序逐个创建
  • 在删除一个副本为 N 的 StatefulSets 时,其 Pod 将被按照 {N-1..0}的顺序逐个删除
  • 在对 StatefulSets 进行扩容(scale up)时,新增加的 Pod 必须在前面的所有的 Pod 都处于 Running(运行) 和  Ready(就绪)状态,才会创建
  • 终止和删除 StatefulSets 中的某一个 Pod 时,该 Pod 所有后面的 Pod 必须全部已终止

StatefulSets 中 pod.spec.terminationGracePeriodSeconds
不能为 0 ,这个字段的意思是终止 Pod 的宽限时间,如果为 0 将不会优雅的删除 Pod

上面的例子中,nginx 的 StatefulSets 被创建时:

  • Pod web-0、web-1、web-2 将被按顺序部署
  • web-0 处于 Running 和 Ready 状态之前,web-1 不会创建;web-1 处于 Running 和 Ready 状态之前,web-2 不会创建
  • 如果 web-1 已处于 Running 和 Ready 的状态,web-2 尚未创建,此时 web-0 发生了故障,则在 web-0 成功重启并达到 Running 和 Ready 的状态之前,web-2 不会创建
  • 如果用户对这个 StatefulSet 执行缩容(scale down)操作,将其副本数调整为 1,则:
    • web-2 将被首先终止;在 web-2 已终止并删除之后,才开始终止 web-1
    • 假设在 web-2 终止并删除之后,web-1 终止之前,此时 web-0 出现故障,则,在 web-0 重新回到 Running 和 Ready 的状态之前,kubernetes 将不会终止 web-1

Pod 管理策略

在 kubernetes 1.7及以后的版本中,可以为 StatefulSets 设定 .spec.podManagementPolicy
字段,使用 StatefulSets 唯一 ID 的特性禁止有序创建和删除 Pod

  • 「OrderedReady」

    OrderedReady 是 .spec.podManagementPolicy
    字段的默认值,对 Pod 的管理就是上面介绍的有序创建

  • 「Parallel」

    .spec.podManagementPolicy
    字段设置为 Parallel 后,则 StatefulSets Controller 将同时并行的创建和删除 Pod,此时 StatefulSet Controller 将不会逐个创建 Pod,等待 Pod 进入 Running 和 Ready 状态之后再创建下一个 Pod,也不会逐个终止 Pod

此选项只会影响伸缩 (scale up / scale down)操作,更新操作不影响

StatefulSets 更新策略

在 kubernetes 1.7 及以后的版本中,可以是 StatefulSets 设定 .spec.updateStrategy
字段,以便你可以在改变 StatefulSets 中的 Pod 的某些字段时 (container/labels/resource request/resource limit/annotation等)禁止滚动更新

On Delete

On Delete 策略实现了 StatefulSets 的遗留版本(kubernetes 1.6及以前的版本)的行为,如果 StatefulSets 的 .spec.updateStrategy.type
字段被设置为 OnDelete,当你修改.spec.template
内容时 StatefulSets Controller 将不会更新其 Pod ,你需要手动删除,此时 StatefulSets Controller 在创建新的 Pod 时,使用修改过的 .spec.template
内容创建

Rolling Update

.spec.updateStrategy.type
字段为 Rolling Update 时,改策略为 StatefulSets 的 Pod 实现的自动滚动更新,在用户更新 StatefulSets 的.spec.template
字段时,StatefulSets Controller 将自动的删除并重建,处理顺序如下:

  • 从序号大的开始,逐步的删除更新每个 Pod,直到序号最小的被更新

  • 当正在更新的 Pod 达到 Runing 和 Ready 时,才继续更新其前序的 Pod

  • 「Partitions」

    通过指定 .spec.updateStrategy.rollingUpdate.partition
    字段,可以分片执行 RollingUpdate 更新策略,当 StatefulSets 的 .spec.template
    字段被更改时

    • 执行预发布
    • 执行金丝雀更新
    • 执行按阶段更新
    • 序号大于或者等于.spec.updateStrategy.rollingUpdate.partition
      的 Pod 将会被删除重建

    • 序号小于.spec.updateStrategy.rollingUpdate.partition
      的 Pod 不会更新,即使手工删除该 Pod kubernetes 也会使用前一个版本的.spec.template
      重建该 Pod

    • 如果.spec.updateStrategy.rollingUpdate.partition
      大于.spec.replicas
      将不会影响到任何的 Pod

      大部分情况下,不需要使用.spec.updateStrategy.rollingUpdate.partition
      ,除非碰到下面情况:

  • 「Forced Rollback」

    当使用 StatefulSets 默认的更新策略(OrderedReady),有可能会进入一种卡住的状态,需要人工修复

    如果更新 Pod Template 后,该 Pod 始终不能进入到 Runing 和 Ready 状态(例如:镜像或者配置错误),StatefulSets 将停止滚动更新并一直等待

    此时,你仅仅将 Pod Template 回退到一个正常的配置是不够的,由于一个已知的问题,StatefulSets 将继续等待出错的 Pod 进入就绪状态 (该状态永远无法实现) 才尝试将该 Pod 回退到正确的配置

    在修复 Pod template 以后,你还必须删除掉所有已经尝试使用有问题的 Pod template 的 Pod,StatefulSet此时才会开始使用修复了的 Pod template 重建 Pod

文章转载自小朱哥的技术人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论