0


【探索 Kubernetes|作业管理篇 系列 14】StatefulSet 存储状态

前言

大家好,我是秋意零。

在上一篇中,我们讲解了 StatefulSet 的拓扑状态;我们发现,它的拓扑状态,就是顺序启动/删除、Pod 名称+编号命名、将 Pod 名称设为 Hostname 名称、通过 Service 无头服务的 DNS 记录访问。

今天,就来看看 StatefulSet 的存储状态。

最近搞了一个扣扣群,旨在技术交流、博客互助,希望各位大佬多多支持!

获取方式:

  • 1.在我主页推广区域,如图:

  • 2.文章底部推广区域,如图:

👿 简介

  • 🏠 个人主页: 秋意零
  • 🧑 个人介绍:在校期间参与众多云计算相关比赛,如:🌟 “省赛”、“国赛”,并斩获多项奖项荣誉证书
  • 🎉 目前状况:24 届毕业生,拿到一家私有云(IAAS)公司 offer,暑假开始实习
  • 🔥 账号:各个平台, 秋意零 账号创作者、 云社区 创建者
  • 💕欢迎大家:欢迎大家一起学习云计算,走向年薪 30 万

系列文章目录


【云原生|探索 Kubernetes-1】容器的本质是进程
【云原生|探索 Kubernetes-2】容器 Linux Cgroups 限制
【云原生|探索 Kubernetes 系列 3】深入理解容器进程的文件系统
【云原生|探索 Kubernetes 系列 4】现代云原生时代的引擎
【云原生|探索 Kubernetes 系列 5】简化 Kubernetes 的部署,深入解析其工作流程


更多点击专栏查看:深入探索 Kubernetes

正文开始

  • 快速上船,马上开始掌舵了(Kubernetes),距离开船还有 3s,2s,1s...

一、解决的实际问题

StatefulSet 的存储状态,主要使用 Persistent Volume Claim(PVC 持久卷声明) 功能实现。

在前面介绍 Pod 时,要在一个 Pod 里生命 Volume,只要在 Pod 里加上 spec.voluems 字段即可。然后,在它里面定义一个具体类型的 Voluem,比如:hostPath。

可如果,你并不知道有那些 Volume 类型可以用,这要怎么办呢?

  • 具体的说,作为一个开发者,我可能对持久化存储项目(Ceph、GlusterFS等)一窍不通,对 Kubernetes 也不懂,自然也不知道对应的 Volume 定义文件。
  • 所谓“术业有专攻”,这些关于 Volume 的管理和远程持久化存储的知识,不仅超越了开发者的知识储备,还会有暴露公司基础设施秘密的风险。

二、PV、PVC 简单介绍

比如,下面这个例子,就是一个声明了 Ceph RBD 类型 Volume 的 Pod:

  • 1.如果,不懂 Ceph RBD 使用方法,那么这个 Pod 里 Volumes 字段,自然是看不懂的;
  • 2.这个 Ceph RBD 对应的存储服务器地址、用户名、授权文件的位置,也都暴露给了全公司的所有开发人员,这是一个典型的信息被“过度暴露”的例子。
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: rbd
  5. spec:
  6. containers:
  7. - image: kubernetes/pause
  8. name: rbd-rw
  9. volumeMounts:
  10. - name: rbdpd
  11. mountPath: /mnt/rbd
  12. volumes:
  13. - name: rbdpd
  14. rbd:
  15. monitors:
  16. - '10.16.154.78:6789'
  17. - '10.16.154.82:6789'
  18. - '10.16.154.83:6789'
  19. pool: kube
  20. image: foo
  21. fsType: ext4
  22. readOnly: true
  23. user: admin
  24. keyring: /etc/ceph/keyring
  25. imageformat: "2"
  26. imagefeatures: "layering"

这也是为什么,Kubernetes 引入了一组叫

  1. Persistent Volume Claim

(PVC)和

  1. Persistent Volume

