首页 > 其他分享 >07-有状态应用:Kubernete 如何通过 StatefulSet 支持有状态应用?

07-有状态应用:Kubernete 如何通过 StatefulSet 支持有状态应用?

时间:2024-01-11 13:33:06浏览次数:35  
标签:web StatefulSet 07 0s demo nginx Pod Kubernete

在上一节课中,我们学习了 Kubernetes 中的无状态工作负载,并上手实践了 Deployment 对象,相信现在你已经慢慢喜欢上 Kubernetes 了。

那么本节课,我们来一起看看Kubernetes 中的另外一种工作负载 StatefulSet。从名字就可以看出,这个工作负载主要用于有状态的服务发布。关于有状态服务和无状态服务,你可以参考上一节课的内容。

这节课,我们从一个具体的例子来逐渐了解、认识 StatefulSet。在 kubectl 命令行中,我们一般将 StatefulSet 简写为 sts。在部署一个 StatefulSet 的时候,有个前置依赖对象,即 Service(服务)。这个对象在 StatefulSet 中的作用,我们在下文中会一一道来。另外,关于这个对象的详细介绍和其他作用,我们会在后面的课程中单独讲解。在此,你可以先暂时略过对 Service 的感知。我们先看如下一个 Service:

$ cat nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-demo
  namespace: demo
  labels:
    app: nginx
spec:
  clusterIP: None
  ports:
  - port: 80
    name: web
  selector:
    app: nginx

上面这段 yaml 的意思是,在 demo 这个命名空间中,创建一个名为 nginx-demo 的服务,这个服务暴露了 80 端口,可以访问带有app=nginx这个 label 的 Pod。

我们现在利用上面这段 yaml 在集群中创建出一个 Service:

$ kubectl create ns demo
$ kubectl create -f nginx-svc.yaml
service/nginx-demo created
$ kubectl get svc -n demo
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-demo   ClusterIP   None             <none>        80/TCP    5s

创建好了这个前置依赖的 Service,下面我们就可以开始创建真正的 StatefulSet 对象,可参照如下的 yaml 文件:

$ cat web-sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web-demo
  namespace: demo
spec:
  serviceName: "nginx-demo"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19.2-alpine
        ports:
        - containerPort: 80
          name: web
$ kubectl create -f web-sts.yaml
$ kubectl get sts -n demo
NAME       READY   AGE
web-demo   0/2     9s

可以看到,到这里我已经将名为web-demo的StatefulSet部署完成了。

下面我们来一点点探索 StatefulSet 的秘密,看看它有哪些特性,为何可以保障服务有状态的运行。

StatefulSet 的特性

通过 kubectl 的watch功能(命令行加参数-w),我们可以观察到 Pod 状态的一步步变化。

$ kubectl get pod -n demo -w
NAME         READY   STATUS              RESTARTS   AGE
web-demo-0   0/1     ContainerCreating   0          18s
web-demo-0   1/1     Running             0          20s
web-demo-1   0/1     Pending             0          0s
web-demo-1   0/1     Pending             0          0s
web-demo-1   0/1     ContainerCreating   0          0s
web-demo-1   1/1     Running             0          2s

通过 StatefulSet 创建出来的 Pod 名字有一定的规律,即$(statefulset名称)-$(序号),比如这个例子中的web-demo-0、web-demo-1。

这里面还有个有意思的点,web-demo-0 这个 Pod 比 web-demo-1 优先创建,而且在 web-demo-0 变为 Running 状态以后,才被创建出来。为了证实这个猜想,我们在一个终端窗口观察 StatefulSet 的 Pod:

$ kubectl get pod -n demo -w -l app=nginx

我们再开一个终端端口来 watch 这个 namespace 中的 event:

$ kubectl get event -n demo -w

现在我们试着改变这个 StatefulSet 的副本数,将它改成 5:

$ kubectl scale sts web-demo -n demo --replicas=5
statefulset.apps/web-demo scaled

