zoukankan      html  css  js  c++  java
  • Kubernetes控制器ReplicaSet

    Kubernetes控制器-ReplicaSet

    控制循环

    Kubernetes控制器会监听资源的创建、更新、删除事件,并触发Reconcile函数作为响应。整个调整过程被称作Reconclile Loop(调谐循环)或者Sync loop(同步循环)。Reconcile是一个使用资源对象的命名空间和资源对象名称来调用的函数,使得资源对象的实际状态和资源清单中定义的状态保持一致。调用完成后,Reconcile会将资源对象的状态更新为当前实际状态。我们使用Go语言风格的伪代码来描述这个过程。

    for {
    	实际状态 := 获取集群中对象 X 的实际状态(Actual State)
    	期望状态 := 获取集群中对象 X 的期望状态(Desired State)
    	if 实际状态 == 期望状态{
    		什么都不做
    	} else {
    		执行编排动作,将实际状态调整为期望状态
    	}
    }
    

    这个编排模型就是 Kubernetes 项目中的一个通用编排模式,即:控制循环(control loop)

    ReplicaSet

    假如我们现在有一个 Pod 正在提供线上的服务,我们来想想一下我们可能会遇到的一些场景:

    • 某次运营活动非常成功,网站访问量突然暴增
    • 运行当前 Pod 的节点发生故障了,Pod 不能正常提供服务了

    第一种情况,可能比较好应对,活动之前我们可以大概计算下会有多大的访问量,提前多启动几个 Pod 副本,活动结束后再把多余的 Pod 杀掉,虽然有点麻烦,但是还是能够应对这种情况的。

    第二种情况,可能某天夜里收到大量报警说服务挂了,然后起来打开电脑在另外的节点上重新启动一个新的 Pod,问题可以解决。

    但是如果我们都人工的去解决遇到的这些问题,似乎又回到了以前刀耕火种的时代了是吧?如果有一种工具能够来帮助我们自动管理 Pod 就好了,Pod 挂了自动帮我在合适的节点上重新启动一个 Pod,这样是不是遇到上面的问题我们都不需要手动去解决了。

    而 ReplicaSet 这种资源对象就可以来帮助我们实现这个功能,ReplicaSet(RS) 的主要作用就是维持一组 Pod 副本的运行,保证一定数量的 Pod 在集群中正常运行,ReplicaSet 控制器会持续监听它所控制的这些 Pod 的运行状态,在 Pod 发生故障数量减少或者增加时会触发调谐过程,始终保持副本数量一定。

    和 Pod 一样我们仍然还是通过 YAML 文件来描述我们的 ReplicaSet 资源对象,如下 YAML 文件是一个常见的 ReplicaSet 定义:(nginx-rs.yaml)

    apiVersion: apps/v1
    kind: Namespace
    metadata:
      name: dev
    
    ---
    apiVersion: apps/v1
    kind: ReplicaSet
    metadata:
      data: rs-demo
      namespace: dev
    spec:
      replicas: 3   # 期望的pod副本数量,默认值 1
      selector:     # 标签选择器,必须匹配Pod模板中的标签
        matchLabels:
          app: nginx
      template:    # Pod模板
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx1.17.1
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 80
                  protocol: TCP
    

    上面的 YAML 文件结构和我们之前定义的 Pod 看上去没太大两样,有常见的 apiVersion、kind、metadata,在 spec 下面描述 ReplicaSet 的基本信息,其中包含3个重要内容:

    • replicas:表示期望的 Pod 的副本数量
    • selector:Label Selector,用来匹配要控制的 Pod 标签,需要和下面的 Pod 模板中的标签一致
    • template:Pod 模板,实际上就是以前我们定义的 Pod 内容,相当于把一个 Pod 的描述以模板的形式嵌入到了 ReplicaSet 中来。

    上面就是我们定义的一个普通的 ReplicaSet 资源清单文件,ReplicaSet 控制器会通过定义的 Label Selector 标签去查找集群中的 Pod 对象:

    image

    我们创建上面的资源对象:

    # kubectl apply -f nginx-rs.yaml
    
    # kubectl get rs -n dev
    
    NAME      DESIRED   CURRENT   READY   AGE
    rs-demo   3         3         3       28s
    

    通过查看 RS 可以看到当前资源对象的描述信息,包括DESIREDCURRENTREADY的状态值,创建完成后,可以利用如下命令查看下 Pod 列表:

    # kubectl get pods -l app=nginx -n dev
    NAME            READY   STATUS    RESTARTS   AGE
    rs-demo-fsrhh   1/1     Running   0          2m28s
    rs-demo-qdvtz   1/1     Running   0          2m28s
    rs-demo-zvgcq   1/1     Running   0          2m28s
    

    可以看到现在有 3 个 Pod,这 3 个 Pod 就是我们在 RS 中声明的 3 个副本,比如我们删除其中一个 Pod:

    # kubectl delete pod rs-demo-fsrhh -n dev
    pod "rs-demo-fsrhh" deleted
    

    我们再次查看pod列表:

    # kubectl get pods -l app=nginx -n dev
    NAME            READY   STATUS    RESTARTS   AGE
    rs-demo-ggb4n   1/1     Running   0          66s
    rs-demo-qdvtz   1/1     Running   0          4m49s
    rs-demo-zvgcq   1/1     Running   0          4m49s
    

    可以看到又重新出现了一个 Pod,这个就是上面我们所说的 ReplicaSet 控制器为我们做的工作,我们在 YAML 文件中声明了 3 个副本,然后现在我们删除了一个副本,就变成了两个,这个时候 ReplicaSet 控制器监控到控制的 Pod 数量和期望的 3 不一致,所以就需要启动一个新的 Pod 来保持 3 个副本,这个过程上面我们说了就是调谐的过程。同样可以查看 RS 的描述信息来查看到相关的事件信息:

    # kubectl describe rs rs-demo -n dev
    Name:         rs-demo
    Namespace:    dev
    Selector:     app=nginx
    Labels:       <none>
    Annotations:  Replicas:  3 current / 3 desired
    Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.17.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Events:
      Type    Reason            Age    From                   Message
      ----    ------            ----   ----                   -------
      Normal  SuccessfulCreate  6m17s  replicaset-controller  Created pod: rs-demo-fsrhh
      Normal  SuccessfulCreate  6m17s  replicaset-controller  Created pod: rs-demo-zvgcq
      Normal  SuccessfulCreate  6m17s  replicaset-controller  Created pod: rs-demo-qdvtz
      Normal  SuccessfulCreate  2m34s  replicaset-controller  Created pod: rs-demo-ggb4n
    

    可以发现最开始通过 ReplicaSet 控制器创建了 3 个 Pod,后面我们删除了 Pod 后, ReplicaSet 控制器又为我们创建了一个 Pod,和上面我们的描述是一致的。如果这个时候我们把 RS 资源对象的 Pod 副本更改为 2 spec.replicas=2,这个时候我们来更新下资源对象:

    # kubectl apply -f nginx-rs.yaml 
    namespace/dev unchanged
    replicaset.apps/rs-demo configured
    # kubectl get pods -l app=nginx -n dev
    NAME            READY   STATUS    RESTARTS   AGE
    rs-demo-qdvtz   1/1     Running   0          9m43s
    rs-demo-zvgcq   1/1     Running   0          9m43s
    

    可以看到 Replicaset 控制器在发现我们的资源声明中副本数变更为 2 后,就主动去删除了一个 Pod,这样副本数就和期望的始终保持一致了。

    当然除了上面使用vim修改yaml文件的方式来实现pod的扩缩容外,我们还可以使用kubectl edit来进行修改:

    kubectl edit rs rs-demo -n dev
    

    上面两种方式都是修改yaml文件来实现扩缩容,我们也可以直接使用scale配合--replicas=n命令来实现扩缩容,比如现在期望的pod的数量是2,那么我们使用命令恢复为3:

    # kubectl scale rs rs-demo --replicas=3 -n dev
    replicaset.apps/rs-demo scaled
    
    # kubectl get pods -l app=nginx -n dev
    NAME            READY   STATUS    RESTARTS   AGE
    rs-demo-qdvtz   1/1     Running   0          34m
    rs-demo-vrrls   1/1     Running   0          55s
    rs-demo-zvgcq   1/1     Running   0          34m
    

    有时候呢,我们需要对镜像实现升级或者做降级处理,同上面的扩容一样,我们可以通过修改yaml文件方式来进行实现,这里就不赘述了,同样我们也可以使用命令set image 命令配合指定容器版本来实现,比如我们需要将我们的nginx从1.17.1版本提升到1.17.2版本:

    # kubectl set image rs rs名称 容器名称=镜像版本 -n 命名空间
    
    # kubectl set image rs rs-demo nginx=nginx:1.17.2 -n dev
    replicaset.apps/rs-demo image updated
    
    # kubectl get rs -n dev -o wide
    NAME      DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
    rs-demo   3         3         3       42m   nginx        nginx:1.17.2   app=nginx
    

    我们还可以随便查看一个Pod的描述信息,来获取这个Pod所属的控制器信息:

    # kubectl describe pod rs-demo-qdvtz -n dev
    Name:         rs-demo-qdvtz
    Namespace:    dev
    Priority:     0
    Node:         node1/192.168.209.147
    Start Time:   Mon, 20 Dec 2021 18:31:09 +0800
    Labels:       app=nginx
    Annotations:  cni.projectcalico.org/podIP: 192.168.166.137/32
                  cni.projectcalico.org/podIPs: 192.168.166.137/32
    Status:       Running
    IP:           192.168.166.137
    IPs:
      IP:           192.168.166.137
    Controlled By:  ReplicaSet/rs-demo
    ...... 省略后面信息
    

    另外被 ReplicaSet 持有的 Pod 有一个 metadata.ownerReferences 指针指向当前的 ReplicaSet,表示当前 Pod 的所有者,这个引用主要会被集群中的垃圾收集器使用以清理失去所有者的 Pod 对象。这个 ownerReferences 和数据库中的外键是不是非常类似。可以通过将 Pod 资源描述信息导出查看:

    # kubectl get pod rs-demo-qdvtz -n dev -o yaml
    ......
      name: rs-demo-qdvtz
      namespace: dev
      ownerReferences:
      - apiVersion: apps/v1
        blockOwnerDeletion: true
        controller: true
        kind: ReplicaSet
        name: rs-demo
        uid: 46ef0dc8-03ac-4802-ac4a-aa0b7d4cad09
     ......
    

    我们可以看到 Pod 中有一个 metadata.ownerReferences 的字段指向了 ReplicaSet 资源对象。如果要彻底删除 Pod,我们就只能删除 RS 对象:

     # 二者选一即可
     kubectl delete rs rs-demo     
     kubectl delete -f nginx-rs.yaml
    

    Replication Controller

    Replication Controller 简称 RC,实际上 RC 和 RS 的功能几乎一致,RS 算是对 RC 的改进,目前唯一的一个区别就是 RC 只支持基于等式的 selector(env=dev或environment!=qa),但 RS 还支持基于集合的 selector,这对复杂的运维管理就非常方便了。

    比如上面资源对象如果我们要使用 RC 的话,对应的 selector 是这样的:

    selector:
      app: nginx
    

    RC 只支持单个 Label 的等式,而 RS 中的 Label Selector 支持 matchLabels 和 matchExpressions 两种形式:

    selector:  
      matchLabels:
        app: nginx
    
    ---
    selector:
      matchExpressions:  # 该选择器要求 Pod 包含名为 app 的标签
      - key: app
        operator: In
        values:  # 并且标签的值是nginx或者mysql
        - nginx 
        - mysql
    

    总的来说 RS 是新一代的 RC,所以以后我们不使用 RC,直接使用 RS 即可,他们的功能都是一致的,但是实际上在实际使用中我们也不会直接使用 RS,而是使用更上层的类似于 Deployment 这样的资源对象。

  • 相关阅读:
    js 递归获取多层树的某个节点
    layui table 打印表格
    tp6 使用queue
    url带参数生成二维码
    redis的常用配置
    《TensorFlow实战》中AlexNet卷积神经网络的训练中
    JavaScript之闭包
    JavaScript之map与parseInt的陷阱
    JavaScript方法中this关键字使用注意
    什么是深度学习?
  • 原文地址:https://www.cnblogs.com/huiyichanmian/p/15712550.html
Copyright © 2011-2022 走看看