(PV)的 API 对象,大大降低了用户声明和使用持久化 Volume 的门槛。因为开发人员只需要关心 PVC 的声明,不需要关心 PV 的声明。

PV 和 PVC 之间的关系:

  • PVC 将 PV 看做是一个存储池,间接使用磁盘;而 PV 实际上才是真是直接使用磁盘和各种存储服务器的对象。

开发者使用 PVC

举个例子:有了 PVC 之后,一个开发人员想要使用一个 Volume,只需要简单的两步即可。

1.定义一个 PVC,声明想要的 Volume 的属性:

  • 可以看到,PVC 对象中不需要任何的 Volume 细节的字段,只有描述 Volume 的属性和定义。比如:storage: 1Gi,表示我要使用的 Volume 大小至少为 1 GiB;accessModes: [ReadWriteOnce],表示这个 Volume 的访问模式是可读写的,并且只能被挂载在一个节点上而非被多个节点挂载。
  1. kind: PersistentVolumeClaim
  2. apiVersion: v1
  3. metadata:
  4. name: pv-claim
  5. spec:
  6. accessModes:
  7. - ReadWriteOnce
  8. resources:
  9. requests:
  10. storage: 1Gi

2.在应用的 Pod 中,声明使用这个 PVC:

  • 可以看到,Pod 中的 Volumes 字段定义了,persistentVolumeClaim 字段来使用 PVC 所请求的磁盘资源。只需要指定 PVC 的名字,而完全不必关心 Volume 本身的定义。
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: pv-pod
  5. spec:
  6. containers:
  7. - name: pv-container
  8. image: nginx
  9. ports:
  10. - containerPort: 80
  11. name: "http-server"
  12. volumeMounts:
  13. - mountPath: "/usr/share/nginx/html"
  14. name: pv-storage
  15. volumes:
  16. - name: pv-storage
  17. persistentVolumeClaim:
  18. claimName: pv-claim

运维人员负载 PV

PVC 创建之后,会自动为它绑定一个符合条件的 Voluem(这个 Voluem 是 PV),从上述的 PV 和 PVC 的结构图中,这个 Volume 就是从 PV 中来的,并且由运维人员维护 PV 对象。PV 如下:

  • 可以看到,这个 PV 对象的 spec.rbd 字段,正是我们前面介绍过的 Ceph RBD Volume 的详细定义。而且,它还声明了这个 PV 的容量是 10 GiB。这样,Kubernetes 就会为我们刚刚创建的 PVC 对象绑定这个 PV(只要 PVC 请求的容量在 PV 所请求容量的范围内,PVC 就能自动与对应的 PV 绑定)。
  1. kind: PersistentVolume
  2. apiVersion: v1
  3. metadata:
  4. name: pv-volume
  5. labels:
  6. type: local
  7. spec:
  8. capacity:
  9. storage: 10Gi
  10. accessModes:
  11. - ReadWriteOnce
  12. rbd:
  13. monitors:
  14. # 使用 kubectl get pods -n rook-ceph 查看 rook-ceph-mon- 开头的 POD IP 即可得下面的列表
  15. - '10.16.154.78:6789'
  16. - '10.16.154.82:6789'
  17. - '10.16.154.83:6789'
  18. pool: kube
  19. image: foo
  20. fsType: ext4
  21. readOnly: true
  22. user: admin
  23. keyring: /etc/ceph/keyring

PV 和 PVC 的设计,就类似于“接口”和“实现”的思想。开发者只要知道并会使用“接口”,不管“接口”背后的实现,类似: PVC;而运维人员负责给“接口”绑定具体的实现,类似:PV

这种解耦,就避免了因为向开发者暴露过多的存储系统细节而带来的隐患。此外,这种职责的分离,往往也意味着出现事故时可以更容易定位问题和明确责任,从而避免“扯皮”现象的出现

三、存储状态

