在k8s中工作负载资源StatefulSet用于管理有状态应用。

什么是无状态?

组成一个应用的pod是对等的,它们之前没有关联和依赖关系,不依赖外部存储。

即我们上篇小作文中deployment创建的nginx pod ,他们是完全一样的,任何一个pod 被移除后依然可以正常工作。由于不依赖外部存储,它们可以被轻易的调度到任何 node 上。

什么是有状态?

显然无状态的反面就是有状态了,pod之间可能包含主从、主备的相互依赖关系,甚至对启动顺序也有要求。更关键的是这些pod 需要外部存储,一旦pod被清除或调度后,怎么把pod 和原来的外部数据联系起来?这就是StatefulSet厉害的地方。

StatefulSet将这些状态应用进行记录,在需要的时候恢复。


StatefulSet如何展开这些工作?

一、维护应用拓扑状态

通过dns记录为 pod 分配集群内唯一、稳定的网络标识。即只要保证pod 的名称不变,pod被调度到任何节点或者ip如何变更都能被找到。

在 k8s 中Service用来来将一组 Pod 暴露给外界访问的一种机制。当创建的service 中clusterIP为None 时(headless 无头服务), 不会进行负载均衡,也不会为该服务分配集群 IP。仅自动配置 DNS。

这样我们集群中的 一个pod 将被绑定到一条DNS记录:

  1. <pod-name>.<svc-name>.<namespace>.svc.cluster.local

通过解析这个地址就能找到pod的IP 。

下面我们创建一个headless service,将clusterIP配置为 None

  1. #headless-service.yml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: nginx-headless
  6. spec:
  7. ports:
  8. - name: nginx-service-port
  9. port: 80
  10. targetPort: 9376
  11. clusterIP: None
  12. selector:
  13. app: nginx

这个service将会绑定 app=nginx标签的pod,我们通过kubectl apply -f headless-service.yml应用service 并通过get 查看:

  1. $ kubectl get service
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18d
  4. nginx-headless ClusterIP None <none> 80/TCP 4h48m

nginx-headless 这个headless service创建成功了。接着我们创建一个StatefulSet:

  1. #nginx-statefulset.yml
  2. apiVersion: apps/v1
  3. kind: StatefulSet
  4. metadata:
  5. name: nginx-statefulset
  6. spec:
  7. serviceName: "nginx-headless"
  8. replicas: 3
  9. selector:
  10. matchLabels:
  11. app: nginx
  12. template:
  13. metadata:
  14. labels:
  15. app: nginx
  16. spec:
  17. containers:
  18. - name: nginx-web
  19. image: nginx:1.17
  20. ports:
  21. - containerPort: 82

nginx-statefulset 将会绑定我们前面的service nginx-headless并创建三个nginx pod。

我们查看创建的pod ,StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。同时statefulset创建出来的pod 名称以$(StatefulSet name)-$(order)开始编号。

  1. $ kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. nginx-statefulset-0 1/1 Running 0 18s
  4. nginx-statefulset-1 1/1 Running 0 15s
  5. nginx-statefulset-2 1/1 Running 0 12s
  6. $ kubectl exec nginx-statefulset-0 -- sh -c hostname
  7. nginx-statefulset-0

其实他们的创建顺序也是从0-2,当我们删除这些pod时,statefulset 马上重建出相同名称的Pod 。

我们通过statefulset 的event可以观测到这个过程:

  1. $ kubectl describe nginx-statefulset
  2. Events:
  3. Type Reason Age From Message
  4. ---- ------ ---- ---- -------
  5. Normal SuccessfulCreate 7m43s statefulset-controller create Pod nginx-statefulset-0 in StatefulSet nginx-statefulset successful
  6. Normal SuccessfulCreate 7m40s statefulset-controller create Pod nginx-statefulset-1 in StatefulSet nginx-statefulset successful
  7. Normal SuccessfulCreate 7m37s statefulset-controller create Pod nginx-statefulset-2 in StatefulSet nginx-statefulset successful

