• Kubernetes 的有状态和无状态服务


    k8s-LAB

    Author:rab



    前言

    在 Kubernetes(k8s)中,有状态服务和无状态服务是两种不同类型的应用程序部署方式,它们在容器编排和管理方面有一些关键区别。

    1、无状态服务(Stateless Services)

    • 无状态服务是指应用程序不依赖于本地状态,并且对于每个请求都能以相同的方式处理。这意味着无状态服务的任何一个实例都可以处理来自客户端的请求,而请求之间没有关联。
    • 无状态服务的典型示例包括 Web 服务器、API 服务、负载均衡器等,这些服务可以水平扩展,即可以通过添加更多的副本来处理更多的请求流量。
    • 无状态服务通常适合使用 Kubernetes 中的 Deployment 进行部署。因为它们的实例可以随意启动和停止,而不会影响应用程序的状态或数据。

    2、有状态服务(Stateful Services)

    • 有状态服务是指应用程序依赖于本地状态,通常需要稳定的标识和数据。这意味着每个实例都有唯一的标识,如数据库服务器或消息队列,它们需要在启动、停止或故障转移时保留其数据和状态。
    • 有状态服务的典型示例包括数据库系统(如 MySQL、PostgreSQL)、消息队列(如 RabbitMQ、Kafka)等,这些服务通常需要持久性存储和网络标识的稳定性。
    • Kubernetes 提供 StatefulSet 来部署有状态服务。StatefulSet 具有管理有状态应用程序的能力,为每个 Pod 提供唯一的标识,以及稳定的网络标识,从而支持有状态服务的稳定性。

    一、无状态服务案例

    1.1 yml 案例

    比如我们可以举个 Nginx 负载均衡的例子,那此时这个负载均衡可以归为无状态服务,任何一个 Nginx 实例都可以处理来自客户端的请求。因此我们可以采用 Kubernetes 中的 Deployment 进行部署。

    vim Stateless.yml
    
    • 1
    apiVersion: v1
    kind: Namespace
    metadata:
      name: myweb
      labels:
        name: ops
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
      namespace: myweb
      labels:
        app: stateless
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: demo
      template:
        metadata:
          labels:
            app: demo
        spec:
          containers:
          - name: nginx
            image: nginx:1.21.4
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-srv
      namespace: myweb
    spec:
      selector:
        app: demo
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80
          nodePort: 30333
      type: NodePort
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    注意:Service 的 selector 标签选择器的值一定要和 Deployment 的 selector 标签选择器的值保持一致,否则外部 Client 的请求就无法路由到 Pod 资源。

    kubectl apply -f Stateless.yml
    
    • 1

    本地浏览器访问:

    http://192.168.56.160:30333/

    image-20231025153300073

    1.2 扩容与缩容

    1.2.1 扩容

    扩容的方式有很多,以下是常见的扩容方式。

    1、修改 yml 文件方式

    vim Stateless.yml    # 只需修改replicas值即可
    
    • 1
    ...
    spec:
      replicas: 5
      ...
    
    • 1
    • 2
    • 3
    • 4

    修改完成后执行扩展(更新)

    kubectl apply -f Stateless.yml
    
    • 1

    2、Shell 命令行方式

    • 法1

      kubectl scale --replicas=5 deploy nginx -n myweb
      
      # 说明:deploy是deployment的缩写,写deployment也是没问题的
      
      • 1
      • 2
      • 3
    • 法2

      kubectl edit deployment nginx -n myweb
      
      # 这会以交互式的方式进入yml配置文件,进去后修改replicas的值后保存退出即可实现扩容
      
      • 1
      • 2
      • 3

    说明:与直接修改 yml 文件不同的是,该命令执行完后即可升级,而无需执行 kubectl apply -f Stateless.yml

    1.2.2 缩容

    与扩容没有任何区别,操作一模一样,所谓的缩容就是当你设置的 replicas 值小于当前 replicas 值时就是缩容的概念。

    注意:但要注意的是,我们说动态扩容和缩容不会触发上线(即不会产生新的 replicaset)

    1.3 暂停与恢复

    注意的是,这个暂停和恢复不是说暂停了我的程序就不对外提供服务了,而是用于当你使用 kubectl set ... 命令来更新时使用。其大概流程就是:当你暂停后,允许你执行多个 kubectl set ... 更新命令,但每条指令执行完后不是立即生效,而是当你恢复 deployment 时你执行的那些 kubectl set ... 才会生效。

    1.3.1 暂停

    1、暂停服务

    kubectl rollout pause deployment nginx -n myweb
    
    • 1

    2、执行第一条更新指令

    kubectl set image deploy nginx nginx=nginx:1.20.0 -n myweb
    
    • 1

    3、执行第二条更新指令

    kubectl set resources deploy nginx -c nginx --limits=cpu=200m,memory=128Mi --requests=cpu=10m,memory=16Mi -n myweb
    
    • 1

    如下图,以上这两条更新指令都是未生效的,从 nginx 镜像版本没变化就可证明。

    image-20231025170727008

    1.3.2 恢复

    那要如何是我们执行过的 kubectl set ... 指令生效呢?恢复即可。

    kubectl rollout resume deploy nginx -n myweb
    
    • 1

    如下图,恢复后我们在暂停期间执行的 kubectl set ... 指令就生效了。

    image-20231025171200713

    当然了,如果你想使用 kubectl set 来更新服务,但又不想暂停,你完全可以直接执行 kubectl set 指令即可,执行完后,就会立马更新。

    kubectl set image deploy nginx nginx=nginx:1.18.0 -n myweb
    
    • 1

    image-20231025171428065

    1.4 回滚

    如果我们以上的一系列更新最终的测试验证出问题了,需要回滚到最初(或更新前)的状态或中间某个版本状态,那此时就需要使用到 K8s 的回滚功能了,具体的操作如下。

    1、先查看更新的版本

    默认情况下,K8s 只会保留近 10 个的 revision,我们可以在 Deployment 配置文件中使用 revisionHistoryLimit 字段来指定 revision 的保留数量。

    kubectl rollout history deploy nginx -n myweb
    
    # 下图中这三个版本编号中,1表示最初版本状态,3表示当前的版本状态
    
    • 1
    • 2
    • 3

    image-20231025172113126

    2、再查看每个版本对应的详情(因为上图根本看不出来)

    kubectl rollout history deployment nginx --revision=1 -n myweb
    
    # 我们来看看第一个版本详情
    
    • 1
    • 2
    • 3

    image-20231025172640153

    3、回滚到我们指定的版本状态

    kubectl rollout undo deployment nginx --to-revision=1 -n myweb
    
    • 1

    image-20231025172836324

    4、最后来验证一下是否回滚成功

    kubectl get deployment nginx -o wide -n myweb
    
    • 1

    image-20231025173029086

    这个时候我们再来看看历史版本号:

    image-20231025173140455

    你会发现,历史版本为 1 的编号消失了,为什么呢?因为已经被我们回滚了(此时你就要记住了,如果你又执行了多次更新操作后,又想回到最初的状态的话,只能回到 4 这个版本,因为 4 此时才是我们最初的状态,而 2 并不是)。

    二、有状态服务案例

    2.1 yml 案例

    上面说到,像 MySQL、RabbitMQ 等这类服务实际上就是属于有状态服务,对于这类服务我们一般会要求每个 Pod 具有唯一的标识,以及稳定的网络标识。例如 MySQL 就是一个典型的有状态应用程序,因为每个 MySQL 实例需要唯一的标识,并且通常依赖于稳定的网络标识。

    vim Stateful.yml
    
    • 1
    apiVersion: v1
    kind: Namespace
    metadata:
      name: myweb-2
      labels:
        name: ops
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: nginx
      namespace: myweb-2
      labels:
        app: stateful
    spec:
      serviceName: myweb-2
      replicas: 3
      selector:
        matchLabels:
          app: demo-2
      template:
        metadata:
          labels:
            app: demo-2
        spec:
          containers:
          - name: nginx
            image: nginx:1.21.4
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-srv
      namespace: myweb-2
    spec:
      selector:
        app: demo-2
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80
          nodePort: 30334
      type: NodePort
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    kubectl apply -f Stateful.yml
    
    • 1

    对于如何查看 deployment 资源,我想你已经会了,那 statefulset 资源如何查看呢?其实都是一样的套路:

    kubectl get statefulset -n myweb-2
    
    # 或
    kubectl get sts -n myweb-2
    
    • 1
    • 2
    • 3
    • 4

    image-20231025175454025

    本地浏览器访问:

    http://192.168.56.160:30334/

    image-20231025175657501

    2.2 扩容与缩容

    2.2.1 扩容
    kubectl scale --replicas=5 sts nginx -n myweb-2
    
    • 1
    2.2.2 缩容
    kubectl scale --replicas=2 sts nginx -n myweb-2
    
    • 1

    说明:暂停、恢复、回滚套路和 deployment 保持一致的,这里就不再重复造轮子了。

    总结

    • 无状态服务适用于可以随意扩展并处理请求而不依赖本地状态的应用程序,而有状态服务适用于需要稳定的标识和数据的应用程序。

    • 在 Kubernetes 中,我们可以使用不同的控制器(例如 Deployment 和 StatefulSet)来适应这两种不同类型的服务。

    • 有状态服务通常需要考虑持久性存储和数据备份策略,以确保数据的可靠性和可用性。

    说明:以上案例均未配置持久化存储,后面会提到。

    —END

  • 相关阅读:
    [OpenCV实战]52 在OpenCV中使用颜色直方图
    微信⼩程序实现限制⽤户转发功能的实例代码
    深入理解Kafka核心设计及原理(四):主题管理
    VUE到底有什么好处?
    Pyside6:开发一个自适应的图片控件
    最近公共祖先(LCA)学习笔记 | P3379 【模板】最近公共祖先(LCA)题解
    React学习之路-目录结构
    ubuntu环境下基于cerbero构建GStreamer及使用VSCode进行调试
    python内置模块subprocess 模块,创建和管理子进程
    关于windows上Python3.12.0安装FastAPI入坑指南
  • 原文地址:https://blog.csdn.net/IT_ZRS/article/details/134040807