此时我们观察到另外两个终端端口的输出:

$ kubectl get pod -n demo -w
NAME         READY   STATUS    RESTARTS   AGE
web-demo-0   1/1     Running   0          20m
web-demo-1   1/1     Running   0          20m
web-demo-2   0/1     Pending   0          0s
web-demo-2   0/1     Pending   0          0s
web-demo-2   0/1     ContainerCreating   0          0s
web-demo-2   1/1     Running             0          2s
web-demo-3   0/1     Pending             0          0s
web-demo-3   0/1     Pending             0          0s
web-demo-3   0/1     ContainerCreating   0          0s
web-demo-3   1/1     Running             0          3s
web-demo-4   0/1     Pending             0          0s
web-demo-4   0/1     Pending             0          0s
web-demo-4   0/1     ContainerCreating   0          0s
web-demo-4   1/1     Running             0          3s

我们再一次看到了 StatefulSet 管理的 Pod 按照 2、3、4 的顺序依次创建,名称有规律,跟上一节通过 Deployment 创建的随机 Pod 名有很大的区别。

通过观察对应的 event 信息,也可以再次证实我们的猜想。

$ kubectl get event -n demo -w
LAST SEEN   TYPE     REASON             OBJECT                 MESSAGE
20m         Normal   Scheduled          pod/web-demo-0         Successfully assigned demo/web-demo-0 to kraken
20m         Normal   Pulling            pod/web-demo-0         Pulling image "nginx:1.19.2-alpine"
20m         Normal   Pulled             pod/web-demo-0         Successfully pulled image "nginx:1.19.2-alpine"
20m         Normal   Created            pod/web-demo-0         Created container nginx
20m         Normal   Started            pod/web-demo-0         Started container nginx
20m         Normal   Scheduled          pod/web-demo-1         Successfully assigned demo/web-demo-1 to kraken
20m         Normal   Pulled             pod/web-demo-1         Container image "nginx:1.19.2-alpine" already present on machine
20m         Normal   Created            pod/web-demo-1         Created container nginx
20m         Normal   Started            pod/web-demo-1         Started container nginx
20m         Normal   SuccessfulCreate   statefulset/web-demo   create Pod web-demo-0 in StatefulSet web-demo successful
20m         Normal   SuccessfulCreate   statefulset/web-demo   create Pod web-demo-1 in StatefulSet web-demo successful
0s          Normal   SuccessfulCreate   statefulset/web-demo   create Pod web-demo-2 in StatefulSet web-demo successful
0s          Normal   Scheduled          pod/web-demo-2         Successfully assigned demo/web-demo-2 to kraken
0s          Normal   Pulled             pod/web-demo-2         Container image "nginx:1.19.2-alpine" already present on machine
0s          Normal   Created            pod/web-demo-2         Created container nginx
0s          Normal   Started            pod/web-demo-2         Started container nginx
0s          Normal   SuccessfulCreate   statefulset/web-demo   create Pod web-demo-3 in StatefulSet web-demo successful
0s          Normal   Scheduled          pod/web-demo-3         Successfully assigned demo/web-demo-3 to kraken
0s          Normal   Pulled             pod/web-demo-3         Container image "nginx:1.19.2-alpine" already present on machine
0s          Normal   Created            pod/web-demo-3         Created container nginx
0s          Normal   Started            pod/web-demo-3         Started container nginx
0s          Normal   SuccessfulCreate   statefulset/web-demo   create Pod web-demo-4 in StatefulSet web-demo successful
0s          Normal   Scheduled          pod/web-demo-4         Successfully assigned demo/web-demo-4 to kraken
0s          Normal   Pulled             pod/web-demo-4         Container image "nginx:1.19.2-alpine" already present on machine
0s          Normal   Created            pod/web-demo-4         Created container nginx
0s          Normal   Started            pod/web-demo-4         Started container nginx

