完事后再聊应用场景,K8S调度实战:Node Affinity

算法听星使
• 阅读 349

写在开篇

Node Affinity(节点亲和性)允许在节点级别上指定一些条件来控制Pod被调度到哪些节点上。它还有两种策略,本篇通过实战演示如何使用两种策略来控制Pod的调度。

测试环境

还是老样子,本次实战继续使用以下K8S集群环境进行:

节点主机名IP
Master节点k8s-b-master192.168.11.100
工作节点1k8s-b-node01192.168.11.101
工作节点2k8s-b-node02192.168.11.102
工作节点3k8s-b-node03192.168.11.103
工作节点4k8s-b-node04192.168.11.104
工作节点5k8s-b-node05192.168.11.105
工作节点6k8s-b-node06192.168.11.106

开始实战

在本次实战中,使用一个名为goweb的测试应用程序来演示Node Affinity的使用。goweb是我用Golang开发的简单Web应用程序,用于测试和验证K8S的调度策略。当然了,你也可以自己开发一个类似的应用,然后使用自己的应用来进行本篇的实战内容。

策略1

在这个实战案例中,我将使用requiredDuringSchedulingIgnoredDuringExecution策略,该策略要求Pod只能调度到特定的节点上。  我将创建一个Node Affinity规则,要求goweb应用只能调度到主机名为k8s-b-node03的节点上。

首先,需要创建一个名为goweb-node-affinity.yaml的文件,并将以下内容复制到文件中:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb
  namespace: goweb-namespace
spec:
  replicas: 3
  selector:
    matchLabels:
      app: goweb
  template:
    metadata:
      labels:
        app: goweb
    spec:
      containers:
      - name: goweb
        image: 192.168.11.253/library/goweb:latest
        ports:
        - containerPort: 80
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - k8s-b-node03

保存文件后,使用以下命令应用goweb应用的Node Affinity规则:

kubectl apply -f goweb-node-affinity.yaml

现在,K8S调度器将只会将goweb应用调度到主机名为k8s-b-node03的节点上。

[root@k8s-b-master ~]# kubectl get pod -n goweb-namespace -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
goweb-5559b4bbf5-5mpd8   1/1     Running   0          64s   10.244.199.138   k8s-b-node03   <none>           <none>
goweb-5559b4bbf5-lbq4j   1/1     Running   0          63s   10.244.199.139   k8s-b-node03   <none>           <none>
goweb-5559b4bbf5-lqkzh   1/1     Running   0          70s   10.244.199.137   k8s-b-node03   <none>           <none>

策略2

在这个实战案例中,我将使用preferredDuringSchedulingIgnoredDuringExecution策略,该策略尽量将Pod调度到满足条件的节点上,但不是强制要求。  我将创建一个Node Affinity规则,优先将goweb应用调度到拥有标签app=goweb-node的节点上。

首先,我创建一个名为goweb-preferred-node-affinity.yaml的文件,并将以下内容复制到文件中:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb
  namespace: goweb-namespace
spec:
  replicas: 3
  selector:
    matchLabels:
      app: goweb
  template:
    metadata:
      labels:
        app: goweb
    spec:
      containers:
      - name: goweb
        image: 192.168.11.253/library/goweb:latest
        ports:
        - containerPort: 80
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: app
                operator: In
                values:
                - goweb-node

保存文件后,使用以下命令应用goweb应用的Node Affinity规则:

kubectl apply -f goweb-preferred-node-affinity.yaml

现在,K8S调度器将优先将goweb应用调度到拥有标签app=goweb-node的节点上,但如果没有满足条件的节点,它仍然可以调度到其他节点上。

查看node标签和调度结果:

# 查看label:
[root@k8s-b-master ~]# kubectl get node --show-labels
NAME           STATUS   ROLES           AGE   VERSION   LABELS
k8s-b-master   Ready    control-plane   49d   v1.25.4   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-b-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-b-node01   Ready    <none>          49d   v1.25.4   app=goweb-node,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-b-node01,kubernetes.io/os=linux
k8s-b-node02   Ready    <none>          49d   v1.25.4   app=goweb-node,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-b-node02,kubernetes.io/os=linux
...

# 调度结果:
[root@k8s-b-master ~]# kubectl get pod -n goweb-namespace -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
goweb-58799f9b4c-cd5j4   1/1     Running   0          16s   10.244.51.204    k8s-b-node01   <none>           <none>
goweb-58799f9b4c-hpwlt   1/1     Running   0          16s   10.244.232.147   k8s-b-node02   <none>           <none>
goweb-58799f9b4c-ksps5   1/1     Running   0          16s   10.244.232.146   k8s-b-node02   <none>           <none>

删除app标签后再次查看调度结果:

# 删除
kubectl label nodes k8s-b-node01 app-
kubectl label nodes k8s-b-node02 app-