由于 PV、PVC 的设计,我们就能使用 StatefulSet 利用 PV、PVC 来实现 StatefulSet 存储状态,如下所示:

  • 可以看到,StatefulSet 中我们使用了 spec.volumeClaimTemplates字段,从名字可以看出它和spec.template(PodTemplate)的作用类似,所以 StatefulSet 会自动创建 PVC 就像自动创建所期望的副本数 Pod 一样;
  • spec.template:一个 Pod 模板,控制器所创建管理的 Pod,都以这个配置为基础;
  • spec.volumeClaimTemplates:一个 PVC 模板,控制器所创建管理的 PVC,都以这个配置为基础;
  • 更重要的是,这个 PVC 的名字,会被分配一个与这个 Pod 完全一致的编号。并且对应编号的 Pod 和 PVC 都会与之匹配,使用对应编号的 PVC。比如:名叫 web-0 的 Pod 的 volumes 字段,它会声明使用名叫 www-web-0 的 PVC,从而挂载到这个 PVC 所绑定的 PV。
  1. apiVersion: apps/v1
  2. kind: StatefulSet
  3. metadata:
  4. name: web
  5. spec:
  6. serviceName: "nginx"
  7. replicas: 3
  8. selector:
  9. matchLabels:
  10. app: nginx
  11. template:
  12. metadata:
  13. labels:
  14. app: nginx
  15. spec:
  16. containers:
  17. - name: nginx
  18. image: nginx
  19. imagePullPolicy: IfNotPresent
  20. ports:
  21. - containerPort: 80
  22. name: web
  23. volumeMounts:
  24. - name: www
  25. mountPath: /usr/share/nginx/html
  26. volumeClaimTemplates:
  27. - metadata:
  28. name: www
  29. spec:
  30. accessModes:
  31. - ReadWriteOnce
  32. resources:
  33. requests:
  34. storage: 1Gi

这个自动创建的 PVC,与 PV 绑定成功后(前提是首先创建了 PV或通过 Dynamic Provisioning 的方式,自动为 PVC 匹配创建 PV),就会进入 Bound 状态,这就意味着这个 Pod 可以挂载并使用这个 PV 了(详细内容会在存储章节讲解)。PV 的 YAML 文件如下:

  1. [root@master01 yaml]# cat > pv.yaml << EOF
  2. apiVersion: v1
  3. kind: PersistentVolume
  4. metadata:
  5. name: my-pv1
  6. spec:
  7. capacity:
  8. storage: 10Gi
  9. accessModes:
  10. - ReadWriteOnce
  11. persistentVolumeReclaimPolicy: Retain
  12. hostPath:
  13. path: /data/my-pv1
  14. ---
  15. apiVersion: v1
  16. kind: PersistentVolume
  17. metadata:
  18. name: my-pv2
  19. spec:
  20. capacity:
  21. storage: 10Gi
  22. accessModes:
  23. - ReadWriteOnce
  24. persistentVolumeReclaimPolicy: Retain
  25. hostPath:
  26. path: /data/my-pv2
  27. ---
  28. apiVersion: v1
  29. kind: PersistentVolume
  30. metadata:
  31. name: my-pv3
  32. spec:
  33. capacity:
  34. storage: 10Gi
  35. accessModes:
  36. - ReadWriteOnce
  37. persistentVolumeReclaimPolicy: Retain
  38. hostPath:
  39. path: /data/my-pv3
  40. EOF

创建 PV 与 StatefulSet

1.创建 PV 与 StatefulSet:

  1. [root@master01 yaml]# kubectl apply -f pv.yaml
  2. persistentvolume/my-pv1 created
  3. persistentvolume/my-pv2 created
  4. persistentvolume/my-pv3 created
  5. [root@master01 yaml]# kubectl apply -f statefulset-pvc.yaml
  6. statefulset.apps/web created

2.查看 Pod 是否运行:

  1. [root@master01 ~]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. web-0 1/1 Running 0 13m
  4. web-1 1/1 Running 0 13m
  5. web-2 1/1 Running 0 13m