现在我们试着进行一次缩容:

$ kubectl scale sts web-demo -n demo --replicas=2
statefulset.apps/web-demo scaled

此时观察另外两个终端窗口,分别如下:

web-demo-4   1/1     Terminating   0          11m
web-demo-4   0/1     Terminating   0          11m
web-demo-4   0/1     Terminating   0          11m
web-demo-4   0/1     Terminating   0          11m
web-demo-3   1/1     Terminating   0          12m
web-demo-3   0/1     Terminating   0          12m
web-demo-3   0/1     Terminating   0          12m
web-demo-3   0/1     Terminating   0          12m
web-demo-2   1/1     Terminating   0          12m
web-demo-2   0/1     Terminating   0          12m
web-demo-2   0/1     Terminating   0          12m
web-demo-2   0/1     Terminating   0          12m
0s          Normal   SuccessfulDelete   statefulset/web-demo   delete Pod web-demo-4 in StatefulSet web-demo successful
0s          Normal   Killing            pod/web-demo-4         Stopping container nginx
0s          Normal   Killing            pod/web-demo-3         Stopping container nginx
0s          Normal   SuccessfulDelete   statefulset/web-demo   delete Pod web-demo-3 in StatefulSet web-demo successful
0s          Normal   SuccessfulDelete   statefulset/web-demo   delete Pod web-demo-2 in StatefulSet web-demo successful
0s          Normal   Killing            pod/web-demo-2         Stopping container nginx

可以看到,在缩容的时候,StatefulSet 关联的 Pod 按着 4、3、2 的顺序依次删除。

可见,对于一个拥有 N 个副本的 StatefulSet 来说Pod 在部署时按照 {0 …… N-1} 的序号顺序创建的而删除的时候按照逆序逐个删除,这便是我想说的第一个特性。

接着我们来看,StatefulSet 创建出来的 Pod 都具有固定的、且确切的主机名,比如:

$ for i in 0 1; do kubectl exec web-demo-$i -n demo -- sh -c 'hostname'; done
web-demo-0
web-demo-1

我们再看看上面 StatefulSet 的 API 对象定义,有没有发现跟我们上一节中 Deployment 的定义极其相似,主要的差异在于spec.serviceName这个字段。它很重要,StatefulSet 根据这个字段,为每个 Pod 创建一个 DNS 域名,这个域名的格式$(podname).(headless service name),下面我们通过例子来看一下。

当前 Pod 和 IP 之间的对应关系如下:

$ kubectl get pod -n demo -l app=nginx -o wide
NAME         READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
web-demo-0   1/1     Running   0          3h17m   10.244.0.39   kraken   <none>           <none>
web-demo-1   1/1     Running   0          3h17m   10.244.0.40   kraken   <none>           <none>

Podweb-demo-0 的IP 地址是 10.244.0.39,web-demo-1的 IP 地址是 10.244.0.40。这里我们通过kubectl run在同一个命名空间demo中创建一个名为 dns-test 的 Pod,同时 attach 到容器中,类似于docker run -it --rm这个命令。
我么在容器中运行 nslookup 来查询它们在集群内部的 DNS 地址,如下所示:

$ kubectl run -it --rm --image busybox:1.28 dns-test -n demo
If you don't see a command prompt, try pressing enter.
/ # nslookup web-demo-0.nginx-demo
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web-demo-0.nginx-demo
Address 1: 10.244.0.39 web-demo-0.nginx-demo.demo.svc.cluster.local
/ # nslookup web-demo-1.nginx-demo
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web-demo-1.nginx-demo
Address 1: 10.244.0.40 web-demo-1.nginx-demo.demo.svc.cluster.local

可以看到,每个 Pod 都有一个对应的 A 记录
我们现在删除一下这些 Pod,看看会有什么变化:

