k8s入门到实战(九)—— StatefulSet详细介绍及示例

Statefulset

如果我们需要部署多个 MySQL 实例,就需要用到 StatefulSet。

StatefulSet 是用来管理有状态的应用。一般用于管理数据库、缓存等。

与 Deployment 类似, StatefulSet 用来管理 pod 集合的部署和扩缩。

Deployment 无序(mysql-xxxxxx-xxx mysql-xxxxxx-xxx mysql-xxxxxx- xxx)用来部署无状态应用。StatefulSet(mysql-xxxxxx-1 mysql-xxxxxx-2 mysql-xxxxxx-3)用来有状态应用。

StatefulSet 的核心功能就是通过某种方式记录这些状态,然后在 Pod 被重新创建时能够为新 Pod 恢复这些状态

StatefulSet 本质上是 Deployment 的一种变体,在 v1.9 版本中已成为 GA 版本,它为了解决有状态服务的问题,它所管理的 Pod 拥有固定的 Pod 名称,启停顺序,在 StatefulSet 中,Pod 名字称为网络标识(hostname),还必须要用到共享存储。

在 Deployment 中,与之对应的服务是 service,而在 StatefulSet 中与之对应的 headless service,headless service,即无头服务,与 service 的区别就是它没有 Cluster IP:,解析它的名称时将返回该 Headless Service 对应的全部 Pod 的 Endpoint 列表。

除此之外,StatefulSet 在 Headless Service 的基础上又为 StatefulSet 控制的每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为:

$(podname).(headless server name)

$(podname).(headless server name).namespace.svc.cluster.local

StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过 Pod 域名来通信而非 Pod IP,因为当 Pod 所在 Node 发生故障时,Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化。

StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:(service name).(namespace).svc.cluster.local,其中,"cluster.local" 指的是集群的域名。

什么是 statefulset

在 k8s 中,StatefulSet 是一种用于管理有状态应用的资源对象。与一般的 deployment 不同,StatefulSet 提供了一种有序部署和管理有状态应用的方式。

StatefulSet 主要用于部署需要持久化存储和唯一标识的应用,例如数据库或分布式存储系统。它为每个 Pod 实例分配一个唯一的标识符,并根据这个标识符进行有序的创建、更新和删除操作。

StatefulSet 在创建和删除 Pod 实例时,会按照一定的顺序进行操作,确保每个 Pod 实例的唯一标识符和网络标识符的稳定性。这使得有状态应用可以保持持久化存储的连接,并且具备可靠的网络标识符,以便于应用之间的通信。

StatefulSet 还提供了一些其他的功能,如有序的缩放、滚动更新、有状态服务的 DNS 解析等。它使用有状态服务的特性,提供了高可用性、可伸缩性和可靠性。

总之,StatefulSet 是 k8s 中用于管理有状态应用的一种资源对象,它为有状态应用提供了有序的部署、更新和删除操作,并保证每个 Pod 实例的唯一标识符和网络标识符的稳定性。

statefulset 优点

k8s 中的 StatefulSet 具有以下优点:

  • 稳定的网络标识符:每个 StatefulSet 的 Pod 实例都具有稳定的网络标识符,这使得其他应用可以通过直接使用该标识符来访问和通信。这对于有状态应用非常重要,因为它们通常需要持久的网络标识符来保持连接。
  • 有序的创建和删除:StatefulSet 按照一定的顺序创建和删除 Pod 实例,这确保了应用在启动和关闭时的有序性。这对于有状态应用非常重要,因为它们通常需要按顺序启动和关闭,以避免数据丢失或损坏。
  • 持久化存储:StatefulSet 可以与持久化存储卷(如PersistentVolume)结合使用,以确保数据的持久性和可靠性。每个 Pod 实例都可以使用独立的存储卷,这对于有状态应用非常重要,因为它们通常需要在重新启动后保留数据。
  • 有序的缩放和滚动更新:StatefulSet 支持有序的缩放和滚动更新,可以根据需要增加或减少 Pod 实例的数量,并确保这些操作按顺序进行。这对于有状态应用非常重要,因为它们的缩放和更新通常需要遵循特定的顺序和规则。
  • 服务发现和 DNS 解析:StatefulSet 可以为每个 Pod 实例创建一个稳定的网络 DNS 名称,使得其他应用可以通过该名称来发现和访问它们。这对于有状态应用非常重要,因为它们通常需要通过名称进行服务发现和通信。

总的来说,StatefulSet 在 k8s 中为有状态应用提供了一种可靠、稳定和有序的部署、管理和操作方式,使得有状态应用可以更好地运行和维护。

statefulset 示例

  1. 编辑 yaml 文件nginx-sf.yaml
yaml 复制代码
# headless service,即无头服务,nginx, ClusterIP: None
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - port: 80
      name: web
  clusterIP: None
  selector:
    app: nginx
---
# StatefulSet, web, 3个副本pod nginx,自动创建3个pvc - 自动创建3个pv
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.20.1
          ports:
            - containerPort: 80
              name: web
          volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
  # 存储卷申请模版,指定了pvc名称,pvc的大小,申请的类型....
  volumeClaimTemplates: # pvc template
    - metadata:
        name: www
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "local-path"
        resources:
          requests:
            storage: 1Gi

注意:这文件中使用的 storageClassName 为 local-path,是在之前介绍存储类的时候的创建的,如果没有这个 local-path 是无法创建 pvc 的,如果没有,编写 yaml 文件创建 local-path

yaml 复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-path
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
  1. 执行 yaml 文件