现在我们来看一下 pod 是否存在于 DNS 记录中:

  1. kubectl run -it --image busybox busybox --rm /bin/sh

运行一个一次性 pod busybox ,接着使用 ping 命令查询之前提到的规则构建名称nginx-statefulset-0.nginx-headless.default.svc.cluster.local

解析的IP与如下nginx-statefulset-0相符。

这样我们使用pod名称通过DNS就可以找到这个pod 再加上StatefulSet可以按顺序创建出不变名称的 pod ,即一个应用通过StatefulSet准确维护其拓扑状态


二、维护应用存储状态

k8s为应对应用的数据存储需求提供了卷的概念(volume)以及提供持久化存储的PVC( PersistentVolumeClaim)PV( PersistentVolume)当一个pod 和 PVC绑定后,即使pod 被移除,PVC和PV仍然保留在集群中,pod 再次被创建后会自动绑定到之前的PVC。他们看起来是这样的:

这里我们以讨论statefulset持久化存储为主,对于k8s存储本身不了解的同学可以参考k8s官方文档存储章节storage

首先我们创建存储目录 /data/volumes/ 以及一个本地的local类型(使用节点上的文件或目录来模拟网络附加存储)的PV:

  1. #pv-local.yml
  2. apiVersion: v1
  3. kind: PersistentVolume
  4. metadata:
  5. name: pv-local
  6. spec:
  7. capacity:
  8. storage: 5Gi
  9. volumeMode: Filesystem
  10. accessModes:
  11. - ReadWriteMany
  12. persistentVolumeReclaimPolicy: Delete
  13. storageClassName: local-storage
  14. local:
  15. path: /data/volumes/
  16. nodeAffinity:
  17. required:
  18. nodeSelectorTerms:
  19. - matchExpressions:
  20. - key: kubernetes.io/hostname
  21. operator: In
  22. values:
  23. - minikube

PV是集群中的一块存储,它声明了后端使用的真实存储,通常会由K8S管理员创建。我们在pv-local中声明了后端存储类型为local挂载到目录 /data/volumes/ , 存储卷类名为local-storage,1Gb容量,访问模式ReadWriteMany -- 卷可以被多个个节点以读写方式挂载。亲和的节点为minikube

我们通过get来查看这个PV:

  1. $ kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pv-local 5Gi RWX Delete Available local-storage 25m

此时PV的状态为available,还未与任何PVC绑定。我们通过创建PV使集群得到了一块存储资源,但此时还不属于你的应用,我们需要通过PVC去构建一个使用它的”通道“。

  1. #app1-pvc.yml
  2. apiVersion: v1
  3. kind: PersistentVolumeClaim
  4. metadata:
  5. name: app1-pvc
  6. spec:
  7. storageClassName: local-storage
  8. accessModes:
  9. - ReadWriteMany
  10. resources:
  11. requests:
  12. storage: 1Gi

现在我们开辟好一个5Gb容量的存储通道(PVC),此时PV和PVC已通过 storageClassName自动形成绑定。这样PV和PVC的status 皆为Bound

  1. $ kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pv-local 5Gi RWX Delete Bound default/app-pvc local-storage 25m
  4. $ kubectl get pvc
  5. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  6. app-pvc Bound pv-local 5Gi RWX local-storage 27m

上面我们创建好通道,接下来要在我们statefuset中绑定这个通道,才能顺利使用存储。

  1. # nginx-statefulset.yml
  2. apiVersion: apps/v1
  3. kind: StatefulSet
  4. metadata:
  5. name: nginx-statefulset
  6. spec:
  7. serviceName: "nginx-headless"
  8. replicas: 3
  9. selector:
  10. matchLabels:
  11. app: nginx
  12. template:
  13. metadata:
  14. labels:
  15. app: nginx
  16. spec:
  17. nodeName: minikube
  18. volumes:
  19. - name: app-storage
  20. persistentVolumeClaim:
  21. claimName: app-pvc
  22. containers:
  23. - name: nginx-web
  24. image: nginx:1.17
  25. ports:
  26. - containerPort: 80
  27. name: nginx-port
  28. volumeMounts:
  29. - mountPath: /usr/share/nginx/html
  30. name: app-storage

