一、什么情况下使用StatefulSet

StatefulSet(有状态集,缩写为sts),常用于部署有状态的且需要有序启动的应用程序,用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

注意:StatefulSet的管理机制为直接管理 Pod,依赖 ControllerRevision

1.1 注意事项

1.需要稳定的独一无二的网络标识符 2.需要持久化数据 3.需要有序的、优雅的部署和扩展 4.需要有序的自动滚动更新

1.2 应用场景

1.Eureka集群 2.MongoDB 3.ElasticSearch 4.Redis 5.Kafka 6.其他需要具有状态的服务

1.3 限制

1.给定 Pod 的存储必须由 PersistentVolume Provisioner 基于所请求的 storage class 来制备,或者由管理员预先制备。

2.删除或者扩缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。

3.StatefulSet 当前需要无头服务来负责 Pod 的网络标识。你需要负责创建此服务。

4.当删除一个 StatefulSet 时,该 StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序且体面地终止,可以在删除之前将 StatefulSet 缩容到 0。

5.在默认 Pod 管理策略(OrderedReady) 时使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。

二、创建StatefulSet

2.1 StatefulSet部署过程

下面以创建名为nginx且副本数为3的StatefulSet文件为例,其部署过程图如下:

StatefulSet部署过程

1.使用kubectl创建一个StatefulSet 资源文件 2.在执行kubectl create命令时,提交给api-server 3.api-server持久化实例 4.假设在Default命名空间创建StatefulSet 文件,先启动一个nginx-0,等nginx-0完全可用了;再启动另一个nginx-1,依次类推。假如nginx-1启动失败,nginx-2不会被创建。

2.2 创建一个 StatefulSet

1.定义一个yaml文件

$ vim stateful.yaml
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:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.14.2
        ports:
        - containerPort: 80
          name: web

上面参数说明:

  • kind(第一个): Service定义了一个名字为Nginx的Headless Service,创建的Service格式为nginx-0.nginx.default.svc.cluster.local,其他的类似,因为没有指定Namespace(命名空间),所以默 认部署在default;
  • kind(第二个): StatefulSet定义了一个名字为web的StatefulSet,replicas表示部署Pod的副本数,本实例为2。

注意事项:在 StatefulSet中必须设置Pod选择器( .spec.selector ) 用来匹配其标签 (.spec.template.metadata.labels)。在1.8 版本之前,如果未配置该字段(.spec.selector), 将被设置为默认值,在1.8 版本之后,如果未指定匹配 Pod Selector,则会导致 StatefulSet 创建错误。 当 StatefulSet 控制器创建 Pod 时,它会添加一个标签 statefulset.kubernetes.io/pod-name,该 标签的值为 Pod 的名称,用于匹配 Service。

2.创建 StatefulSet

$ k create -f stateful.yaml
service/nginx created
statefulset.apps/web created

3.查看sts

$ kubectl get sts
NAME   READY   AGE
web    2/2     25s

4.查看service

$ k get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP   7d21h
nginx        ClusterIP   None         <none>        80/TCP    69s

5.查看pod

$ k get po -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          113s
web-1   1/1     Running   0          112s

三、StatefulSet创建Pod的顺序规则

StatefulSet 管理的 Pod 部署和扩展规则如下:

  • 对于具有N个副本的StatefulSet,将按顺序从0到N-1开始创建Pod;
  • 当删除Pod时,将按照N-1到0的反顺序终止;
  • 在缩放Pod之前,必须保证当前的Pod是Running(运行中)或者Ready(就绪);
  • 在终止Pod之前,它所有的继任者必须是完全关闭状态。

StatefulSet 的 pod.Spec.TerminationGracePeriodSeconds(终止 Pod 的等待时间)不应该指定 为 0,设置为 0 对 StatefulSet 的 Pod 是极其不安全的做法,优雅地删除 StatefulSet 的 Pod 是非常 有必要的,而且是安全的,因为它可以确保在 Kubelet 从 APIServer 删除之前,让 Pod 正常关闭。

当创建上面的 Nginx 实例时,Pod 将按 web-0、web-1、web-2 的顺序部署 3 个 Pod。在 web-0 处于 Running 或者 Ready 之前,web-1 不会被部署,相同的,web-2 在 web-1 未处于 Running 和 Ready 之前也不会被部署。如果在 web-1 处于 Running 和 Ready 状态时,web-0 变成 Failed (失败)状态,那么 web-2 将不会被启动,直到 web-0 恢复为 Running 和 Ready 状态。

如果用户将 StatefulSet 的 replicas 设置为 1,那么 web-2 将首先被终止,在完全关闭并删除 web-2 之前,不会删除web-1。如果 web-2 终止并且完全关闭后,web-0 突然失败,那么在 web0 未恢复成 Running 或者 Ready 时,web-1不会被删除。

如果要实现无序地同时创建0到N-1个Pod,你需要将 StatefulSet 的 .spec.podManagementPolicy 设置为 "Parallel"。默认情况下,.spec.podManagementPolicy 的值为 "OrderedReady",它指示 Kubernetes 按顺序逐个创建 Pod,并等待每个 Pod 就绪才能创建下一个 Pod。将 .spec.podManagementPolicy 设置为 "Parallel" 后,Kubernetes 将会同时创建所有 Pod。你还需要设置 .spec.updateStrategy.type"RollingUpdate",这样 Kubernetes 才会在启动和更新时同时创建所有 Pod。