StatefulSet是用来管理有状态应用的工作负载API对象。
StatefulSet用来管理某Pod集合的部署和扩缩,并为这些Pod提供持久存储和持久标识符。
和Deployment类似,StatefulSet管理基于相同容器规约的一组Pod。但和Deployment不同的是,StatefulSet为它们的每个Pod维护了一个有粘性的ID。这些Pod是基于相同的规约来创建的,但是不能相互替换:无论怎么调度,每个Pod都有一个永久不变的ID。
如果希望使用存储卷为工作负载提供持久存储,可以使用StatefulSet作为解决方案的一部分。尽管StatefulSet中的单个Pod仍可能出现故障,但持久的Pod标识符使得将现有卷与替换已失败Pod的新Pod相匹配变得更加容易。
使用StatefulSets
StatefulSets对于需要满足以下一个或多个需求的应用程序很有价值:
在上面描述中,“稳定的”意味着Pod调度或重调度的整个过程是有持久性的。如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如Deployment或者ReplicaSet可能更适用于你的无状态应用部署需要。
限制组件
下面的示例演示了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
上述例子中:
StatefulSet的命名需要遵循DNS子域名规范。
Pod选择算符
你必须设置StatefulSet的.spec.selector字段,使之匹配其在.spec.template.metadata.labels中设置的标签。在Kubernetes1.8版本之前,被忽略.spec.selector字段会获得默认设置值。在1.8和以后的版本中,未指定匹配的Pod选择器将在创建StatefulSet期间导致验证错误。
Pod标识
StatefulSetPod具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。该标识和Pod是绑定的,不管它被调度在哪个节点上。
有序索引
对于具有N个副本的StatefulSet,StatefulSet中的每个Pod将被分配一个整数序号,从0到N-1,该序号在StatefulSet上是唯一的。
稳定的网络ID
StatefulSet中的每个Pod根据StatefulSet的名称和Pod的序号派生出它的主机名。组合主机名的格式为$(StatefulSet名称)-$(序号)。上例将会创建三个名称分别为web-0、web-1、web-2的Pod。StatefulSet可以使用无头服务控制它的Pod的网络域。管理域的这个服务的格式为:$(服务名称).$(命名空间).svc.cluster.local,其中cluster.local是集群域。一旦每个Pod创建成功,就会得到一个匹配的DNS子域,格式为:$(pod名称).$(所属服务的DNS域名),其中所属服务由StatefulSet的serviceName域来设定。
取决于集群域内部DNS的配置,有可能无法查询一个刚刚启动的Pod的DNS命名。当集群内其他客户端在Pod创建完成前发出Pod主机名查询时,就会发生这种情况。负缓存(在DNS中较为常见)意味着之前失败的查询结果会被记录和重用至少若干秒钟,即使Pod已经正常运行了也是如此。
如果需要在Pod被创建之后及时发现它们,有以下选项:
正如限制中所述,你需要负责创建无头服务以便为Pod提供网络标识。
下面给出一些选择集群域、服务名、StatefulSet名、及其怎样影响StatefulSet的Pod上的DNS名称的示例:
集群域名服务(名字空间/名字)StatefulSet(名字空间/名字)StatefulSet 域名Pod DNSPod 主机名
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,除非有其他配置。
稳定的存储
对于StatefulSet中定义的每个VolumeClaimTemplate,每个Pod接收到一个PersistentVolumeClaim。在上面的nginx示例中,每个Pod将会得到基于StorageClassmy-storage-class提供的1Gib的PersistentVolume。如果没有声明StorageClass,就会使用默认的StorageClass。当一个Pod被调度(重新调度)到节点上时,它的volumeMounts会挂载与其PersistentVolumeClaims相关联的PersistentVolume。请注意,当Pod或者StatefulSet被删除时,与PersistentVolumeClaims相关联的PersistentVolume并不会被删除。要删除它必须通过手动方式来完成。
Pod名称标签
当StatefulSet控制器(Controller)创建Pod时,它会添加一个标签statefulset.kubernetes.io/pod-name,该标签值设置为Pod名称。这个标签允许你给StatefulSet中的特定Pod绑定一个Service。
部署和扩缩保证
StatefulSet不应将pod.Spec.TerminationGracePeriodSeconds设置为0。这种做法是不安全的,要强烈阻止。
在上面的nginx示例被创建后,会按照web-0、web-1、web-2的顺序部署三个Pod。在web-0进入Running和Ready状态前不会部署web-1。在web-1进入Running和Ready状态前不会部署web-2。如果web-1已经处于Running和Ready状态,而web-2尚未部署,在此期间发生了web-0运行失败,那么web-2将不会被部署,要等到web-0部署完成并进入Running和Ready状态后,才会部署web-2。
如果用户想将示例中的StatefulSet收缩为replicas=1,首先被终止的是web-2。在web-2没有被完全停止和删除前,web-1不会被终止。当web-2已被终止和删除、web-1尚未被终止,如果在此期间发生web-0运行失败,那么就不会终止web-1,必须等到web-0进入Running和Ready状态后才会终止web-1。
Pod管理策略
在Kubernetes1.7及以后的版本中,StatefulSet允许你放宽其排序保证,同时通过它的.spec.podManagementPolicy域保持其唯一性和身份保证。
OrderedReadyPod管理
OrderedReadyPod管理是StatefulSet的默认设置。它实现了上面描述的功能。
并行Pod管理
ParallelPod管理让StatefulSet控制器并行的启动或终止所有的Pod,启动或者终止其他Pod前,无需等待Pod进入Running和ready或者完全停止状态。这个选项只会影响伸缩操作的行为,更新则不会被影响。
更新策略
StatefulSet的.spec.updateStrategy字段让你可以配置和禁用掉自动滚动更新Pod的容器、标签、资源请求或限制、以及注解。有两个允许的值:
RollingUpdate滚动更新
当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。
最短就绪秒数
FEATURESTATE:Kubernetesv1.22[alpha]
.spec.minReadySeconds是一个可选字段,用于指定新创建的Pod就绪(没有任何容器崩溃)后被认为可用的最小秒数。默认值是0(Pod就绪时就被认为可用)。
请注意只有当你启用StatefulSetMinReadySeconds特性门控时,该字段才会生效。