前言:
上一章学习了kubernetes的扩容,横向扩容、自动伸缩,这章开始学习扩缩容pod被调度到那个节点。
之前kubernetes允许去影响pod被调度到哪个节点,是通过在pod规范来指定节点选择器来实现,本章开始增加了其他的机制。
高级调度的两个特性:节点污点、pod对污点的容忍度
这两个特性被用于限制那些pod可以被调度到某一个节点。只有当一个pod容忍某个节点的污点,这个pod才能被调度带该节点。
说明:这与节点选择器和节点亲缘性有些不同。节点选择器和节点亲缘性规则,是通过明确的在pod中添加的信息,来决定一个pod可以或者不可以被调度到哪些节点。而污点则是不修改已有pod信息的前提下,通过在节点上污点信息,来拒绝pod在某些节点上的部署
默认情况下,一个集群的主节点需要设置污点,这样才能保证只有控制面板pod才能部署在这个主节点
显示节点的污点信息
可以通过kubect describe node查看节点的污点信息
$kubectl describe node master.k8s
一个pod只有容忍了节点的污点,才能被调度到该节点上面
显示pod的污点容忍度
在一个通过kubeadm初始化的集群中,kube-proxy集群组件以pod的形式运行在每个节点,其中也包括主节点,因为以pod形式运行的主节点组件同时也需要访问kubernetes服务。为了确保kube-proxy pod也能够运行在主节点上,该pod的需要添加相应的污点容忍度。
如下代码:
一个pod的污点容忍度
$kubectl describe po kube-proxy-80wqm -n kube-system
...
Tolerations:node-role.kubernetes.io/master=:NoSchedule
node.alpha.kubernetes.io/notReady=:Exists:NoExecute
node.alpha.kubernetes.io/unreachable=:Exists:NoExecute
注意:尽管pod的污点容忍度中显示了等号,但是在节点的污点信息中却没有。当污点或者污点容忍度中的value位null时,kubectl故意将污点和污点容忍度进行不同形式的显示
污点效果
每一个污点都可以关联一个效果,效果包含以下三种:
NoSchedule表示如果pod没有容忍这些污点,pod则不能被调度到包含这些污点的节点上
PreferNoSchedule是NoSchedule的一个宽松版本,表示尽量阻止pod被调度到这个节点,但是如果没有其他节点可以调度,pod依然会被调度到这个节点上
NoExecute不同于NoSchedule以及PreferNoSchedule,后两者只在调度期间起作用,而NoExecute也会影响正在节点上运行着的pod。如果在一个节点上添加了NoExecute污点,那些在该节点上运行着的pod,如果没有容忍这个NoExecute污点,将会从这个节点去除。
场景:假设你有一个单独的kubernetes集群,上面同时有生产环境和非生产环境的流量,要求:非生产环境的pod不能运行在生产环境的节点。可以通过在生产环境的节点上添加污点来满足这个要求。
使用kubectl taint来添加污点
$kubectl taint node node1.k8s node-type=production:NoSchedule
node "node1.k8s" tainted
这个命令添加了一个taint,其中key为node-type,value为production,效果为NoSchedule
这样就没有人随意将pod部署到生产环境节点上了
为了将生成环境pod部署到生产环境节点上,pod需要能容忍那些你添加在节点上的污点。在生产环境pod的清单里面需要增加yaml代码片段
production-deployment.yaml
apiVersion:extensions/v1beta1
kind:Deployment
metadata:
name:prod
spec:
replicas:5
template:
spec:
...
tolerations:
-key:node-type
operator:Equal
value:prodction
effect:NoSchedule
如果运行了这个部署,pod就会被调度到生产环境节点。
节点可以拥有多个污点,而pod也可以有多个污点容忍度。污点容忍度可以通过Equal操作符来指定匹配的value(默认情况下的操作符),或者也可以通过设置Exists操作符来匹配污点的key
在调度时使用污点和容忍度
污点可以用来组织新pod的调度(使用NoSchedule效果),或者定义非优先调度的节点(使用PreferNoSchedule效果),甚至是将已有的pod才能够当前节点剔除
可以使用任何你觉得合适的方式去设置污点和容忍度。如,可以将一个集群分成多个部分,只允许开发团队将pod调度到特定的节点。当你的部分节点提供了某种特殊硬件,并只有部分pod需要使用这些硬件的时候,也可以通过设置污点和容忍度的方式实现。
配置节点失效后的pod重新调度最长等待时间
可以配置一个容忍度,用于当某个pod运行所在的节点变成unready或者unreachabel状态时,kubernetes可以等待该pod被调度到其他节点的最长等待时间。
$kubectl get po prod-350605-1ph5h -o yaml
....
tolerations:
-effect:NoExecute
key:node.alpha.kubernetes.io/notReady
operator:Exists
tlerationSeconds:300
-effect:NoExecute
key:node.alpha.kubernetes.io/unreachable
operator:Exists
tolerationSeconds:300
如上这两个容忍度表示,该pod将容忍所在节点处于notReady或者unreachable状态维持300秒。当kubernetes控制器检测到有节点处于notReady或者unreachable状态时,将会等待300秒,如果状态持续的话,之后将把该pod重新调度到其他节点上。
前面一节的污点可以用来让pod远离特定的节点,节点亲缘性(node affinity)可以通知kubernetes将pod只调度到某几个节点子集上。
对比节点亲缘性和节点选择器
早期版本的kubernetes中,初始的节点亲缘机制,就是pod描述中的nodeSelector字段,节点必须包含所有pod对应字段中的指定abel,才能成为pod调动的目标节点
节点选择器实现简单,但是不能满足所有你的需求。一种更强打的机制被引入,节点选择器最终会被弃用。
与节点选择器类似,每个pod可以定义自己的节点亲缘性规则,这些规则可以允许你硬性限制或者偏好。如果指定一种偏好,将告知kubernetes对于某个特定的pod,他更倾向于调度到某些节点上,之后kubernetes将尽量把这个pod调度到这些节点上。如果无法实现的话,pod将被调度到其他某个节点上。
默认的节点标签
节点亲缘性根据节点的标签来进行选择,这点与节点选择器是一致的。
看下Google Kubernetes引擎集群(GKE)中节点的标签,默认标签是什么
GKE节点默认标签
$kuectl describe node gke-kubia-default-pool-db274c5a-mjnf
Name:gke-kubia-default-pool-db274c5a-mjnf
Role:
Labels:beta.kubernetes.io/arch=amd4
beta.kubernetes.io/fluentd-ds-ready=true
beta.kubernetes.io/instance-type=f1-micro
beta.kubernetes.io/os=linux
cloud.google.com/gke-nodepool=default-pool
failure-domain.beta.kubernetes.io/region=europe-west1
failure-domain.beta.kubernetes.io/zone=europe-west1-d
kubernetes.io/hostname=gke-kubia-default-pool-db2754c5a-mjnf
这个节点有很多标签,但涉及节点亲缘性和pod亲缘性时,最后三个红色标注的标签是最重要的。这三个标签含义如下:
failure-domain.beta.kubernetes.io/region=europe-west1 表示该节点所在的地理地域
failure-domain.beta.kubernetes.io/zone=europe-west1-d 表示该节点所在的可用性区域(availability zone)
kubernetes.io/hostname 很显然是该节点的主机名
这三个以及他的标签,被用于pod亲缘性规则。
例如:使用了节点选择器使得最需要GPU的pod只被调度到有GPU的节点上,包含了nodeSelector字段的pod描述如下代码:kubia-gpu-nodeselector.yaml
apiVersion:v1
kind:pod
metadata:
name:kubia-gpu
spec:
nodeSelector:
gpu:"true"
..
nodeSelector字段表示,pod只能被部署到包含了gpu=true标签的节点上,如果将节点选择器替换为节点亲缘性规则,pod定义如下:kubia-gpu-nodeaffinity.yaml
apiVersion:v1
kind:pod
metadata:
name:kubia-gpu
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
-matchExpressions:
-key:gpu
operator:In
values:
-"true"
亲缘性规则写法比简单的节点选择器复杂很多,所以他表达能力更强。
较长的节点亲缘性属性名意义
pod描述包含了affinity字段,该字段包含了nodeAffinity字段,这个字段有一个很长的名字requiredDuringSchedulingIgnoredDuringExecution,我们把这个名字分为两部分,然后分别看含义:
requiredDuringSchedulingI...表明了字段下定义的规则,为了让pod能调度到该节点上,明确指出该节点必须包含的标签
...IgnoredDuringExecution表明该字段下定义的规则,不会影响已经在节点运行的pod
亲缘性规则只会影响正在被调度的pod,并且不会导致已经在运行的pod被剔除,情况可能会更简单,这就是为什么目前的规则都是以IgnoredDuringExecution结尾的。最终,kubernetes也会支持RequiredDuringExecution,表示如果去除节点上的某个节点,那些需要节点包含该标签的pod将会被提出。但是目前还未支持
了解节点选择器条件
nodeSelectorTerms和matchExpressions字段定义了节点的标签必须满足哪一种表达式,才能满足pod调度的条件。
当调度某一个pod时,指定调度器可以优先考虑那些节点,这是通过perferredDuringSchedulingIgnoredDuringExecution字段来实现。
给节点加上标签
首先节点必须加上合适的标签。每个节点需要包含两个标签,一个用于表示所在的这个节点所归属的可用性区域,另一个用于表示这是一个独占的节点还是一个共享的节点。
指定优先级节点亲缘性规则
可以在deployment的yaml文件中用权重weight字段来确定优先级
如下为含有优先级节点亲缘性规则的Deployment的yaml文件:preferred-deployment.yaml
apiVersion:extensions/v1beta1
kind:Deployment
metadata:
name:pref
spec:
template:
...
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight:80
preference:
matchExpressions:
-key:availability-zone
operator:In
values:
-zone1
-weight:20
preference:
matchExpressions:
-key:share-type
operator:In
values:
-dedicated
如上代码,定义了一个节点亲缘性优先级,而不是强制要求。
上一节学的是节点亲缘性规则,以及该规则如何影响pod被调度到那个节点。但是这些规则只影响了pod和节点之间的亲缘性。有时候需要有能力指定pod自身之间的亲缘性。
如有一个前端pod和一个后端pod,为了降低延时,提高应用性能,将节点部署的比较靠近。可以使用节点亲缘性规则来确保这两个pod被调度到同一个节点、同一个机架、同一个数据中心。但是,之后还需要指定调度到具体哪个节点、哪个机架和哪个服务中心。目前这不是最佳解决方案,更好方法应该是让kubernetes将你的pod部署在任何它觉得合适的地方。同时确保2个pod都靠近,这种功能可以通过pod亲缘性来实现。
如需要部署1个后端和5个包含pod亲缘性配置的前端pod实例,是的这些前端实例将被部署在后端pod所在同一个节点上。
首先部署后端pod
$kubectl run backend -l app=backend --image busybox -- sleep 999999
deployment "backend " created
在pod中定义指定pod亲缘性
前端pod描述文件frontend-podaffinity-host.yaml
apiVersion:extensions/v1beta1
kind:Deployment
metadata:
name:frontend
spec:
replicas:5
template:
...
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologykey:kubernetes.io/hostname
matchLabels:
app:backend
....
注意:除了使用简单的matchLabels字段,也可以使用表达能力更强的matchExpressions字段
部署包含pod亲缘性的pod
$kubectl get po -o wide
前端部署pod
$kubectl create -f frontend-podaffinity-host.yaml
所有的前端pod都被调度到了和后端pod相同的节点上,当调度前端pod时,调度器首先找出所有匹配前端pod的podAffinity配置中的labelSelector的pod,之后将前端pod调度到相同的节点上。
了解调度如何使用pod亲缘性规则
如果现在删除了后端pod,调度器仍然会将该pod调度到前端pod所在节点上,即便后端pod没有定义任何pod亲缘性规则(只有前端pod设置了规则)。
前面例子中使用podAffinity将前端pod和后端pod部署在同一节点上,如果不希望所有的前端pod都部署在同一节点,但仍然希望和后端pod保持足够近,比如在同一可用性区域中。
在同一个可用性区域中协同部署od
可以通过将topologyKey属性设置为failure-domain.beta.kubernetes.io/zone,以确保前端pod和后端pod运行在同一个可用性区域
在同一区域中协同部署pod
可以通过将topologyKey属性设置为failure-domain.beta.kubernetes.io/region
节点亲缘性,nodeAffinity可以表示一种强制性要求,表示pod只能被调度到符合节点亲缘性规则的节点上,也可以表示一种节点优先级,用于告知调度器将pod调度到某些节点上,同时也满足当这些节点出于各种原因无法满足pod要求时,将pod调度到其他节点上。
这种特性同样适用于podAffinity,你可以告诉调度器,优先将前端pod调度到和后端pod相同的节点,但是如果不满足,调度到其他节点也可以。使用了一个preferrendDuringSchedulingIgnoredDuringExecution pod亲缘性规则的deployment。
yaml文件如下
apiVersion:extensions/v1beta1
kind:Deployment
metadata:
name:frontend
spec:
replicas:5
template:
...
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExection:
-weight:80
podAffinityTerm:
topologyKey:kubernetes.io/hostname
labelSelector:
matchLabels:
app:backend
containers...
和nodeAffinity优先级规则一样,需要为一个规则设置一个权重。同时也需要设置topologyKey和labelSelector。
如果需要pod远离彼此,可以使用pod非亲缘性,他和pod亲缘性的表达方式一样,只不过是将podAffinity字段换成了podAntiAffinity,这将导致调度器永远不会选择那些有包含podAntiAffinity匹配标签的pod所在节点。
说明:一个为什么需要使用pod非亲缘性的例子,就是当两个集合的pod,如果运行在同一个节点上回彼此影响性能。在这种情况下,就需要告知调度器永远不要将这些pod调度到同一个节点。
另一个例子是强制调度器将同一组的pod分在不同的可用性区域或者地区,这样让整个区域或者地区失效后,不会使得整个服务完全不可用。
使用非亲缘性分散一个部署中的pod
frontend-podantiaffinity-host.yaml
apiVersion:extensions/v1beta1
kind:Deployment
metadata:
name:frontend
spec:
replicas:5
template:
metadata:
labels:
app:frontend
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExection
- topologyKey:kubernetes.io/hostname
labelSelector:
machLabels:
app:frontend
containers:...
如上定义的是podAntiAffinity而不是podAffinity,并且将labelSelector和Deployment创建的pod匹配。
本章主要学习了如何通过节点的标签或者上面运行的pod,保证不把pod调度到某个节点,或者只把pod调度到特定节点上
如果在节点上添加1个污点信息,除非pod容忍这些污点,否则pod不会被调度到该节点
有3种污点类型:NoSchedule完全阻止调度,PreferNoSchedule不强制阻止调度,NoExecute会将已经运行的pod从节点剔除
NoExecute污点同时还可以设置,当pod运行的节点变成unreachable或者unready状态,pod需要重新调度时,控制面板最长的等待时间
节点亲缘性运行你去指定pod应该被调度到哪些节点上。他可以被用作一种强制性要求,也可以作为一种节点的优先级。
pod亲缘性被用于告知调度器将pod调度到和另一个pod相同的节点(基于pod的标签)上
pod亲缘性的topologyKey表示被调度的pod和另一个pod的距离(在同一个节点、同一个机柜、同一个可用性局域或者可用性地域)
pod非亲缘性可以被用于将pod调度到远离某些pod的节点
和节点亲缘性一样,pod亲缘性和非亲缘性可以设置上强制性要求还是优先级选择