$ kubectl delete pod -l app=nginx -n demo
pod "web-demo-0" deleted
pod "web-demo-1" deleted
$ kubectl get pod -l app=nginx -n demo -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
web-demo-0   1/1     Running   0          15s   10.244.0.50   kraken   <none>           <none>
web-demo-1   1/1     Running   0          13s   10.244.0.51   kraken   <none>           <none>

删除成功后,可以发现 StatefulSet 立即生成了新的 Pod,但是 Pod 名称维持不变。唯一变化的就是 IP 发生了改变。

我们再来看看 DNS 记录:

$ kubectl run -it --rm --image busybox:1.28 dns-test -n demo
If you don't see a command prompt, try pressing enter.
/ # nslookup web-demo-0.nginx-demo
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-demo-0.nginx-demo
Address 1: 10.244.0.50 web-demo-0.nginx-demo.demo.svc.cluster.local
/ # nslookup web-demo-1.nginx-demo
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-demo-1.nginx-demo
Address 1: 10.244.0.51 web-demo-1.nginx-demo.demo.svc.cluster.local

可以看出,DNS 记录中 Pod 的域名没有发生变化仅仅 IP 地址发生了更换。因此当 Pod 所在的节点发生故障导致 Pod 飘移到其他节点上,或者 Pod 因故障被删除重建,Pod 的 IP 都会发生变化,但是 Pod 的域名不会有任何变化,这也就意味着服务间可以通过不变的 Pod 域名来保障通信稳定而不必依赖 Pod IP

有了spec.serviceName这个字段,保证了 StatefulSet 关联的 Pod 可以有稳定的网络身份标识,即 Pod 的序号、主机名、DNS 记录名称等。

最后一个我想说的是,对于有状态的服务来说,每个副本都会用到持久化存储,且各自使用的数据是不一样的。

StatefulSet 通过 PersistentVolumeClaim(PVC)可以保证 Pod 的存储卷之间一一对应的绑定关系。同时,删除 StatefulSet 关联的 Pod 时,不会删除其关联的 PVC。

我们会在后续网络存储的章节中来专门介绍,再次先略过。

如何更新升级 StatefulSet

那么,如果想对一个 StatefulSet 进行升级,该怎么办呢?

在 StatefulSet 中,支持两种更新升级策略,即 RollingUpdate 和 OnDelete。

RollingUpdate策略是默认的更新策略。可以实现 Pod 的滚动升级,跟我们上一节课中 Deployment 介绍的RollingUpdate策略一样。比如我们这个时候做了镜像更新操作,那么整个的升级过程大致如下,先逆序删除所有的 Pod,然后依次用新镜像创建新的 Pod 出来。这里你可以通过kubectl get pod -n demo -w -l app=nginx来动手观察下。

同时使用 RollingUpdate 更新策略还支持通过 partition 参数来分段更新一个 StatefulSet。所有序号大于或者等于 partition 的Pod 都将被更新。你这里也可以手动更新 StatefulSet 的配置来实验下。

当你把更新策略设置为 OnDelete 时,我们就必须手动先删除 Pod,才能触发新的 Pod 更新。

写在最后

现在我们就总结下 StatefulSet 的特点:

  • 具备固定的网络标记,比如主机名,域名等;

  • 支持持久化存储,而且最好能够跟实例一一绑定;

  • 可以按照顺序来部署和扩展;

  • 可以按照顺序进行终止和删除操作;

  • 在进行滚动升级的时候,也会按照一定顺序。

借助 StatefulSet 的这些能力,我们就可以去部署一些有状态服务,比如 MySQL、ZooKeeper、MongoDB 等。你可以跟着这个教程在 Kubernetes 中搭建一个 ZooKeeper 集群。

到这里这节课就结束了,下节课我们就来学习配置管理。如果你对本节课有什么想法或者疑问,欢迎你在留言区留言,我们一起讨论。


标签:web,StatefulSet,07,0s,demo,nginx,Pod,Kubernete
From: https://www.cnblogs.com/huangjiale/p/17958377