3.查看 PV 与 PVC:

  • PVC,根据 StatefulSet 控制器的 volumeClaimTemplates 字段,自动创建;并且可以看到 PVC,都以“<PVC 名字 >-<StatefulSet 名字 >-< 编号 >”的方式命名,并且与 PV 都处于 Bound 状态。前面提到过,StatefulSet 创建出来的所有 Pod,都会声明使用并使用对应编号的 PVC。
  1. [root@master01 ~]# kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. my-pv1 10Gi RWO Retain Bound default/www-web-1 14m
  4. my-pv2 10Gi RWO Retain Bound default/www-web-0 14m
  5. my-pv3 10Gi RWO Retain Bound default/www-web-2 14m
  6. [root@master01 ~]# kubectl get pvc
  7. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  8. www-web-0 Bound my-pv2 10Gi RWO 14m
  9. www-web-1 Bound my-pv1 10Gi RWO 14m
  10. www-web-2 Bound my-pv3 10Gi RWO 14m

验证 StatefulSet 的存储状态

现在,我们要做这样一件事。

  • 首先,在 Pod 中的 Volume 的 Web 目录中写入一个文件;
  • 其次,查看 Volume 的 Web 目录中的数据;
  • 最后,删除所以 Pod,并再次查看 Volume 中的数据是否还与之前 Pod 中的数据对应。

1.在 Pod 中的 Volume 的 Web 目录中写入一个文件:

  1. $ for i in 0 1 2; do kubectl exec web-$i -- sh -c 'echo hello $(hostname) > /usr/share/nginx/html/index.html'; done
  • for 循环展开状态如下:

2.查看 Volume 的 Web 目录中的数据:

  1. $ for i in 0 1 2; do kubectl exec -it web-$i -- curl localhost; done
  2. hello web-0
  3. hello web-1
  4. hello web-2
  • for 循环展开状态如下:

3.删除所有 Pod,并再次查看 Volume 的 Web 目录中的数据是否还与之前 Pod 中的数据对应:

  1. **1、删除 Pod**
  1. [root@master01 ~]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. web-0 1/1 Running 0 53m
  4. web-1 1/1 Running 0 53m
  5. web-2 1/1 Running 0 53m
  6. [root@master01 ~]#
  7. [root@master01 ~]# kubectl delete pod --all
  8. pod "web-0" deleted
  9. pod "web-1" deleted
  10. pod "web-2" deleted
  1. **2、验证,访问 web 数据是否还一致**
  1. [root@master01 ~]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. web-0 1/1 Running 0 22s
  4. web-1 1/1 Running 0 20s
  5. web-2 1/1 Running 0 18s
  6. [root@master01 ~]#
  7. [root@master01 ~]# for i in 0 1 2; do kubectl exec -it web-$i -- curl localhost; done
  8. hello web-0
  9. hello web-1
  10. hello web-2

可以看到,我们再次查看访问 web 时,数据还是一致的。也就是说,我们重建之后的 Pod 还会与之前绑定过的 PVC 再次绑定。

这是怎么做到的呢?

  • 重建的 web-0 Pod 被创建出来之后,Kubernetes 为它查找名叫 www-web-0 的 PVC 时,就会直接找到旧 Pod 遗留下来的同名的 PVC,进而找到跟这个 PVC 绑定在一起的 PV。是通过 Pod 与 PVC 的命名编号判断是否与之绑定。

总结

StatefulSet 工作原理的三个方面:

  • 首先,StatefulSet 的控制器直接管理的是 Pod。
  • 其次,Kubernetes 通过 Headless Service,为这些有编号的 Pod,在 DNS 服务器中生成带有同样编号的 DNS 记录。
  • 最后,StatefulSet 还为每一个 Pod 分配并创建一个同样编号的 PVC。


本文转载自: https://blog.csdn.net/qq_48450494/article/details/131412162
版权归原作者 秋意零 所有, 如有侵权,请联系我们删除。

“【探索 Kubernetes|作业管理篇 系列 14】StatefulSet 存储状态”的评论:

还没有评论