与之前的statefulset相比我们在pod 模板中添加了volume 已经 volumeMounts,这样使用这个statefulset 所创建的pod都将挂载 我们前面定义的PVC app-pvc,应用nginx-statefulset.yml后我们进入到pod 检验一下目录是否被正确挂载。

  1. $ kubectl exec -it nginx-statefulset-0 -- /bin/bash
  2. root@nginx-statefulset-0:/# cat /usr/share/nginx/html/index.html
  3. hello pv

查看本地目录文件:

  1. root@minikube:/# cat /data/volumes/index.html
  2. hello pv

接着我们在pod 中修改index.html内容为并将pod删除,检验重载后的 pod 存储数据是否能被找回。

  1. root@nginx-statefulset-0:/# echo "pod data" > /usr/share/nginx/html/index.html

删除带有标签app=nginx的pod ,由于statefulset的控制器使pod按顺序被重建:

  1. $ kubectl delete pod -l app=nginx
  2. pod "nginx-statefulset-0" deleted
  3. pod "nginx-statefulset-1" deleted
  4. pod "nginx-statefulset-2" deleted
  5. $ kubectl get pod
  6. NAME READY STATUS RESTARTS AGE
  7. nginx-statefulset-0 1/1 Running 0 9s
  8. nginx-statefulset-1 1/1 Running 0 6s
  9. nginx-statefulset-2 0/1 ContainerCreating 0 3s

毫无疑问,pod 数据完好无损:

  1. $ kubectl exec -it nginx-statefulset-0 -- /bin/bash
  2. root@nginx-statefulset-0:/# cat /usr/share/nginx/html/index.html
  3. pod data

也就是说虽然我们的pod被删除了,但是PV已经PV依然保留在集群中,当pod 被重建后,它依然会去找定义的claimName: app-pvc这个PVC,接着挂载到容器中。

这里我们一个PVC 绑定了多个节点,其实可以为每一个 statefulset中的pod 创建PVC,可以自行了解。

k8s存储可操作性非常强,这里只在statefulset下做了简单的演示。后续我们会对k8s存储做更深入的了解。


三、总结

这篇小作文我们一起学习了k8s中工作负载资源StatefulSet是如何管理有状态应用的,主要从维护应用拓扑状态和存储状态两个方面做了简单介绍。这样我们对statefulset这个工作资源有了大体了解:StatefulSet 与 Deployment 相比,它为每个管理的 Pod 都进行了编号,使Pod有一个稳定的启动顺序,并且是集群中唯一的网络标识。有了标识后使用PV、PVC对存储状态进行维护。


希望小作文对你有些许帮助,如果内容有误请指正。

您可以随意转载、修改、发布本文,无需经过本人同意。 通过博客阅读:iqsing.github.io