相关文章

  • 前世今生:Kubernete 是如何火起来的?
    本课时,我们就开始正式进入Kubernetes的学习,或许你已经听过或简单了解过Kubernetes,它是一款由Google开源的容器编排管理工具,而我们想要深入地掌握Kubernetes框架,就不得不先了解Kubernetes的前世今生,而这一切都要从“云计算”的兴起开始讲起。云计算平台说来也巧,“云计算......
  • kubernetes-dashboard crashloopbackoff
    Kubernetes(简称K8S)是现阶段非常热门的容器编排平台,可以用于部署、扩展和管理容器化应用程序。Kubernetes提供了一个称为Dashboard的web界面,用于监视和管理集群中的各种资源。然而,有时我们可能会遇到kubernetes-dashboardcrashloopbackoff的问题,本篇文章将介绍这个问题的原因和解决......
  • kubernetes-dashboard crashloopbackoff
    你好,小白!欢迎来到K8S的世界。K8S,也就是Kubernetes,是当今最流行的开源容器编排平台。你在使用中遇到了【kubernetes-dashboardcrashloopbackoff】的问题,这是一个相对常见的问题。别担心,我会帮助你解决它。一、整体流程在解决问题之前,我们先简单了解一下涉及的步骤和整体流程:确认问......
  • 当创建pvc后,kubernetes组件如何协作
    本文分享自华为云社区《当创建一个pvc后,kubernetes会发生什么?》,作者:可以交个朋友。一、背景   外部存储接入Kubernetes的方式主要有两种:In-Tree和Out-of-Tree:In-Tree 是指存储驱动的源码都在Kubernetes代码库中,与Kubernetes一起发布、迭代、管理,这种方式灵活性较差,且......
  • P1307题解
    思路1.定义及输入原数/反转后的数intn,cnt=0;//反转后的数一定要归零!cin>>n;2.用while循环反转while(n!=0){//只要n还没有被分解完,就继续分解cnt=cnt*10+n%10;//cnt每次*10再加上分离出的数位(*10为了防0)n/=10;//n减一位}3.输出cout<<cnt;至此,这道题就做完......
  • MEM0701 Correctable memory error rate exceeded for . 内存可能无法正常工作。 这是
    ......
  • 图解Kubernetes的服务(Service)
    pod准备:不要直接使用和管理Pods:当使用ReplicaSet水平扩展scale时,Pods可能被terminated当使用Deployment时,去更新DockerImageVersion,旧Pods会被terminated,然后创建新Pods0啥是服务(Service)Kubernetes中Service是将运行在一个或一组[Pod]上的网络应用程序公开为网络......
  • 使用Terraform部署华为云和kubernetes资源
    本文分享自华为云社区《使用Terraform部署华为云和kubernetes资源》,作者:可以交个朋友。Terraform概述Terraform是由HashiCorp创建的开源“基础架构即代码”工具。作为一种声明式编码工具,Terraform使开发人员能够使用一种称为HCL(HashiCorp配置语言)的高级配置语言来描述运行应......
  • 在Kubernetes中优雅地导出和清理Ingress资源
    引言Kubernetes的Ingress资源是定义外部访问集群服务的规则。随着微服务架构和容器化技术的普及,Ingress作为路由流量的关键组件变得愈发重要。当我们需要在环境之间迁移Ingress资源或者备份当前的配置时,就会用到导出功能。然而,直接使用kubectl导出可能会包括一些我们不需要的元数......
  • 【云原生】kubernetes 1.24 安装教程
    博主介绍:–我是了凡微信公众号【了凡银河系】期待你的关注。未来大家一起加油啊~前言下面记录kubernets1.24安装记录。主要使用calico作为网络插件、kongingress作为路由转发、nginx作为四层代理。如果安装过程中遇到问题,及时沟通,如果不能及时回应,可以关注【了凡银河系】微......