ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【云原生 | 从零开始学Kubernetes】二十三、Kubernetes控制器Statefulset [打印本页]

作者: 李优秀    时间: 2024-9-19 01:32
标题: 【云原生 | 从零开始学Kubernetes】二十三、Kubernetes控制器Statefulset
该篇文章已经被专栏《从零开始学k8s》收录
上一篇文章:kubernetes持久化存储下 点击跳转
  


  
Statefulset 控制器:概念、原明确读

StatefulSet 是为了管理有状态服务的问题而设计的
对于StatefulSet中的Pod,每个Pod挂载本身独立的存储,假如一个Pod出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来Pod的存储继续以它的状态提供服务。
有状态服务?

StatefulSet 是有状态的聚集,管理有状态的服务,它所管理的 Pod 的名称不能随意变革。数据持久化的目录也是不一样,每一个 Pod 都有本身独有的数据持久化存储目录。比如 MySQL 主从、redis 集群等。

适合StatefulSet的业务包括数据库服务 MySQL 和 PostgreSQL,集群化管理服务Zookeeper、etcd等有状态服务
StatefulSet的另一种典范应用场景是作为一种比普通容器更稳定可靠的模拟虚拟机的机制。传统的虚拟机正是一种有状态的宠物,运维人员需要不断地维护它,容器刚开始流行时,我们用容器来模拟虚拟机使用,所有状态都保存在容器里,而这已被证明是非常不安全、不可靠的。
使用StatefulSet,Pod仍然可以通过漂移到差别节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet做的只是将确定的Pod与确定的存储关联起来保证状态的连续性。
无状态服务?

RC、Deployment、DaemonSet 都是管理无状态的服务,它们所管理的 Pod 的 IP、名字,启停次序等都是随机的。个体对团体无影响,所有 pod 都是共用一个数据卷的,摆设的 tomcat 就是无状态的服务,tomcat 被删除,在启动一个新的 tomcat,到场到集群即可,跟 tomcat 的名字无关。

StatefulSet 由以下几个部门组成:
1.Headless Service:用来定义 pod 网路标识,生成可剖析的 DNS 记录
2.volumeClaimTemplates:存储卷申请模板,创建 pvc,指定 pvc 名称巨细,主动创建 pvc,且 pvc 由存储类供应。
3.StatefulSet:管理 pod 的
什么是 Headless service

Headless service 不分配 clusterIP,headless service 可以通过剖析 service 的 DNS,返回所有 Pod 的 dns 和 ip 地点 (statefulSet 摆设的 Pod 才有 DNS),普通的 service,只能通过剖析 service 的 DNS 返回 service 的 ClusterIP。
为什么要用 headless service(没有 service ip 的 service)?

在使用 Deployment 时,创建的 Pod 名称是没有次序的,是随机字符串,在用 statefulset 管理 pod 时要求 pod 名称必须是有序的 ,每一个 pod 不能被随意代替,pod 重建后 pod 名称还是一样的。因为 pod IP 是变革的,以是要用 Pod 名称来辨认。pod 名称是 pod 唯一性的标识符, 必须持久稳定有用。这时间要用到无头服务,它可以给每个 Pod 一个唯一的名称。
  1. 1.headless service 会为 service 分配一个域名
  2. <service name>.$<namespace name>.svc.cluster.local
  3. K8s 中资源的全局 FQDN 格式:
  4. Service_NAME.NameSpace_NAME.Domain.LTD.
  5. Domain.LTD.=svc.cluster.local. #这是默认 k8s 集群的域名。
  6. FQDN 全称 Fully Qualified Domain Name 即全限定域名:同时带有主机名和域名的名称
  7. FQDN = Hostname + DomainName
  8. 如 主机名是 paopao 域名是 csdn.com
  9. FQDN= paopao.csdn.com
  10. 2.StatefulSet 会为关联的 Pod 保持一个不变的 Pod Name
  11. statefulset 中 Pod 的名字格式为$(StatefulSet name)-$(pod 序号)
  12. 3.StatefulSet 会为关联的 Pod 分配一个 dnsName
  13. $<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local
复制代码
为什么要用 volumeClaimTemplate?