# 调度结果:
[root@k8s-b-master ~]# kubectl get pod -n goweb-namespace -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
goweb-58799f9b4c-5hr8h   1/1     Running   0          2s    10.244.25.77     k8s-b-node05   <none>           <none>
goweb-58799f9b4c-5vnr9   1/1     Running   0          2s    10.244.232.148   k8s-b-node02   <none>           <none>
goweb-58799f9b4c-hwxjc   1/1     Running   0          2s    10.244.151.4     k8s-b-node06   <none>           <none>
注意到了吧?删除app标签后,就没有满足条件的节点了,但它仍然可以调度到其他节点上。通过实战,已经证实了这一点。

使用场景

下面聊聊两种策略在实际工作中可能的使用场景。

1. requiredDuringSchedulingIgnoredDuringExecution

它的特点是:强制要求Pod只能调度到特定节点的策略。可能适用的场景:

  • 硬件特性要求:比如对底层硬件有特殊要求的应用,就可以将Pod调度到拥有特定标签货其它属性的节点上。
  • 数据本地性要求:比如需要访问特定数据源的应用,将Pod调度到与数据源最近的节点上,这样可以减少网络延迟和提高性能。
  • 资源隔离:将特定类型的任务或工作负载隔离到专用的节点上。

举个贴近实际的例子,假设有一组节点,其中几个节点拥有牛逼的CPU和内存资源。希望将一些需要较高计算能力的任务调度到这些节点上。这时候就可以使用此策略来指定这些节点的标签或其他属性,就可以将Pod限制在这些节点上了。

2. preferredDuringSchedulingIgnoredDuringExecution

它的特点是:优先选择满足条件的节点进行调度的策略。可能适用的场景:

  • 资源利用率优化:比如希望将Pod调度到拥有特定资源的节点上,但如果没有满足条件的节点,仍然可以将Pod调度到其他节点上。
  • 可用性和容错性:为了提高应用程序的可用性和容错性,我们可以将Pod调度到多个节点上,但仍然优先将其调度到特定节点上。如果该节点不可用,调度器将选择次优节点进行调度。

举个例子,假设有一组节点,其中一些节点的网卡连接的是光交换机,网络更快。希望将网络密集型的应用优先调度到这些节点上,这时候就可以指定这些节点的网络特性标签或其他属性。

再举个例子,假设在具有不同地理位置的多个数据中心中部署应用。就可以优先将Pod调度到与应用所服务的区域最近的节点上,以此来达到减少延迟、提高用户体验的效果。

我为什么要用“可能适用的场景”来说明?因为我觉得,看这篇文章的你应该可以想到更好的应用场景,欢迎留言讨论。

最后总结

接下来做个关键性的总结:

  1. Node Affinity有两种策略:
  • requiredDuringSchedulingIgnoredDuringExecution:调度器只有在规则被满足的时候才能执行调度(硬策略)
  • preferredDuringSchedulingIgnoredDuringExecution:调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该Pod(软策略)
  1. 匹配方式有两种:
  • matchExpressions:基于标签的匹配,可以包含一个或多个条件,每个条件由键、运算符和值组成,适用于更复杂的标签匹配逻辑的场景。
  • matchFields:允许基于节点的字段选择节点,而不是基于标签。比如指定节点上的字段名称和值进行匹配。这对于选择节点的特定属性非常有用,例如节点的操作系统、内存大小等。
  1. operator属性可能的枚举值:
  • "DoesNotExist": 用于检查标签或注解不存在的情况。
  • "Exists": 用于检查标签或注解存在的情况。
  • "Gt": 用于数字类型的匹配,表示大于指定值。
  • "In": 用于匹配给定值列表中的任何一个值。
  • "Lt": 用于数字类型的匹配,表示小于指定值。
  • "NotIn": 用于匹配不在给定值列表中的任何一个值。

希望本篇的实战能对你理解和使用K8S中的Node Affinity特性有所帮助。你可以根据实际需求和集群环境,灵活应用Node Affinity来优化应用的调度行为。好了,本篇就到此结束,有问题可以评论区留言讨论或者私信讨论。


注重运维实战,我们比谁都拼!日常分享实用干货,助你成为运维大神!探索技术的魅力,从这里开始!

点击链接,畅读精彩文章,从中获取洞见,为自己的技术之旅注入新的动力!关注我的微信公众号,不错过更多精彩内容。

【K8S(专注于深入研究K8S相关的各种技术和知识分享。)】:https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz...

【Go&Py(涵盖了Go和Python两种流行的编程语言。)】:https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz...

【Ops(运维领域的讨论和交流。)】:https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz...

