• Kubernetes 的亲和性污点与容忍


    Taints and tolerations in Kubernetes, how to use them? | Padok

    写在前面#

    我们在使用k8s过程中经常有这样的需求:我的k8s集群有多台服务器,配置不尽相同。我想把数据库部署到CPU、内存比较好的这几台机;我想把静态承载服务部署到有固态硬盘的机器等;而这些需求,就是我们今天要讲的k8s的调度:

    在Kubernetes 中,调度 是指将 Pod 部署到合适的节点(node)上。
    

    k8s的默认调度器kube-scheduler,它执行的是一个类似平均分配的原则,让同一个service管控下的pod尽量分散在不同的节点。

    那接下来分别说说k8s几种不同的调度策略。

    节点标签#

    在介绍调度策略之前,我们先提一句节点标签;节点标签关联的指令是kubectl label ,标签是一种键值对,可以用来标识和选择资源。例如,你需要给某个节点打个标记以便后面用得上,这个标记就叫标签。

    增加标签

    kubectl label node docker-desktop restype=strong-cpu
    

    这里增加了一个restype=strong-cpu的标签,表示这个节点cpu很强;

    查看标签

    kubectl get nodes --show-labels
    NAME             STATUS   ROLES    AGE    VERSION   LABELS
    docker-desktop   Ready    <none>   328d   v1.22.5   disktype=ssd
    

    删除标签

    kubectl label node docker-desktop restype-
    

    nodeSelector-简单的节点选择器#

    nodeSelector:在部署pod的时候告诉集群,我要部署到符合我要求的节点;

    前面已经看到我k8s的节点 docker-desktop,已经打了disktype=ssd的标签,那我们来部署一个测试的pod看看

    创建文件:test-netcore6-dep.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demoapi-net6
      namespace: aspnetcore
      labels:
        name: demoapi-net6
    spec:
      replicas: 1
      selector:
        matchLabels:
          name: demoapi-net6
      template:
        metadata:
          labels:
            name: demoapi-net6
        spec:
          containers:
          - name: demoapi-net6
            image: gebiwangshushu/demoapi-net6 #这是个kong的webapi
          nodeSelector:
            restype: strong-cpu #我这里要求节点要有资源类型restype=strong-cpu的标签
    

    部署

    kubectl apply -f .\test-netcore6-dep.yaml
    

    查看pod状态

    确实处于pending状态

     kubectl get pods
    NAME                          READY   STATUS    RESTARTS   AGE
    demoapi-net6-c9c5cb85-rwqn2   0/1     Pending   0          52m
    

    describe一下

    可以清楚看到pending原因是affinity/selector不匹配

     kubectl describe pod demoapi-net6-c9c5cb85-rwqn2
     
     ...
      Type     Reason            Age   From               Message
      ----     ------            ----  ----               -------
      Warning  FailedScheduling  32s   default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector. 
      
    

    给node加上strong-cpn加上label看看

    kubectl label node docker-desktop restype=strong-cpu
    

    再看看

    ...
    Normal   Scheduled         46s    default-scheduler  Successfully assigned aspnetcore/demoapi-net6-c9c5cb85-l7sdb to docker-desktop
      Normal   Pulling           45s    kubelet            Pulling image "gebiwangshushu/demoapi-net6"
      
    #可以清楚看到已分配好node了,pod run起来了
    

    nodeName-更粗暴的节点选择器#

    前面的nodeSelector是“我要部署到符合我要求的节点”;见名思意,nodeName是更粗暴的节点选择器,意思是:我就要部署到这个节点!

    写法示例:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demoapi-net6
      namespace: aspnetcore
      labels:
        name: demoapi-net6
    spec:
      replicas: 1
      selector:
        matchLabels:
          name: demoapi-net6
      template:
        metadata:
          labels:
            name: demoapi-net6
        spec:
          containers:
          nodeName: docker-desktop #就要部署到这个
    

    这种写法简单粗暴,一般用来测试和测试用途,一般不这么写,因为存在比较多的局限性

    1. 比如节点不存在或者节点名字写错了,部署失败;
    2. 指定的节点硬件资源不够,比如cpu或者内存不够了,部署失败;
    3. 在云服务环境中,节点名字总是变化的,指定节点名没什么意义;

    affinity-节点亲和性和pod反亲和性#

    节点亲和性功能类似于 nodeSelector 字段,但它的选择表达能力更强,有各种各样的规则,还有软规则。甚至还可以有反亲和性,拒绝/排斥部署到哪些节点;

    nodeAffinity--节点亲和性#

    节点亲和性(nodeAffinity)分成两种:

    • requiredDuringSchedulingIgnoredDuringExecution: 硬策略。就是node你一定要满足我的要求,才能执行调度,不然pod就一直pending
    • preferredDuringSchedulingIgnoredDuringExecution: 软策略。就是我更倾向于满足我要去的node,如果没有那就按默认规则调度。

    示例

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demoapi-net6
      namespace: aspnetcore
      labels:
        name: demoapi-net6
    spec:
      replicas: 1
      selector:
        matchLabels:
          name: demoapi-net6
      template:
        metadata:
          labels:
            name: demoapi-net6
        spec:
          containers:
          - name: demoapi-net6
            image: gebiwangshushu/demoapi-net6
          affinity: 
            nodeAffinity: #亲和性
              requiredDuringSchedulingIgnoredDuringExecution: #硬策略
                nodeSelectorTerms:
                - matchExpressions: #这里意思一定要restype=strong-cpu的节点
                  - key: restype
                    operator: In
                    values:
                    - strong-cpu
              preferredDuringSchedulingIgnoredDuringExecution: #软策略
              - weight: 1
                preference:
                  matchExpressions: #这里意思倾向于部署到有ssd硬盘的节点
                  - key: disktype
                    operator: In
                    values:
                    - ssd
    

    affinity: 亲和性

    requiredDuringSchedulingIgnoredDuringExecution:硬策略

    preferredDuringSchedulingIgnoredDuringExecution:软策略

    nodeSelectorTerms:节点选择项,数组

    matchExpressions:匹配表达式,数组

    weightpreferredDuringSchedulingIgnoredDuringExecution 可设置的权重字段,值范围是 1 到 100。 会计算到调度打分算法上,分数高的优先级高;

    operator:逻辑操作符,比如这里的in表示包含,一共有以下逻辑运算符;

      - In:label 的值在某个列表中
      - NotIn:label 的值不在某个列表中
      - Gt:label 的值大于某个值
      - Lt:label 的值小于某个值
      - Exists:某个 label 存在
      - DoesNotExist:某个 label 不存在
      
      #可用NotIn和DoesNotExist实现反亲和性;
    

    匹配规则:

    如果你同时指定了 nodeSelectornodeAffinity两者 必须都要满足, 才能将 Pod 调度到候选节点上。

    如果你在与 nodeAffinity 类型关联的 nodeSelectorTerms 中指定多个条件, 只要其中一个 nodeSelectorTerms 满足,Pod 就可以被调度到节点上。

    如果你在与 nodeSelectorTerms的一个 matchExpressions 中写个表达式, 则只有当所有表达式都满足,Pod 才能被调度到节点上。

    pod间的亲和反亲和性#

    前面的节点亲和性是通过pod和节点之间的标签进行匹配,选择的;

    pod的亲和性和反亲和性调度指:通过已在运行中的pod标签进行选择调度部署的节点;

    pod的亲和性调度:一个典型的使用场景就是在集群环境是有多数据中心的,那一个服务部署已经部署到广东了,那我跟他相关的需要大量通信的其他服务也尽量部署到广东,降低彼此间的通信延迟;

    pod的反亲和性调度:一个典型的使用场景就是我的服务要尽可能分散到各个数据中心、区域,比如广东、西安、上海、北京,都要有我的服务,避免某个数据中心故障服务全部宕机;

    示例

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demoapi-net6
      namespace: aspnetcore
      labels:
        name: demoapi-net6
    spec:
      replicas: 1
      selector:
        matchLabels:
          name: demoapi-net6
      template:
        metadata:
          labels:
            name: demoapi-net6
        spec:
          containers:
          - name: demoapi-net6
            image: gebiwangshushu/demoapi-net6      
          affinity: 
            podAffinity: #pod亲和性
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: restype
                    operator: In
                    values:
                    - strong-cpu
                topologyKey: topology.kubernetes.io/zone #topology.kubernetes.io/hostname 表示同一节点
            podAntiAffinity: #pod间反亲和性
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: disktype
                      operator: In
                      values:
                      - ssd 
                  topologyKey: topology.kubernetes.io/zone 
    

    亲和性规则表示:当且仅当至少一个已运行且有 restype=strong-cpu 的标签的 Pod 处于同一区域时(topology.kubernetes.io/zone=GuangDong),才可以将该 Pod 调度到节点上。

    反亲和性规则表示:如果节点处于 Pod 所在的同一可用区(也是看topology.kubernetes.io/zone)且至少一个 Pod 具有 disktype=ssd 标签,则该 Pod 不应被调度到该节点上。

    PS:Pod 间亲和性和反亲和性都需要一定的计算量,因此会在大规模集群中显著降低调度速度(比如上百个节点上千上万的pod),影响性能;

    这块我也用的不多,就写到这里;

    taint + tolerations -污点与容忍度调度#

    nodeSelector/nodeName和节点亲和性都是pod的一种属性,它可以主动选择某些节点。但如果Node想排他性地部署呢?答案就是污点+容忍的调度;

    名称理解#

    taint-污点:污点是节点用来排斥pod的一组标签,比如设置一个weak-cpu的污点;当然你也可以设置strong-cpu这种“污点”;

    toleration-容忍:容忍是pod用来容忍,接收节点污点的,比如给pod一个weak-cpu的容忍,这样它就可以被调度到weak-cpu的节点上了;

    taint-污点#

    新增污点#

    语法

    kubectl taint NODE NAME key1=value1:EFFECT(容忍的效果)
    

    示例

    kubectl taint nodes docker-desktop restype=strong-cpu:NoSchedule
    

    EFFECT取值

    • PreferNoSchedule: 尽量不要调度。
    • NoSchedule: 一定不能被调度。
    • NoExecute: 不仅不会调度, 还会驱逐 Node 上已有的 Pod。

    我们这时候再看看可以明显看到pod是pending状态

    Warning  FailedScheduling  12s   default-scheduler  0/1 nodes are available: 1 node(s) had taint {restype: strong-cpu}, that the pod didn't tolerate.
    

    查看污点#

    kubectl describe  node docker-desktop|grep Taints
    

    删除污点#

    kubectl taint node restype-
    

    tolerations-容忍#

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demoapi-net6
      namespace: aspnetcore
      labels:
        name: demoapi-net6
    spec:
        spec:
          containers:
          - name: demoapi-net6
            image: gebiwangshushu/demoapi-net6      
          tolerations: #添加容忍
          - key: "restype"
            operator: "Equal"
            value: "strong-cpu"
            effect: "NoSchedule"
    

    这里表示容忍restype=strong-cpu

    tolerations有两种写法#

    写法1、operator="Equal"

    tolerations: 
    - key: "restype"
      operator: "Equal"
      value: "strong-cpu"
      effect: "NoSchedule"
    

    这种写法时,key、value跟effect都要跟taint的一致;

    写法2、operator="Exists"

    tolerations:
    - key: "restype"
      operator: "Exists"
      effect: "NoSchedule"
    

    这种写法时,key、effect 跟taint的要一致,且不能写value的值;

    tolerationSeconds-容忍时间#

    容忍时间是指:以指定当节点失效时, Pod 依旧不被驱逐的时间。

    示例

    tolerations:
    - key: "node.kubernetes.io/unreachable"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 6000 #单位(秒)
    

    node.kubernetes.io/unreachable是k8s内置的污点,功能是让节点网络不可用时pod自动驱逐;这里tolerationSeconds: 6000,意思是网络不可用6000秒后,才开始驱逐;

    说明

    Kubernetes 会自动给 Pod 添加针对 node.kubernetes.io/not-readynode.kubernetes.io/unreachable 的容忍度,且配置 tolerationSeconds=300, 除非用户自身或者某控制器显式设置此容忍度。

    这些自动添加的容忍度意味着 Pod 可以在检测到对应的问题之一时,在 5 分钟内保持绑定在该节点上。

    其他规则

    DaemonSet 中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute 的容忍度将不会指定 tolerationSeconds

    • node.kubernetes.io/unreachable
    • node.kubernetes.io/not-ready

    容忍规则#

    1、operator="Exists"且key为空,表示这个容忍度与任意的 key、value 和 effect 都匹配,即这个容忍度能容忍任何污点。

    tolerations:
    - operator: "Exists"
    

    2、如果 effect 为空,,那么将匹配所有与 key 相同的 effect。

    tolerations:
    - key: "key"
      operator: "Exists"
    

    3、一个 node 可以有多个污点,一个 pod 可以有多个容忍。

    4、pod如果需要调度到某个node,需要容忍该node的所有污点;

    5、pod如果需要调度到某个node,但没有容忍该node的所有污点,且剩下的污点effect 均为 PreferNoSchedule,那存在调度的可能;

    6、如果 Node 上带有污点 effect 为 NoExecute,这个已经在 Node 上运行的 、不容忍该污点的Pod 会从 Node 上驱逐掉(调度到其他node);

    7、当集群只有一个 node 节点时,无法做到 Pod 迁移(主要是驱逐),因为 Pod 已经无路可退了。

    总结#

    总的来说k8s中Node&Pod的调度策略还是比较实用,常用的需求,学学防身没毛病;

    [参考]#

    https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/kube-scheduler/

    https://www.cnblogs.com/hukey/p/15724506.html

    👇专注于后端和架构,欢迎扫码关注我的公众号👇
  • 相关阅读:
    如何生成SSH服务器的ed25519公钥SHA256指纹
    OJ输入输出练习
    Java项目:SSM演唱会售票管理系统
    文献认证!Kamiya艾美捷抗酒石酸酸性磷酸酶TRAP染色试剂盒
    基于STM32结合CubeMX学习Free-RT-OS的源码之两类中断解析
    金仓数据库KingbaseES数据库参考手册(服务器配置参数6. 预写式日志)
    【C刷题】day1
    【达摩院OpenVI】视频目标渐进式Transformer跟踪器ProContEXT
    基于JSP+Servlet的屋租赁系统
    JFrame中有关于DefaultCloseOperation的使用及参数说明(含源码阅读)
  • 原文地址:https://www.cnblogs.com/xiaxiaolu/p/17238463.html