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