点赞
收藏
评论区
推荐文章
Prodan Labs Prodan Labs
4年前
Kubernetes自定义调度器 — 初识调度框架
Kubernetes已经成为容器编排(Orchestration)平台的事实标准,它为容器化应用提供了简单且高效部署的方式、大规模可伸缩、资源调度等生命周期管理功能。kubescheduler作为kubernetes的核心组件,它负责整个集群资源的调度功能,根据特定的调度算法或调度策略,将Pod调度到最优的Node节点,使集群的资源得到合理且充分的利用。
Prodan Labs Prodan Labs
4年前
Kubernetes自定义调度器 — 初窥门径
通过上一篇文章对schedulerframework调度框架已经有了大致了解,根据我们的实际生产的一些问题(如计算服务没有被调度到实际CPU最优的节点)和需求,来实现一个简单的基于CPU指标的自定义调度器。自定义调度器通过kubernetes资源指标服务metricsserver来获取各节点的当前的资源情况,并进行打分,然后把Pod调度到分数最高的节
DevOpSec DevOpSec
2年前
kubernetes之pod拓扑分布约束
kubernetes之pod拓扑分布约束在日常使用kubernetes的过程中中,很多时候我们并没有过多的关心pod的到底调度在哪里,只是通过多副本的测试,来提高的我们的业务的可用性,但是当多个
Wesley13 Wesley13
3年前
K8S从入门到放弃系列
摘要:Kubelet组件运行在Node节点上,维持运行中的Pods以及提供kuberntes运行时环境,主要完成以下使命:  1.监视分配给该Node节点的pods  2.挂载pod所需要的volumes  3.下载pod的secret  4.通过docker/rkt来运行pod中的容器  5.周期的执行pod中为容器定义的
Wesley13 Wesley13
3年前
K8S基础概念
一、核心概念1、NodeNode作为集群中的工作节点,运行真正的应用程序,在Node上Kubernetes管理的最小运行单元是Pod。Node上运行着Kubernetes的Kubelet、kubeproxy服务进程,这些服务进程负责Pod的创建、启动、监控、重启、销毁、以及实现软件模式的负载均衡。Node包含
Stella981 Stella981
3年前
Kubernetes 调度器实现初探
Kubernetes调度器Kubernetes是一个基于容器的分布式调度器,实现了自己的调度模块。在Kubernetes集群中,调度器作为一个独立模块通过pod运行。从几个方面介绍Kubernetes调度器。调度器工作方式Kubernetes中的调度器,是作为单独组件运行,一般运行在Master中,和Master数量保持
Stella981 Stella981
3年前
Kubernetes Pod OOM 排查日记
一、发现问题在一次系统上线后,我们发现某几个节点在长时间运行后会出现内存持续飙升的问题,导致的结果就是Kubernetes集群的这个节点会把所在的Pod进行驱逐OOM;如果调度到同样问题的节点上,也会出现Pod一直起不来的问题。我们尝试了杀死Pod后手动调度的办法(label),当然也可以排除调度节点。但是在一段时间后还会复现,我们通过监控
Stella981 Stella981
3年前
CocosCreator 代码组件(创建销毁节点、访问节点和组件) (第四篇)
前言:在游戏开发中,我么都是通过代码来控制场景中的节点,下面讲解怎么用代码,创建节点、销毁节点、访问节点、访问组件。一、创建和销毁节点1\.创建新节点除了通过场景编辑器创建节点外,我们也可以在脚本中动态创建节点。通过newcc.Node()并将它加
Crane-scheduler:基于真实负载进行调度
作者邱天,腾讯云高级工程师,负责腾讯云TKE动态调度器与重调度器产品。背景原生kubernetes调度器只能基于资源的resourcerequest进行调度,然而Pod的真实资源使用率,往往与其所申请资源的request/limit差异很大,这直接导致了集群负载不均的问题:1.集群中的部分节点,资源的真实使用率远低于resourcerequest,却没有被调度更多的Pod,这造成了比较大的资源浪费;2.而集群中的另外一些节点,其资源的真实使用率事实上已经过载,却无法为调
Crane-scheduler:基于真实负载进行调度
作者邱天,腾讯云高级工程师,负责腾讯云TKE动态调度器与重调度器产品。背景原生kubernetes调度器只能基于资源的resourcerequest进行调度,然而Pod的真实资源使用率,往往与其所申请资源的request/limit差异很大,这直接导致了集群负载不均的问题:1.集群中的部分节点,资源的真实使用率远低于resourc
一种基于Nginx的热点数据调度处理方法
基于Nginx的热点数据调度处理,热点节点数据负载均衡处理,减少热点节点压力,提高处理和访问效率;每一个节点的nginx服务接收大量的访问,但是每个节点处理请求都有一个峰值,当请求数达到峰值时,后续请求的处理效率就会有一定的下降,为了保证请求能及时处理,热点节点会触发请求调度策略,转发请求到非热点节点进行处理,若无非热点节点,则触发分布式节点策略,备机节点会启动Nginx服务处理,并接收热点节点转发过来的请求,从而提升访问及处理效率。