k8s负载资源StatefulSet工作解析的更多相关文章

  1. K8s容器资源限制

    在K8s中定义Pod中运行容器有两个维度的限制: 1. 资源需求:即运行Pod的节点必须满足运行Pod的最基本需求才能运行Pod. 如: Pod运行至少需要2G内存,1核CPU    2. 资源限额: ...

  2. k8s控制器资源(五)

    Pod pod在之前说过,pod是kubernetes集群中是最小的调度单元,pod中可以运行多个容器,而node又可以包含多个pod,关系如下图: 在对pod的用法进行说明之前,有必要先对docke ...

  3. Kubernetes K8S之资源控制器Job和CronJob详解

    Kubernetes的资源控制器Job和CronJob详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...

  4. k8s控制器资源

    k8s控制器资源   Pod pod在之前说过,pod是kubernetes集群中是最小的调度单元,pod中可以运行多个容器,而node又可以包含多个pod,关系如下图: 在对pod的用法进行说明之前 ...

  5. k8s核心资源之Pod概念&入门使用讲解(三)

    目录 1. k8s核心资源之Pod 1.1 什么是Pod? 1.2 Pod如何管理多个容器? 1.3 Pod网络 1.4 Pod存储 1.5 Pod工作方式 1.5.1 自主式Pod 1.5.2 控制 ...

  6. Kubernetes K8S之资源控制器StatefulSets详解

    Kubernetes的资源控制器StatefulSet详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...

  7. 8.深入k8s:资源控制Qos和eviction及其源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com,源码版本是1.19 又是一个周末,可以愉快的坐下来静静的品味一段源码,这一篇涉及到资源的 ...

  8. Kubernetes K8S之资源控制器Daemonset详解

    Kubernetes的资源控制器Daemonset详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2C/ ...

  9. spring mvc: 多解析器映射(资源绑定视图解析器 + 内部资源[普通模式/]视图解析器)

    spring mvc: 多解析器映射(资源绑定视图解析器 + 内部资源[普通模式/]视图解析器) 资源绑定视图解析器 + 内部资源(普通模式)视图解析器 并存方式 内部资源视图解析器: http:// ...

随机推荐

  1. 数学log的基本知识

    在数学中,对数是对求幂的逆运算,正如除法是乘法的倒数,反之亦然.这意味着一个数字的对数是必须产生另一个固定数字(基数)的指数, 在简单的情况下,乘数中的对数计数因子.如果a的x次方等于N(a>0 ...

  2. .NET Core程序发布报错:project.assets.json”没有“.NETCoreApp,Version=v3.1/win-x64”的目标。确保已运行还原,且“netcoreapp3.1”已包含在项目的 TargetFrameworks中。

    在控制台中使用命令发布.NET Core程序的时候,报如下的错误: project.assets.json"没有".NETCoreApp,Version=v3.1/win-x64& ...

  3. HttpURLConnection 中Cookie 使用

    方式一: 如果想通过 HttpURLConnection 访问网站,网站返回cookie信息,下次再通过HttpURLConnection访问时,把网站返回 cookie信息再返回给该网站.可以使用下 ...

  4. 徒手撸一个简单的RPC框架

    来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...

  5. Web安全-信息收集

    信息收集 前言:在渗透测试过程中,信息收集是非常重要的一个环节,此环节的信息将影响到后续成功几率,掌握信息的多少将决定发现漏洞的机会的大小,换言之决定着是否能完成目标的测试任务.也就是说:渗透测试的思 ...

  6. 遇到Web页面禁用鼠标右键操作时,该如何解禁?

    在使用Selenium做Web UI自动化测试过程中,经常需要鼠标右击Web页面检查DOM节点,用于获取Web元素的定位信息.一般情况下,绝大多数页面都是能够响应鼠标右击操作的.但出于某些目的,有些W ...

  7. 前后端数据交互(六)——ajax 、fetch 和 axios 优缺点及比较

    一.ajax.fetch 和 axios 简介 1.1.ajax ajax是最早出现发送后端请求的技术,属于原生 js .ajax使用源码,请点击<原生 ajax 请求详解>查看.一般使用 ...

  8. system、 exec函数族、fork函数用法说明

    system(), exec函数族, fork函数用法说明 启动一个新线程的方式: system() 该函数经常用来在C程序中调用shell脚本或者命令行程序. 特点: 效率低下,首先需要创建一个sh ...

  9. 在C#中将图像转换为BASE64

    本教程说明如何在C#.NET Windows Forms Application中将图像转换为base64字符串,以及将base64字符串转换为图像.您可以创建一个新的Windows窗体应用程序项目来 ...

  10. 【第八篇】- Git 查看提交历史之Spring Cloud直播商城 b2b2c电子商务技术总结

    ​ Git 查看提交历史 Git 提交历史一般常用两个命令: git log 在使用 Git 提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,我们可以使用 git log 命令查看. 针对 ...