对于有状态应用都会用到持久化存储,比如 mysql 主从,由于主从数据库的数据是不能存放在一个目录下的,每个 mysql 节点都需要有本身独立的存储空间。而在 deployment 中创建的存储卷是一个共享的存储卷,多个 pod 使用同一个存储卷,它们数据是同步的,而 statefulset 定义中的每一个 pod 都不能使用同一个存储卷,这就需要使用 volumeClainTemplate,当在使用statefulset 创建 pod 时,volumeClainTemplate 会主动生成一个 PVC,从而请求绑定一个PV,每一个 pod 都有本身专用的存储卷。Pod、PVC 和 PV 对应的关系图如下:

Statefulset 资源清单文件编写本领

  1. #查看定义 Statefulset 资源需要的字段
  2. [root@k8smaster ~]# kubectl explain statefulset
  3. KIND:     StatefulSet
  4. VERSION:  apps/v1
  5. DESCRIPTION:
  6.      StatefulSet represents a set of pods with consistent identities. Identities
  7.      are defined as: - Network: A single stable DNS and hostname. - Storage: As
  8.      many VolumeClaims as requested. The StatefulSet guarantees that a given
  9.      network identity will always map to the same storage identity.
  10. FIELDS:
  11.    apiVersion <string> #定义 statefulset 资源需要使用的 api 版本
  12.    kind <string> #定义的资源类型
  13.    metadata<Object> #元数据
  14.    spec <Object> #定义容器相关的信息
  15. #查看 statefulset.spec 字段如何定义?
  16. [root@k8smaster ~]#  kubectl explain statefulset.spec
  17. KIND:     StatefulSet
  18. VERSION:  apps/v1
  19. RESOURCE: spec <Object>
  20. DESCRIPTION:
  21.      Spec defines the desired identities of pods in this set.
  22.      A StatefulSetSpec is the specification of a StatefulSet.
  23. FIELDS:
  24.    podManagementPolicy <string> #pod 管理策略
  25.    replicas <integer> #副本数
  26.    revisionHistoryLimit <integer> #保留的历史版本
  27.    selector <Object> -required- #标签选择器,选择它所关联的 pod
  28.    serviceName <string> -required- #headless service 的名字
  29.    template <Object> -required- #生成 pod 的模板
  30.    updateStrategy <Object> #更新策略
  31.    volumeClaimTemplates<[]Object> #存储卷申请模板
  32. #查看 statefulset 的 spec.template 字段如何定义?
  33. #对于 template 而言,其内部定义的就是 pod,pod 模板是一个独立的对象
  34. [root@k8smaster ~]# kubectl explain statefulset.spec.template
  35. KIND:     StatefulSet
  36. VERSION:  apps/v1
  37. RESOURCE: template <Object>
  38. DESCRIPTION:
  39.      template is the object that describes the pod that will be created if
  40.      insufficient replicas are detected. Each pod stamped out by the StatefulSet
  41.      will fulfill this Template, but have a unique identity from the rest of the
  42.      StatefulSet.
  43.      PodTemplateSpec describes the data a pod should have when created from a
  44.      template
  45. FIELDS:
  46.    metadata        <Object>
  47.    spec <Object> #定义容器属性的
