领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

动手动脑学Kubernetes系列之StatefulSet

nixiaole 2025-04-29 03:05:11 知识剖析 3 ℃

在之前的文章中, 介绍了搭建好的#minikube#环境,如果你现在还没有一个可用的minikube环境, 那么可以去该篇文章中直接下载;

在之前的文章中, 先后介绍了如何从源代码开始构建一个Node.js应用和Spring Boot 应用, 并且部署到Kubernetes 中(这个Kubernetes 环境主要是之前建好的#minikube#) , 现在我们开始进一步深入的学习Kubernetes, 用一个个可以实际运行的例子的形式, 深入理解#Kubernetes#的概念以及原理.


在#动手动脑学Kubernetes#系列教程中, 我们展示了Kubernetes的基本用法

第一篇里, 学习了#Pod#的基本知识;

第二篇里, 学习了标签(#Label#)的语法, 使用Label来选择和过滤Kubernetes 资源;

第三篇里, 介绍了#Deployment#的使用, 介绍了Deployment与Replica Set、Pod的关系, 并展示了如何进行应用的版本回滚;

第四篇里, 介绍了#Service#的使用,使用Replication Controller创建Pod, 并创建Service, 展示了从Service 调用应用的方法; 随后又展示了扩展 Pod的数量为2, 比较了Service和之前的不同, 基本展示了Cluster IP 类型的Service的基本用法.

第五篇里, 介绍了#Namespace#的使用, 除了创建,列出系统的Namespace之外, 还说明Namespace 如何对资源进行隔离, 建立Development, Staging, Production等环境的方法.

第六篇里, 介绍了#Service Discovery#的使用, 讲解了如何检查Kube-dns, 如何检查和使用Service的FQDN等知识, 对Kubernetes的DNS 系统有整体的理解.

第七篇里, 介绍了#Port Forwards#, #端口转发#的用法, 在本地程序开发的时候, 使用端口转发可以简化本地测试的工作, 同时介绍了其他几种本地端口转发的用法.

第八篇里, 介绍了#Probe#(#探针#)的知识, 介绍了livenessProbe 和readinessProbe的用法,同时介绍了Pod 容器中的几个状态变化, 以及2个容器生命周期回调接口.

第九篇里, 介绍了#环境变量#的用法, 使用环境变量可以把Pod 定义的信息传递给运行其中的镜像.

第十篇里, 我们介绍了#Volume#, 卷的用法, 主要展示了emptyDir卷的使用, emptyDir卷的生命周期是和Pod 生命周期同步的.

第十一篇里, 我们介绍了#Persistent Volume#, 也就是持久卷的用法,展示了当数据存入到PV 之后,数据超乎Pod 生命周期之外的情况.

第十二篇里, 介绍了Secret, 也就是机密信息的用法, 机密是绑定在命名空间里的, 在使用时候和Volume的用法一样, 可以被Pod 访问, 本篇展示了Opaque类型的Secret的用法.

第十三篇里, 介绍了日志(logging)的使用, 介绍了Kubernetes中基本日志记录的查看和常用的命令行参数,在理论部分展示了其他几种logging的使用.

第十四篇里, 介绍了Job, 也就是作业的使用, 介绍了Kubernetes中最基本的、非并行性的一次性运行的作业, 同时介绍了作业的基本概念和基本用法.

今天, 我们来学习StatefulSet的用法, 继续来学习Kubernetes 吧!




如果有无状态应用程序,则要使用Deployment。 但是,对于有状态的应用程序,就需要使用StatefulSet。 与Deployment不同,StatefulSet提供有关其管理的Pod的身份(即可预测名称)和启动顺序的某些保证。

与部署相比,还有另外两点不同:对于网络通信,您需要创建无头服务,而对于持久性,StatefulSet管理每个Pod的持久卷。



从镜像开始

首先, 来创建一个有状态的应用,然后体验StatefulSet .

让我们首先创建有状态的应用程序,即StatefulSet以及持久卷PV和Headless服务.

来回顾一下无头服务:

无头服务(Headless Services)

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。

你可以使用无头 Service 与其他服务发现机制进行接口,而不必与 Kubernetes 的实现捆绑在一起。

对这无头 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们, 而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了选择算符。

带选择算符的服务

对定义了选择算符的无头服务,Endpoint 控制器在 API 中创建了 Endpoints 记录, 并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod 上。

无选择算符的服务

对没有定义选择算符的无头服务,Endpoint 控制器不会创建 Endpoints 记录。 然而 DNS 系统会查找和配置,无论是:

对于 ExternalName 类型的服务,查找其 CNAME 记录

对所有其他类型的服务,查找与 Service 名称相同的任何 Endpoints 的记录

可见无头服务, 就是没有Cluster IP的Service, 来看看具体的定义吧.

GitHub地址:

https://raw.githubusercontent.com/hintcnuie/mehdb/main/app.yaml

文件内容:

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mehdb
spec:
  selector:
    matchLabels:
      app: mehdb
  serviceName: "mehdb"
  replicas: 2
  template:
    metadata:
      labels:
        app: mehdb
    spec:
      containers:
      - name: shard
        image: quay.io/mhausenblas/mehdb:0.6
        ports:
        - containerPort: 9876
        env:
        - name: MEHDB_DATADIR
          value: "/mehdbdata"
        livenessProbe:
          initialDelaySeconds: 2
          periodSeconds: 10
          httpGet:
            path: /status
            port: 9876
        readinessProbe:
          initialDelaySeconds: 15
          periodSeconds: 30
          httpGet:
            path: /status?level=full
            port: 9876
        volumeMounts:
        - name: data
          mountPath: /mehdbdata
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
  name: mehdb
  labels:
    app: mehdb
spec:
  ports:
  - port: 9876
  clusterIP: None
  selector:
    app: mehdb

在上面的文件定义里面, 定义了一个mehdb 的Service, 一个名为data的PVC, 最后是名为mehdb的StatefulSet, 在这里类型为StatefulSet.

运行StatefulSet

先来创建一下StatefulSet:

$ kubectl apply -f https://raw.githubusercontent.com/hintcnuie/mehdb/main/app.yaml
statefulset.apps/mehdb created
service/mehdb created

然后检查一下相应的创建过程:

$ kubectl get sts,po,pvc,svc
NAME                     READY   AGE
statefulset.apps/mehdb   2/2     4m23s

NAME          READY   STATUS    RESTARTS   AGE
pod/mehdb-0   1/1     Running   0          4m23s
pod/mehdb-1   1/1     Running   0          3m

NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-mehdb-0   Bound    pvc-ae4c72bc-5821-485b-ba74-b86f3b21b8de   1Gi        RWO            standard       4m23s
persistentvolumeclaim/data-mehdb-1   Bound    pvc-a4f548fa-5685-441e-883d-442d4e8f60f3   1Gi        RWO            standard       3m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP    52d
service/mehdb        ClusterIP   None         <none>        9876/TCP   4m23s

注意: 在这里我们又学到了一招,可以同时查看过个资源对象的命令的写法, 就是kubectl get sts,po,pvc,svc, 注意这几个资源名称之间不能有空间.另外, StatefulSet的缩写是sts, 是不是简单了很多!

现在,我们可以检查有状态的应用程序是否正常运行。 为此,我们使用无头服务mehdb:9876的/status端点,并且由于尚未将任何数据放入数据存储区,因此我们希望报告0个键:

$ kubectl run -it --rm jumpod --restart=Never --image=quay.io/openshiftlabs/jump:0.2 -- curl -s mehdb:9876/status?level=full
0
pod "jumpod" deleted

注意上面的用法, 这里面通过kubectl run命令并指定镜像, 直接创建了一个jumpod, 而且使用--rm的参数, 让这个pod 在执行完之后就自动销毁.

我们来查看一下这个StatefulSet下面的Pod:

$ kubectl get sts
NAME    READY   AGE
mehdb   2/2     13m
[vagrant@control-plane ~]$ kubectl describe sts mehdb
Name:               mehdb
Namespace:          default
CreationTimestamp:  Sat, 20 Mar 2021 14:08:09 +0000
Selector:           app=mehdb
Labels:             <none>
Annotations:        <none>
Replicas:           2 desired | 2 total
Update Strategy:    RollingUpdate
  Partition:        0
Pods Status:        2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=mehdb
  Containers:
   shard:
    Image:      quay.io/mhausenblas/mehdb:0.6
    Port:       9876/TCP
    Host Port:  0/TCP
    Liveness:   http-get http://:9876/status delay=2s timeout=1s period=10s #success=1 #failure=3
    Readiness:  http-get http://:9876/status%3Flevel=full delay=15s timeout=1s period=30s #success=1 #failure=3
    Environment:
      MEHDB_DATADIR:  /mehdbdata
    Mounts:
      /mehdbdata from data (rw)
  Volumes:  <none>
Volume Claims:
  Name:          data
  StorageClass:  
  Labels:        <none>
  Annotations:   <none>
  Capacity:      1Gi
  Access Modes:  [ReadWriteOnce]
Events:
  Type    Reason            Age   From                    Message
  ----    ------            ----  ----                    -------
  Normal  SuccessfulCreate  13m   statefulset-controller  create Claim data-mehdb-0 Pod mehdb-0 in StatefulSet mehdb success
  Normal  SuccessfulCreate  13m   statefulset-controller  create Pod mehdb-0 in StatefulSet mehdb successful
  Normal  SuccessfulCreate  12m   statefulset-controller  create Claim data-mehdb-1 Pod mehdb-1 in StatefulSet mehdb success
  Normal  SuccessfulCreate  12m   statefulset-controller  create Pod mehdb-1 in StatefulSet mehdb successful

通过Replicas参数我们可以看到, StatefulSet 下面有两个就绪状态的Pod, 先来看看详情:

$ kubectl describe pod -l app=mehdb
Name:         mehdb-0
Namespace:    default
Priority:     0
Node:         localhost.localdomain/10.0.2.15
Start Time:   Sat, 20 Mar 2021 14:08:11 +0000
Labels:       app=mehdb
              controller-revision-hash=mehdb-9b596f6c9
              statefulset.kubernetes.io/pod-name=mehdb-0
Annotations:  <none>
Status:       Running
IP:           172.17.0.5
IPs:
  IP:           172.17.0.5
Controlled By:  StatefulSet/mehdb
Containers:
  shard:
    Container ID:   docker://917ac165ecfe545faa99bef0ded7e63daff51bcfdaf1a0a1a391a10b5fca6b99
    Image:          quay.io/mhausenblas/mehdb:0.6
    Image ID:       docker-pullable://quay.io/mhausenblas/mehdb@sha256:1f8fd2b656213fe4b15e925afe061dee6083900c7e1e150f762a7e23f8ca90fc
    Port:           9876/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 20 Mar 2021 14:09:06 +0000
    Ready:          True
    Restart Count:  0
    Liveness:       http-get http://:9876/status delay=2s timeout=1s period=10s #success=1 #failure=3
    Readiness:      http-get http://:9876/status%3Flevel=full delay=15s timeout=1s period=30s #success=1 #failure=3
    Environment:
      MEHDB_DATADIR:  /mehdbdata
    Mounts:
      /mehdbdata from data (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-q77th (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  data:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  data-mehdb-0
    ReadOnly:   false
  default-token-q77th:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-q77th
    Optional:    false
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:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  16m (x2 over 16m)  default-scheduler  0/1 nodes are available: 1 pod has unbound immediate PersistentVolumeClaims.
  Normal   Scheduled         16m                default-scheduler  Successfully assigned default/mehdb-0 to localhost.localdomain
  Normal   Pulling           16m                kubelet            Pulling image "quay.io/mhausenblas/mehdb:0.6"
  Normal   Pulled            15m                kubelet            Successfully pulled image "quay.io/mhausenblas/mehdb:0.6" in 52.790537274s
  Normal   Created           15m                kubelet            Created container shard
  Normal   Started           15m                kubelet            Started container shard

命令行不明显的话,我们来看看Dashboard, 可以看到在metadata 部分有个uid字段:

到目前为止, 我们看到的StatefulSet 的表现, 和之前的Deployment并没有展现出什么不同, 那么两者之间究竟有什么区别呢?

StatefulSet 和Deployment的区别

和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

具体的说, 他们之间的差异在于:

特性

Deployment

StatefulSet

适合场景

无状态的应用

有状态的应用

特点

1.pod之间没有顺序

2.所有pod共享存储

3.pod名字包含随机数字

4.service都有ClusterIP,可以负载均衡

1.部署、扩展、更新、删除都要有顺序

2.每个pod都有自己存储,所以都用volumeClaimTemplates,为每个pod都生成一个自己的存储,保存自己的状态

3.pod名字始终是固定的

4.service没有ClusterIP,是headlessservice,所以无法负载均衡,返回的都是pod名,所以pod名字都必须

是否暴露到外网

可以

一般不

请求面向的对象

serviceName

指定pod的域名

灵活性

只能通过service/serviceIp访问到k8s自动转发的pod

可以访问任意一个自定义的pod

易用性

只需要关心Service的信息即可

需要知道要访问的pod启动的名称、headlessService名称

PV/PVC绑定关系的稳定性(多replicas)

(pod挂掉后重启)无法保证初始的绑定关系

可以保证

pod名称稳定性

不稳定,因为是通过template创建,每次为了避免重复都会后缀一个随机数

稳定,每次都一样

启动顺序(多replicas)

随机启动,如果pod宕掉重启,会自动分配一个node重新启动

pod按 app-0、app-1...app-(n-1),如果pod宕掉重启,还会在之前的node上重新启动

停止顺序(多replicas)

随机停止

倒序停止

Pod 名称的区别

通过上面的比较, 我们先来看Pod的名称, 我们之前看到的两个Pod 的名称分别是:

  • mehdb-1
  • mehdb-0

这个确实是和我们之前用Deployment 创建的Pod 名称不一样, 回想我们在Deployment 介绍那篇文章中创建的Pod, 创建的Pod 如下所示:

我们能看到, 使用Deployment 创建的Pod, 其网络名称的后缀是随机的, 这就是其中的区别.

我们再来尝试删掉一个Pod, 看看重新生成的Pod 会不会还是同一个:

$ kubectl delete pod/mehdb-1
pod "mehdb-1" deleted
[vagrant@control-plane ~]$ kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
mehdb-0   1/1     Running   0          55m
mehdb-1   0/1     Running   0          4s
[vagrant@control-plane ~]$ kubectl describe sts mehdb
Name:               mehdb
Namespace:          default
CreationTimestamp:  Sat, 20 Mar 2021 14:08:09 +0000
Selector:           app=mehdb
Labels:             <none>
Annotations:        <none>
Replicas:           2 desired | 2 total
Update Strategy:    RollingUpdate
  Partition:        0
Pods Status:        2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=mehdb
  Containers:
   shard:
    Image:      quay.io/mhausenblas/mehdb:0.6
    Port:       9876/TCP
    Host Port:  0/TCP
    Liveness:   http-get http://:9876/status delay=2s timeout=1s period=10s #success=1 #failure=3
    Readiness:  http-get http://:9876/status%3Flevel=full delay=15s timeout=1s period=30s #success=1 #failure=3
    Environment:
      MEHDB_DATADIR:  /mehdbdata
    Mounts:
      /mehdbdata from data (rw)
  Volumes:  <none>
Volume Claims:
  Name:          data
  StorageClass:  
  Labels:        <none>
  Annotations:   <none>
  Capacity:      1Gi
  Access Modes:  [ReadWriteOnce]
Events:
  Type    Reason            Age                From                    Message
  ----    ------            ----               ----                    -------
  Normal  SuccessfulCreate  56m                statefulset-controller  create Claim data-mehdb-0 Pod mehdb-0 in StatefulSet mehdb success
  Normal  SuccessfulCreate  56m                statefulset-controller  create Pod mehdb-0 in StatefulSet mehdb successful
  Normal  SuccessfulCreate  54m                statefulset-controller  create Claim data-mehdb-1 Pod mehdb-1 in StatefulSet mehdb success
  Normal  SuccessfulCreate  30s (x2 over 54m)  statefulset-controller  create Pod mehdb-1 in StatefulSet mehdb successful

从上面的运行记录中我们可以看到, 当使用kubectl get pos查询Pod 状态时, 上面的命令行输出中显示的是, Pod mehdb不是ready 的状态:

NAME      READY   STATUS    RESTARTS   AGE
mehdb-1   0/1     Running   0          4s

而且Pod mehdb在Age 字段上也和mehdb-0 有明显的不同, 这个在我们再次检查Pod 状态的时候就能看出来.

$ kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
mehdb-0   1/1     Running   0          63m
mehdb-1   1/1     Running   0          7m34s

所以说, 上面的操作, 证明在StatefulSet下面的Pod, 确实会使用同一个网络ID , 而且这个网络ID是有顺序的.

扩容/缩容 StatefulSet

刚才我们使用kubectl deleet 来删除一个StatefulSet中的Pod, 这个在Kuberenete文档中其实是很有风险而且不安全的操作, 下面我们使用kubectl scale 来增加Pod 数量, 看看Pod 命名的变化:

$ kubectl scale sts mehdb --replicas=5
statefulset.apps/mehdb scaled
$ kubectl get pods -w -l app=mehdb
NAME      READY   STATUS    RESTARTS   AGE
mehdb-0   1/1     Running   0          83m
mehdb-1   1/1     Running   0          27m
mehdb-2   0/1     Running   0          39s
mehdb-2   1/1     Running   0          44s
mehdb-3   0/1     Pending   0          0s
mehdb-3   0/1     Pending   0          0s
mehdb-3   0/1     Pending   0          2s
mehdb-3   0/1     ContainerCreating   0          2s
mehdb-3   0/1     Running             0          4s
mehdb-3   1/1     Running             0          31s
mehdb-4   0/1     Pending             0          0s
mehdb-4   0/1     Pending             0          0s
mehdb-4   0/1     Pending             0          3s
mehdb-4   0/1     ContainerCreating   0          3s
mehdb-4   0/1     Running             0          4s
mehdb-4   1/1     Running             0          34s

在运行完kubectl scale 命令之后, 我们紧接着运行了kubetctl get pod -w,注意后面的参数-w表示的是--watch的意思, 就是会持续地监控我们指定的Pod, 可以看出, Kuberentes 慢慢地把StatefulSet 下面的Pod 扩容到5个Pod, 而且命名是非常有顺序的.

删除StatefulSet及其资源

最后来删除StatefulSet.

$ kubectl delete sts/mehdb
statefulset.apps "mehdb" deleted
$ kubectl get sts
No resources found in default namespace.
$ kubectl get pods
No resources found in default namespace.
$ kubectl get pvc
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-mehdb-0   Bound    pvc-ae4c72bc-5821-485b-ba74-b86f3b21b8de   1Gi        RWO            standard       12h
data-mehdb-1   Bound    pvc-a4f548fa-5685-441e-883d-442d4e8f60f3   1Gi        RWO            standard       12h
data-mehdb-2   Bound    pvc-74df0444-0c0b-4509-ab07-dc13c8384079   1Gi        RWO            standard       11h
data-mehdb-3   Bound    pvc-3a23d929-5e5e-4d28-bc30-c862a6b4dec7   1Gi        RWO            standard       11h
data-mehdb-4   Bound    pvc-76111f79-93f4-4021-87d9-120f46906a40   1Gi        RWO            standard       11h
[vagrant@control-plane ~]$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
pvc-3a23d929-5e5e-4d28-bc30-c862a6b4dec7   1Gi        RWO            Delete           Bound    default/data-mehdb-3   standard                11h
pvc-74df0444-0c0b-4509-ab07-dc13c8384079   1Gi        RWO            Delete           Bound    default/data-mehdb-2   standard                11h
pvc-76111f79-93f4-4021-87d9-120f46906a40   1Gi        RWO            Delete           Bound    default/data-mehdb-4   standard                11h
pvc-a4f548fa-5685-441e-883d-442d4e8f60f3   1Gi        RWO            Delete           Bound    default/data-mehdb-1   standard                12h
pvc-ae4c72bc-5821-485b-ba74-b86f3b21b8de   1Gi        RWO            Delete           Bound    default/data-mehdb-0   standard                12h

可以看到, 当删除StatefulSet mehdb的时候, StatefulSet和相应的Pod 都会被立刻删除; 但是相应的PV和PVC则没有被删除, 需要单独删除.

这其实为了数据安全考虑,手工逐一删除就好了.


好了, 对于StatefulSet的操作部分, 先介绍到这里吧, 下面学习下理论知识.


什么是StatefulSets

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易

StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:

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

在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用 由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 或者 ReplicaSet 可能更适用于你的无状态应用部署需要。

使用StatefulSet的限制

  • 给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。
  • 删除或者收缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
  • StatefulSet 当前需要无头服务 来负责 Pod 的网络标识。你需要负责创建此服务。
  • 当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序地且体面地终止,可以在删除之前将 StatefulSet 缩放为 0。
  • 在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新,可能进入需要人工干预 才能修复的损坏状态。


StatefulSet 中的Pod 选择算符

你必须设置 StatefulSet 的 .spec.selector 字段,使之匹配其在 .spec.template.metadata.labels 中设置的标签。在 Kubernetes 1.8 版本之前, 被忽略 .spec.selector 字段会获得默认设置值。 在 1.8 和以后的版本中,未指定匹配的 Pod 选择器将在创建 StatefulSet 期间导致验证错误。

Pod 标识

StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识稳定的存储该标识和 Pod 是绑定的,不管它被调度在哪个节点上。

有序索引

对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号, 从 0 到 N-1,该序号在 StatefulSet 上是唯一的。

稳定的网络 ID

StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。 组合主机名的格式为$(StatefulSet 名称)-$(序号)。 上例将会创建三个名称分别为 mehdb-0、mehdb-1 的 Pod。

StatefulSet 可以使用 无头服务 控制它的 Pod 的网络域。管理域的这个服务的格式为:

$(服务名称).$(命名空间).svc.cluster.local

其中 cluster.local 是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为:

$(pod 名称).$(所属服务的 DNS 域名)

其中所属服务由 StatefulSet 的 serviceName 域来设定。

稳定的存储

Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolume。 在上面的 nginx 示例中,每个 Pod 将会得到基于 StorageClass my-storage-class 提供的 1 Gib 的 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 部署和扩缩保证

  • 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1。
  • 当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。
  • 在将缩放操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
  • 在 Pod 终止之前,所有的继任者必须完全关闭。

StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。 这种做法是不安全的,要强烈阻止。更多的解释请参考 强制删除 StatefulSet Pod。

强制删除 StatefulSet 类型的 Pods

在 StatefulSet 的正常操作中,永远不需要强制删除 StatefulSet 管理的 Pod。 StatefulSet 控制器负责创建、 扩缩和删除 StatefulSet 管理的 Pods。它尝试确保指定数量的从序数 0 到 N-1 的 Pod 处于活跃状态并准备就绪。StatefulSet 确保在任何时候,集群中最多只有一个具有给定标识的 Pod。 这就是所谓的由 StatefulSet 提供的*最多一个(At Most One)*的语义。

应谨慎进行手动强制删除操作,因为它可能会违反 StatefulSet 固有的至多一个的语义。 StatefulSets 可用于运行分布式和集群级的应用,这些应用需要稳定的网络标识和可靠的存储。 这些应用通常配置为具有固定标识固定数量的成员集合。 具有相同身份的多个成员可能是灾难性的,并且可能导致数据丢失 (例如:票选系统中的恶裂场景)。

如何安全地强制删除StatefulSet中的Pod

强制删除不会等待来自 kubelet 对 Pod 已终止的确认消息。 无论强制删除是否成功杀死了 Pod,它都会立即从 API 服务器中释放该名字。 这将让 StatefulSet 控制器创建一个具有相同标识的替身 Pod;因而可能导致正在运行 Pod 的重复, 并且如果所述 Pod 仍然可以与 StatefulSet 的成员通信,则将违反 StatefulSet 所要保证的 最多一个的语义。

当你强制删除 StatefulSet 类型的 Pod 时,你要确保有问题的 Pod 不会再和 StatefulSet 管理的其他 Pod 通信并且可以安全地释放其名字以便创建替代 Pod。

如果要使用 kubectl 1.5 以上版本强制删除 Pod,请执行下面命令:

kubectl delete pods <pod> --grace-period=0 --force

如果你使用 kubectl 的 1.4 以下版本,则应省略 --force 选项:

kubectl delete pods <pod> --grace-period=0

如果在这些命令后 Pod 仍处于 Unknown 状态,请使用以下命令从集群中删除 Pod:

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

请始终谨慎地执行强制删除 StatefulSet 类型的 pods,并且能够完全了解所涉及的风险。

好了, 关于StatefulSet, 我们先简单地介绍到这里吧, 后续会更加深入的例子, 通过创建一个有状态应用的方式,来更好地展示这部分知识.


回顾

本篇文章中简单介绍了StatefulSet 的用法, 展示了StatefulSet 的创建, 扩容, 以及和Deployment的不同之处. 在最后的理论部分, 指出了在删除StatefulSet 里面的Pod 时候的注意事项.



不积跬步无以至千里,继续学习Kubernetes 吧!


最近发表
标签列表