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),指定分区之后,可以批量更新,而不是一次更新一个。