理解 Kubernetes 的亲和性调度

这次给大家介绍下k8s的亲和性调度:nodeSelector、nodeAffinity、podAffinity、Taints以及Tolerations用法。
一般情况下我们部署的 POD 是通过集群自动调度选择某个节点的,默认情况下调度器考虑的是资源足够,并且负载尽量平均,但是有的时候我们需要能够更加细粒度的去控制 POD 的调度,比如我们内部的一些服务 gitlab 之类的也是跑在Kubernetes集群上的,我们就不希望对外的一些服务和内部的服务跑在同一个节点上了,害怕内部服务对外部的服务产生影响;有的时候呢我们两个服务直接交流比较频繁,又希望能够将这两个服务的 POD 调度到同样的节点上。这就需要用到 Kubernetes 里面的一个概念:亲和性,亲和性主要分为两类:nodeAffinity和podAffinity。
nodeSelector
我们知道label是kubernetes中一个非常重要的概念,用户可以非常灵活的利用 label 来管理集群中的资源,比如最常见的一个就是 service 通过匹配 label 去选择 POD 的。而 POD 的调度也可以根据节点的 label 进行特定的部署。
我们可以通过下面的命令查看我们的 node 的 label:
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
192.168.1.140 Ready <none> 42d v1.8.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=192.168.1.140
192.168.1.161 Ready <none> 118d v1.8.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/cluster-service=true,kubernetes.io/hostname=192.168.1.161
192.168.1.170 Ready <none> 118d v1.8.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/cluster-service=true,kubernetes.io/hostname=192.168.1.170
192.168.1.172 Ready <none> 114d v1.8.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/cluster-service=true,kubernetes.io/hostname=192.168.1.172
现在我们先给节点192.168.1.140增加一个source=qikqiak的标签,命令如下:
$ kubectl label nodes 192.168.1.140 source=qikqiak
node "192.168.1.140" labeled
我们可以通过上面的--show-labels参数可以查看上述标签是否生效。当 node 被打上了相关标签后,在调度的时候就可以使用这些标签了,只需要在 POD 的 spec 字段中添加nodeSelector字段,里面是我们需要被调度的节点的 label。例如,下面是我们之前的一个默认的 busybox POD 的 YAML 文件:
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox-pod
name: test-busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: test-busybox
然后我需要让上面的 POD 被调度到140的节点上,那么最简单的方法就是去匹配140上面的 label,如下:
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox-pod
name: test-busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: test-busybox
nodeSelector:
source: qikqiak
然后我们可以通过 describe 命令查看调度结果:
$ kubectl describe pod test-busybox
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 49s default-scheduler Successfully assigned test-busybox to 192.168.1.140
Normal SuccessfulMountVolume 49s kubelet, 192.168.1.140 MountVolume.SetUp succeeded for volume "default-token-hmpbz"
Normal Pulling 49s kubelet, 192.168.1.140 pulling image "busybox"
Normal Pulled 41s kubelet, 192.168.1.140 Successfully pulled image "busybox"
Normal Created 41s kubelet, 192.168.1.140 Created container
Normal Started 41s kubelet, 192.168.1.140 Started container
我们可以看到 Events 下面的信息,上面的 POD 被正确的调度到了140节点。通过上面的例子我们可以感受到nodeSelector的方式比较直观,但是还够灵活,控制粒度偏大,下面我们再看另外一种更加灵活的方式:nodeAffinity。
nodeAffinity
nodeAffinity就是节点亲和性,相对应的是Anti-Affinity,就是反亲和性,这种方法比上面的nodeSelector更加灵活,它可以进行一些简单的逻辑组合了,不只是简单的相等匹配。 调度可以分成软策略和硬策略两种方式,软策略就是如果你没有满足调度要求的节点的话,POD 就会忽略这条规则,继续完成调度过程,说白了就是满足条件最好了,没有的话也无所谓了的策略;而硬策略就比较强硬了,如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然我就不干的策略。 nodeAffinity就有两上面两种策略:preferredDuringSchedulingIgnoredDuringExecution和requiredDuringSchedulingIgnoredDuringExecution,前面的就是软策略,后面的就是硬策略。
如下例子:(test-node-affinity.yaml)
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- 192.168.1.140
- 192.168.1.161
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: source
operator: In
values:
- qikqiak
上面这个 POD 首先是要求 POD 不能运行在140和161两个节点上,如果有个节点满足source=qikqiak的话就优先调度到这个节点上,同样的我们可以使用descirbe命令查看具体的调度情况是否满足我们的要求。这里的匹配逻辑是 label 的值在某个列表中,现在Kubernetes提供的操作符有下面的几种:
- In:label 的值在某个列表中
- NotIn:label 的值不在某个列表中
- Gt:label 的值大于某个值
- Lt:label 的值小于某个值
- Exists:某个 label 存在
- DoesNotExist:某个 label 不存在
如果
nodeSelectorTerms下面有多个选项的话,满足任何一个条件就可以了;如果matchExpressions有多个选项的话,则必须同时满足这些条件才能正常调度 POD。
podAffinity
上面两种方式都是让 POD 去选择节点的,有的时候我们也希望能够根据 POD 之间的关系进行调度,Kubernetes在1.4版本引入的podAffinity概念就可以实现我们这个需求。
和nodeAffinity类似,podAffinity也有requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution两种调度策略,唯一不同的是如果要使用互斥性,我们需要使用podAntiAffinity字段。 如下例子,我们希望with-pod-affinity和busybox-pod能够就近部署,而不希望和node-affinity-pod部署在同一个拓扑域下面:(test-pod-affinity.yaml)
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
labels:
app: pod-affinity-pod
spec:
containers:
- name: with-pod-affinity
image: nginx
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox-pod
topologyKey: kubernetes.io/hostname
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- node-affinity-pod
topologyKey: kubernetes.io/hostname
上面这个例子中的 POD 需要调度到某个指定的主机上,至少有一个节点上运行了这样的 POD:这个 POD 有一个app=busybox-pod的 label。podAntiAffinity则是希望最好不要调度到这样的节点:这个节点上运行了某个 POD,而这个 POD 有app=node-affinity-pod的 label。根据前面两个 POD 的定义,我们可以预见上面这个 POD 应该会被调度到140的节点上,因为busybox-pod被调度到了140节点,而node-affinity-pod被调度到了140以为的节点,正好满足上面的需求。通过describe查看:
$ kubectl describe pod with-pod-affinity
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8s default-scheduler Successfully assigned with-pod-affinity to 192.168.1.140
Normal SuccessfulMountVolume 7s kubelet, 192.168.1.140 MountVolume.SetUp succeeded for volume "default-token-lcl77"
Normal Pulling 7s kubelet, 192.168.1.140 pulling image "nginx"
上面的事件信息也验证了我们的想法。
在
labelSelector和topologyKey的同级,还可以定义 namespaces 列表,表示匹配哪些 namespace 里面的 pod,默认情况下,会匹配定义的 pod 所在的 namespace;如果定义了这个字段,但是它的值为空,则匹配所有的 namespaces。
查看上面我们定义的3个 POD 结果:
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
test-busybox 1/1 Running 0 8m 172.30.95.18 192.168.1.140
with-node-affinity 1/1 Running 0 10m 172.30.81.25 192.168.1.172
with-pod-affinity 1/1 Running 0 8m 172.30.95.17 192.168.1.140
亲和性/反亲和性调度策略比较如下:
| 调度策略 | 匹配标签 | 操作符 | 拓扑域支持 | 调度目标 |
|---|---|---|---|---|
| nodeAffinity | 主机 | In, NotIn, Exists, DoesNotExist, Gt, Lt | 否 | 指定主机 |
| podAffinity | POD | In, NotIn, Exists, DoesNotExist | 是 | POD与指定POD同一拓扑域 |
| podAnitAffinity | POD | In, NotIn, Exists, DoesNotExist | 是 | POD与指定POD不在同一拓扑域 |
污点(Taints)与容忍(tolerations)
对于nodeAffinity无论是硬策略还是软策略方式,都是调度 POD 到预期节点上,而Taints恰好与之相反,如果一个节点标记为 Taints ,除非 POD 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度pod。
比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 POD,则污点就很有用了,POD 不会再被调度到 taint 标记过的节点。taint 标记节点举例如下:
$ kubectl taint nodes 192.168.1.40 key=value:NoSchedule
node "192.168.1.40" tainted
如果仍然希望某个 POD 调度到 taint 节点上,则必须在 Spec 中做出Toleration定义,才能调度到该节点,举例如下:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
effect 共有三个可选项,可按实际需求进行设置:
NoSchedule:POD 不会被调度到标记为 taints 节点。PreferNoSchedule:NoSchedule 的软策略版本。NoExecute:该选项意味着一旦 Taint 生效,如该节点内正在运行的 POD 没有对应 Tolerate 设置,会直接被逐出。
参考资料
理解 Kubernetes 的亲和性调度的更多相关文章
- Kubernetes 学习20调度器,预选策略及优选函数
一.概述 1.k8s集群中能运行pod资源的其实就是我们所谓的节点,也称为工作节点.master从本质上来讲,他其实是运行整个集群的控制平面组件的比如apiserver,scheal,controlm ...
- Kubernetes集群调度器原理剖析及思考
简述 云环境或者计算仓库级别(将整个数据中心当做单个计算池)的集群管理系统通常会定义出工作负载的规范,并使用调度器将工作负载放置到集群恰当的位置.好的调度器可以让集群的工作处理更高效,同时提高资源利用 ...
- 理解Kubernetes(2): 应用的各种访问方式
理解Kubernetes系列文章: 手工搭建环境 应用的各种访问方式 1. 通过 Pod 的 IP 地址访问应用 1.1 Pod 的IP地址 每个Pod 都会被分配一个IP地址,比如下面这儿pod的I ...
- kubernetes之pod调度
调度规则 deployment全自动调度: 运行在哪个节点上完全由master的scheduler经过一系列的算法计算得出, 用户无法进行干预 nodeselector定向调度: 指定pod调度到一些 ...
- 后端技术杂谈11:十分钟理解Kubernetes核心概念
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 本文转自 https://github.com/h2pl/Java-Tutorial 喜欢的 ...
- istio kiali 亲和性调度
一.节点调度 在开始 kiali 亲和性调度之前,先演示一个简单的例子介绍 pod 选择调度到指定 node: 节点打标 使用命令查看当前所有 k8s 节点: [root@k8s-master ~]# ...
- 1.通俗易懂理解Kubernetes核心组件及原理
文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247483736&idx=1&sn=0cbc3d6a ...
- 理解Kubernetes(1):手工搭建Kubernetes测试环境
系列文章: 1. 手工搭建环境 1. 基础环境准备 准备 3个Ubuntu节点,操作系统版本为 16.04,并做好以下配置: 系统升级 设置 /etc/hosts 文件,保持一致 设置从 0 节点上无 ...
- Kubernetes K8S之调度器kube-scheduler详解
Kubernetes K8S之调度器kube-scheduler概述与详解 kube-scheduler调度概述 在 Kubernetes 中,调度是指将 Pod 放置到合适的 Node 节点上,然后 ...
随机推荐
- Nginx 核心配置-自定义错误页面
Nginx 核心配置-自定义错误页面 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 生产环境中错误页面一般都是UI或开发工程师提供的,他们已经在软件中定义好了,我们这里就简单写个h ...
- springboot集成spring data ElasticSearch
ES支持SpringBoot使用类似于Spring Data Jpa的方式查询,使得查询更加方便. 1.依赖引入 compile “org.springframework.boot:spring-bo ...
- opencart按重量配送如何设置
OpenCart中基于重量的配送模块让你使用自己的价格折算表来决定配送价格.可以基于不同的重量允许设置价格范围,重量和价格的格式如下:weight(重量1):cost(价格1), weight(重量2 ...
- CSV读取
可以在Excel中编辑好后 另存为CVS文件
- (HK1-0)激活与配置摄像机
HK使用手册 网络连接 激活与配置摄像机 网络摄像机可通过 SADP 软件.客户端软件和浏览器三种方式激活, 具体激活操作方式可参见<网络摄像机操作手册>. 1. 安装随机光盘或从官网下载 ...
- MongoDB executionStats 详细分步查询计划与分步时间(转载)
mongodb性能分析方法:explain() 为了演示的效果,我们先来创建一个有200万个文档的记录.(我自己的电脑耗了15分钟左右插入完成.如果你想插更多的文档也没问题,只要有耐心等就可以了.) ...
- django认证系统-user对象(创建,改密,认证)
User对象 User对象是认证系统的核心.它们通常表示与你的站点进行交互的用户,并用于启用限制访问.注册用户信息和关联内容给创建者等.在Django的认证框架中只存在一种类型的用户,因此诸如'sup ...
- HashMap和HashTable本质性的区别
一,HashMap 1.HashMap是键值对key-value形式双列集合.它的底层存储原理是哈希表. 2.对应HashMap采用哈希表存储键值对元素的方式. HashMap.put(key,val ...
- django -- 母版继承
csrf_token 在之前我们提交post请求的时候,都是在setting.py文件里注释掉了 'django.middleware.csrf.CsrfViewMiddleware' 这一行,这是因 ...
- cf1179D
cf1179D 链接 cf 思路 csdn 很玄学,正解是斜率优化dp,但被一个奇妙的贪心过了. 代码 #include <bits/stdc++.h> #define ll long l ...