StatefulSet

Administrator
发布于 2023-12-05 / 83 阅读 / 0 评论 / 0 点赞

StatefulSet

在实际使用的过程中,Deployment并不能编排所有类型的应用,它适用于编排无状态服务的 Pod,StatefulSet适用于编排有状态服务的 Pod。

  • 无状态服务(Stateless Service):该服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的,比如 WordPress 实例,我们可以同时启动多个实例,但是我们访问任意一个实例得到的结果都是一样的。

  • 有状态服务(Stateful Service):该服务运行的实例需要在本地存储持久化数据,比如 Etcd 实例,Etcd 集群启动有先后顺序,他们会把数据持久化存储,当启动一个新的 Pod 时,他们是按照持久化里数据顺序加入集群。

Kubernetes 引入了 StatefulSet 这种资源对象来支持这种复杂的需求。StatefulSet 类似于 ReplicaSet,但是它可以处理 Pod 的启动顺序,为保留每个 Pod 的状态设置唯一标识,具有以下几个功能特性:

  • 稳定的、唯一的网络标识符
  • 稳定的、持久化的存储
  • 有序的、优雅的部署和缩放
  • 有序的、优雅的删除和终止
  • 有序的、自动滚动更新

Headless Service

在介绍 StatefulSet 之前需要先介绍一下无头服务 Headless Service,就是 Service 没有 ClusterIP。

  • 有头服务:部署一个 3 副本的服务,会通过 Service 使其达到负载均衡,这个时候 Service 会有一个 ClusterIP。比如:创建了一个 Service 名为:mysvc;ClusterIP 为:10.250.33.99;命名空间为 default。通过 DNS 访问到 Pod 服流程为:mysvc.default.svc.cluster.local -> 10.250.33.99 -> Pod

  • 无头服务:部署一个 3 副本的服务,不给 Service 创建 ClusterIP。比如:创建了一个 Service 名为:mysvc;没有 ClusterIP;命名空间为 default。通过 DNS 访问到 Pod 服流程为:mypod1.mysvc.default.svc.cluster.local -> Pod,这个样子省去一个负载均衡的环节,使用无头服务服务的用途在需要固定 Pod 名称的场景

Headless Service 资源类型:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  ports:
  - name: http
    port: 80
  clusterIP: None  #此处 ClusterIP 设置为 None
  selector:
    app: nginx

StatefulSet

先准备两个 1G 的存储卷(PV), PV 是实际的存储卷,PVC 是挂载卷的逻辑

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /tmp/pv001

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /tmp/pv002

直接创建 PV

[root@kube01 sts]# kubectl apply -f pv.yaml
persistentvolume/pv001 created
persistentvolume/pv002 created
[root@kube01 sts]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv001   1Gi        RWO            Retain           Available                                   4s
pv002   1Gi        RWO            Retain           Available                                   4s

新建StatefulSet 资源:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: default
spec:
  serviceName: "nginx"  # 为通过 DNS 访问固定 service 名称
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - name: web
          containerPort: 80
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:  # 通过 PVC 自动获取适用的 PV 并进行绑定
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

在 StatefulSet 资源中发现了 serviceName 属性,这是因为此处设置的 Pod 的子域名,设置后可以通过mypod1.mysvc.default.svc.cluster.local访问

通过 describe 查看到 Pod 的 Controlled By 属于 StatefulSet,与 Deployment 还是不一样的,Deployment 是通过控制 ReplicaSet 控制 Pod,StatefulSet 是直接控制 Pod

[root@kube01 sts]# kubectl describe pod web-0
Name:         web-0
Namespace:    default
Priority:     0
Node:         kube02/192.168.17.6
Start Time:   Tue, 13 Jun 2023 11:15:28 +0800
Labels:       app=nginx
              controller-revision-hash=web-7466694c86
              statefulset.kubernetes.io/pod-name=web-0
Annotations:  <none>
Status:       Running
IP:           172.30.1.42
IPs:
  IP:           172.30.1.42
Controlled By:  StatefulSet/web
Containers:
  nginx:
    Container ID:   docker://a4c4b0e27886ce2ba62a472c5a296b1fa12beca2e006523427d3ee7af309fc84
    Image:          nginx:1.7.9
    Image ID:       docker-pullable://nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Tue, 13 Jun 2023 11:15:29 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from www (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-jhtxv (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  www:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  www-web-0
    ReadOnly:   false
  kube-api-access-jhtxv:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:                      <none>

image-xapp.png

#一个简单的快捷启动Pod方式
kubectl run -it --image busybox:1.28.3 test --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ #

滚动更新

在 StatefulSet 中同样也支持两种升级策略:onDelete 和 RollingUpdate,同样可以通过设置 .spec.updateStrategy.type 进行指定。

  • OnDelete:该策略表示当更新了 StatefulSet 的模板后,只有手动删除旧的 Pod 才会创建新的 Pod。
  • RollingUpdate:该策略表示当更新 StatefulSet 模板后会自动删除旧的 Pod 并创建新的Pod,如果更新发生了错误,这次“滚动更新”就会停止。不过需要注意 StatefulSet 的 Pod 在部署时是顺序从 0n 的,而在滚动更新时,这些 Pod 则是按逆序的方式即 n0 一次删除并创建。