sh 复制代码
[root@k8s-master k8s]# vi nginx-sf.yaml
[root@k8s-master k8s]# kubectl apply -f nginx-sf.yaml 
service/nginx created
statefulset.apps/web created
  1. 查看 pod 和 pvc
sh 复制代码
[root@k8s-master k8s]# kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-3050033b-638f-4d03-80d6-be6e270ac02a   1Gi        RWO            local-path     34m
www-web-1   Bound    pvc-e22cd565-5f89-4b57-8be7-c4f648d52214   1Gi        RWO            local-path     9m15s
www-web-2   Bound    pvc-e79aa081-176a-439f-875e-8119c021930e   1Gi        RWO            local-path     8m53s
[root@k8s-master k8s]# kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          81s
web-1   1/1     Running   0          57s
web-2   1/1     Running   0          48s
  1. pod 和 pvc 创建完毕,名字也是按顺序起的,不是 deployment 那种随机字符串的格式
  2. 进入容器内部,访问首页
sh 复制代码
[root@k8s-master k8s]# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE     IP                NODE        NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          3m16s   192.169.36.106    k8s-node1   <none>           <none>
web-1   1/1     Running   0          2m52s   192.169.169.161   k8s-node2   <none>           <none>
web-2   1/1     Running   0          2m43s   192.169.36.107    k8s-node1   <none>           <none>
[root@k8s-master k8s]# kubectl exec -it web-0 /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@web-0:/# curl 192.169.36.106:80
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>

访问成功,我们创建的没有问题

为什么需要 headless service 无头服务?

在用 Deployment 时,每一个 Pod 名称是没有顺序的,是随机字符串,因此是 Pod 名称是无序的,但是在 statefulset 中要求必须是有序 ,每一个pod 不能被随意取代,pod 重建后 pod 名称还是一样的。而 pod IP 是变化的,所以是以 Pod 名称来识别。pod 名称是 pod 唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个 Pod 一个唯一的名称 。

为什么需要 volumeClaimTemplate?

对于有状态的副本集都会用到持久存储,对于分布式系统来讲,它的最大特点是数据是不一样的,所以各个节点不能使用同一存储卷,每个节点有自已的专用存储,但是如果在 Deployment 中的 Pod template 里定义的存储卷,是所有副本集共用一个存储卷,数据是相同的,因为是基于模板来的 ,而 statefulset 中每个 Pod 都要自已的专有存储卷,所以 statefulset 的存储卷就不能再用 Pod 模板来创建了,于是 statefulSet 使用 volumeClaimTemplate,称为卷申请模板,它会为每个 Pod 生成不同的 pvc,并绑定 pv, 从而实现各 pod 有专用存储。这就是为什么要用volumeClaimTemplate 的原因。

稳定的存储:在 StatefulSet 中使用 VolumeClaimTemplate,为每个 Pod 创建持久卷声明(PVC)。 请注意,当 Pod 或者 StatefulSet 被删除时,持久卷声明和关联的持久卷不会被删除。

结论

使用 StatefulSet 创建 pod,按照顺序依次创建。 StatefulSet name 名 - 0/1/2/3/4 依次类推

无论如何删除 pod,再次启动,名称不会发生变化。

动态创建的 pvc 名字也不会发生变化。

在真实的项目中,redis,mysql 持久化存储的 pod 都是使用 StatefulSet 来部署的,创建有状态应用。

无论 pod ip 怎么变化,服务名不变,项目中就可以通过服务名来访问,域名只能在容器内部访问(对应项目启动的 pod)

测试:使用域名访问

sh 复制代码
root@web-0:/# curl web-1.nginx
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>
root@web-0:/# curl web-2.nginx.default.svc.cluster.local
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>

可以看到,完全可以通过域名来访问

总结

有状态应用 statefulset 无状态应用 deployment

Headless Service 给 deployment 的 pod 分了名字,然后 StatefulSet 将这种名字锁死。

StatefulSet 可以让 pod 的名字不变 + PVC,使得 pod 对应的 volume 不会变,也就是存储了存储状态。

Pod 标识:在具有 N 个副本的 StatefulSet中,每个 Pod 会被分配一个从 0 到 N-1 的整数序号,该序号在此 StatefulSet 上是唯一的。

部署扩缩容前提保证:

  • 对于包含 N 个 副本的 ,当部署 Pod 时,它们是依次创建的,顺序为 0...N-1。
  • 当删除 Pod 时,它们是逆序终止的,顺序为 N-1...0。
  • 在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
相关推荐
chuanauc5 小时前
Kubernets K8s 学习
java·学习·kubernetes
小张是铁粉5 小时前
docker学习二天之镜像操作与容器操作
学习·docker·容器
烟雨书信5 小时前
Docker文件操作、数据卷、挂载
运维·docker·容器
IT成长日记5 小时前
【Docker基础】Docker数据卷管理:docker volume prune及其参数详解
运维·docker·容器·volume·prune
这儿有一堆花5 小时前
Docker编译环境搭建与开发实战指南
运维·docker·容器
LuckyLay5 小时前
Compose 高级用法详解——AI教你学Docker
运维·docker·容器
Uluoyu5 小时前
redisSearch docker安装
运维·redis·docker·容器
IT成长日记9 小时前
【Docker基础】Docker数据持久化与卷(Volume)介绍
运维·docker·容器·数据持久化·volume·
疯子的模样14 小时前
Docker 安装 Neo4j 保姆级教程
docker·容器·neo4j
虚伪的空想家14 小时前
rook-ceph配置dashboard代理无法访问
ceph·云原生·k8s·存储·rook