复制代码
通过上面可以看到,statefulset 资源中有两个 spec 字段。
第一个 spec 声明的是 statefulset 定义多少个 Pod 副本(默认将仅摆设 1 个 Pod)、匹配 Pod 标签的选择器、创建 pod 的模板、存储卷申请模板。
第二个 spec 是 spec.template.spec:主要用于 Pod 里的容器属性等配置。.spec.template 里的内容是声明 Pod 对象时要定义的各种属性,以是这部门也叫做 PodTemplate(Pod 模板)。
另有一个值得留意的地方是:在.spec.selector 中定义的标签选择器必须可以大概匹配到 spec.template.metadata.labels 里定义的 Pod 标签,否则 Kubernetes 将 不答应创建 statefulset。
Statefulset 使用案例:摆设 web 站点

  1. #创建存储类
  2. [root@k8smaster ~]# mkdir state
  3. [root@k8smaster ~]# cd state/
  4. [root@k8smaster state]# vim class-web.yaml
  5. apiVersion: storage.k8s.io/v1
  6. kind: StorageClass
  7. metadata:
  8.   name: nfs-web
  9. provisioner: example.com/nfs
  10. #更新资源清单文件
  11. [root@xianchaomaster1 ~]# kubectl apply -f class-web.yaml
  12. [root@k8smaster state]# kubectl get storageclass
  13. NAME      PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
  14. nfs-web   example.com/nfs   Delete          Immediate           false                  6m16s
  15. #编写一个 Statefulset 资源清单文件
  16. [root@k8smaster state]# vim statefulset.yaml
  17. apiVersion: v1
  18. kind: Service
  19. metadata:
  20.   name: nginx
  21.   labels:
  22.      app: nginx
  23. spec:
  24.   ports:
  25.   - port: 80
  26.     name: web
  27.   clusterIP: None
  28.   selector:
  29.     app: nginx
  30. ---
  31. apiVersion: apps/v1
  32. kind: StatefulSet
  33. metadata:
  34.   name: web
  35. spec:
  36.   selector:
  37.     matchLabels:
  38.       app: nginx
  39.   serviceName: "nginx"
  40.   replicas: 2
  41.   template:
  42.     metadata:
  43.      labels:
  44.        app: nginx
  45.     spec:
  46.       containers:
  47.       - name: nginx
  48.         image: nginx
  49.         ports:
  50.         - containerPort: 80
  51.           name: web
  52.         volumeMounts:
  53.         - name: www
  54.           mountPath: /usr/share/nginx/html
  55.   volumeClaimTemplates:
  56.   - metadata:
  57.       name: www
  58.     spec:
  59.       accessModes: ["ReadWriteOnce"]
  60.       storageClassName: "nfs-web"
  61.       resources:
  62.         requests:
  63.           storage: 1Gi
  64. #更新资源清单文件
  65. [root@k8smaster state]# kubectl apply -f statefulset.yaml
  66. service/nginx created
  67. statefulset.apps/web created
  68. #查看 statefulset 是否创建成功
  69. [root@k8smaster state]# kubectl get sts -o wide
  70. NAME   READY   AGE   CONTAINERS   IMAGES
  71. web    2/2     59s   nginx        nginx
  72. #查看 pod
  73. [root@k8smaster state]# kubectl get pods -l app=nginx
  74. NAME                    READY   STATUS    RESTARTS   AGE
  75. web-0                   0/1     Pending   0          76s
  76. web-1                   1/1     Pending   0          76s
  77. #通过上面可以看到创建的 pod 是有序的
  78. #查看 headless service
  79. [root@k8smaster state]# kubectl get svc -l app=nginx
  80. NAME    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
  81. nginx   ClusterIP  None           <none>        80/TCP              3m
  82. #查看 pvc
  83. [root@k8smaster state]# kubectl get pvc
  84. www-web-0 Bound pvc-13a0482f-4927-63ff-9f2e-1b926b299c6f 1Gi RWO,RWX nfs-web 4m45s
  85. www-web-1 Bound pvc-bc23p2a9-5bjd-00df-829f-ccb4d24g01h1 1Gi RWO,RWX nfs-web 4m41s
  86. #查看 pv
  87. [root@k8smaster state]# kubectl get pv
  88. pvc-13a0482f-4927-63ff-9f2e-1b926b299c6f 1Gi RWO,RWX Delete Bound default/www-web-0 nfs-web 5m3s
  89. pvc-bc23p2a9-5bjd-00df-829f-ccb4d24g01h1 1Gi RWO,RWX Delete Bound default/www-web-1 nfs-web 5m59s
  90. #在data nfs_pro目录下划分的pv
  91. #查看 pod 主机名
  92. [root@k8smaster ~]# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname';done
  93. web-0
  94. web-1
  95. #使用 kubectl run 运行一个提供 nslookup 命令的容器的,这个命令来自于 dnsutils 包,通过对 pod 主机名执行 nslookup,可以检查它们在集群内部的 DNS 地址:
  96. [root@k8smaster ~]# kubectl exec -it web-1 -- /bin/bash
  97. root@web-1:/# apt-get update
  98. root@web-1:/# apt-get install dnsutils -y
  99. root@web-1:/# nslookup web-0.nginx.default.svc.cluster.local
  100. Server: 10.96.0.10
  101. Address: 10.96.0.10#53
  102. Name: web-0.nginx.default.svc.cluster.local
  103. #statefulset 创建的 pod 也是有 dns 记录的
  104. Address: 10.244.103.139    #解析到的是 pod 的 ip 地址
  105. root@web-1:/# nslookup nginx.default.svc.cluster.local
  106. Server: 10.96.0.10
  107. Address: 10.96.0.10#53
  108. Name: nginx.default.svc.cluster.local #查询 service dns,会把对应的 pod ip 解析出来
  109. Address: 10.244.103.139
  110. Name: nginx.default.svc.cluster.local
  111. Address: 10.244.121.93
  112. [root@k8smaster state]# kubectl describe svc nginx
  113. Name:                     nginx
  114. Namespace:                default
  115. Labels:                   app=nginx
  116. Annotations:              <none>
  117. Type:                     ClusterIP
  118. IP:                       none
  119. Port:                     web  80/TCP
  120. TargetPort:               80/TCP
  121. Endpoints:                10.244.103.139:80,10.244.121.93:80                #这两个pod都有app=ngin的标签,service把这两个都写入endpoints列表了
  122. Session Affinity:         None
  123. Events:                   <none>
  124. root@web-1:/# dig -t A nginx.default.svc.cluster.local @10.96.0.10       
  125. ; <<>> DiG 9.11.5-P4-5.1+deb10u3-Debian <<>> -t A nginx.default.svc.cluster.local @10.96.0.10
  126. ;; global options: +cmd
  127. ;; Got answer:
  128. ;; WARNING: .local is reserved for Multicast DNS
  129. ;; You are currently testing what happens when an mDNS query is leaked to DNS
  130. ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16869
  131. ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
  132. ;; WARNING: recursion requested but not available
  133. ;; OPT PSEUDOSECTION:
  134. ; EDNS: version: 0, flags:; udp: 4096
  135. ; COOKIE: 1cf973a5daa99ac7 (echoed)
  136. ;; QUESTION SECTION:
  137. ;nginx.default.svc.cluster.local. IN A
  138. ;; ANSWER SECTION:
  139. nginx.default.svc.cluster.local. 30 IN A 10.244.103.139
  140. nginx.default.svc.cluster.local. 30 IN A 10.244.121.93
  141. ;; Query time: 0 msec
  142. ;; SERVER: 10.96.0.10#53(10.96.0.10)
  143. ;; WHEN: Thu Jul 14 06:49:51 UTC 2022
  144. ;; MSG SIZE rcvd: 166
  145. root@web-1:/# nslookup kubernetes.default.svc.cluster.local
  146. Server:                10.96.0.10
  147. Address:        10.96.0.10#53
  148. Name:        kubernetes.default.svc.cluster.local
  149. Address: 10.96.0.1
  150. #解析kubernertes,有ip会把ip解析出来,没有会解析后端pod
  151. dig 的使用
  152. dig -t A nginx.default.svc.cluster.local @10.96.0.10
  153. 格式如下:
  154. @来指定域名服务器
  155. A 为解析类型 ,A 记录
  156. -t 指定要解析的类型
  157. A 记录:
  158. A 记录是解析域名到 IP
  159. 资源清单详细解读:
  160. apiVersion: v1 #定义 api 版本
  161. kind: Service #定义要创建的资源:service
  162. metadata:
  163. name: nginx #定义 service 的名字
  164. labels:
  165. app: nginx #service 的标签
  166. spec:
  167. ports:
  168. - port: 80
  169. name: web
  170. clusterIP: None #创建一个没有 ip 的 service
  171. selector:
  172. app: nginx #选择拥有 app=nginx 标签的 pod
  173. ---
  174. apiVersion: apps/v1
  175. kind: StatefulSet
  176. metadata:
  177. name: web
  178. spec:
  179. selector:
  180. matchLabels:
  181. app: nginx
  182. serviceName: "nginx" #headless service 的名字
  183. replicas: 2 #副本数
  184. template: #定义 pod 的模板
  185. metadata:
  186. labels:
  187. app: nginx
  188. spec:
  189. containers:
  190. - name: nginx
  191. image: nginx
  192. imagePullPolicy: IfNotPresent
  193. ports:
  194. - containerPort: 80
  195. name: web
  196. volumeMounts:
  197. - name: www
  198. mountPath: /usr/share/nginx/html
  199. volumeClaimTemplates: #存储卷申请模板
  200. - metadata:
  201. name: www
  202. spec:
  203. accessModes: ["ReadWriteOnce"]
  204. storageClassName: "nfs-web" #指定从哪个存储类申请 pv
  205. resources:
  206. requests:
  207. storage: 1Gi #需要 1G 的 pvc,会自动跟符合条件的 pv 绑定
  208. 扩展:
  209. service 和 headless service 区别:
  210. #deployment 创建的 pod 是随机生成的 解析的是 service 的 ip 地址
复制代码
Statefulset 管理 pod:扩容、缩容、更新

  1. #Statefulset 实现 pod 的动态扩容
  2. 如果我们觉得两个副本太少了,想要增加,只需要修改配置文件 statefulset.yaml 里的 replicas的值即可,原来 replicas: 2,现在变成 replicaset: 3,修改之后,执行如下命令更新:
  3. [root@k8smaster state]# kubectl apply -f statefulset.yaml
  4. service/nginx unchanged
  5. statefulset.apps/web configured
  6. [root@k8smaster state]# kubectl get sts
  7. web         3/3         30m
  8. [root@k8smaster state]# kubectl get pods -l app=nginx
  9. NAME         READY                 STATUS                 RESTARTS                 AGE
  10. web-0          1/1                 Running          0                                61m
  11. web-1          1/1                 Running          0                                 60m
  12. web-2          1/1                 Running          0                                 79s
  13. #也可以直接编辑控制器实现扩容
  14. [root@xianchaomaster1 ~]# kubectl edit sts web
  15. #这个是我们把请求提交给了 apiserver,实时修改,把 spec 下的 replicas 后面的值改成 4,保存退出
  16. [root@xianchaomaster1 ~]# kubectl get pods -l app=nginx
  17. NAME READY STATUS RESTARTS AGE
  18. web-0 1/1 Running 0 62m
  19. web-1 1/1 Running 0 62m
  20. web-2 1/1 Running 0 3m13s
  21. web-3 1/1 Running 0 26s
  22. #Statefulset 实现 pod 的动态缩容
  23. 如果我们觉得 4 个 Pod 副本太多了,想要减少,只需要修改配置文件 statefulset.yaml 里的
  24. replicas 的值即可,把 replicaset:4 变成 replicas: 2,修改之后,执行如下命令更新:
  25. [root@k8smaster state]# kubectl apply -f statefulset.yaml
  26. service/nginx unchanged
  27. statefulset.apps/web configured
  28. [root@k8smaster state]# kubectl get pods -l app=nginx
  29. NAME         READY                 STATUS                 RESTARTS                 AGE
  30. web-0          1/1                 Running          0                                64m
  31. web-1          1/1                 Running          0                                 63m
  32. #Statefulset 实现 pod 的更新
  33. [root@k8smaster state]# kubectl edit sts web
  34. #修改镜像 nginx 变成 image: tomcat,修改之后保存退出
  35. [root@k8smaster state]# kubectl get pods -o wide -l app=nginx
  36. NAME            READY   STATUS    RESTARTS   AGE   IP                             NODE      
  37. web-0                         1/1         Running   0                   18s   10.244.201.106                 k8snode
  38. web-1                         1/1         Running   0                   36s   10.244.133.176                 k8snode2
  39. #查看 pod 详细信息
  40. [root@k8smaster state]# kubectl describe pods web-0
  41. 通过上面可以看到 pod 已经使用刚才更新的镜像 tomcat 了
复制代码
写在最后

创作不易,假如觉得内容对你有帮助,麻烦给个三连关注支持一下我!假如有错误,请在评论区指出,我会实时更改!
目前正在更新的系列:从零开始学k8s
感谢各位的观看,文章掺杂个人明确,如有错误请联系我指出~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4