前一篇文章 Kubernetes 调度器浅析,大体讲述了调度器的工做原理及相关调度策略。这一章会继续深刻调度器,介绍下“亲和性调度”。html
Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定更倾向于在某些特定 Node 上运行。
有几种方式能够实现这个功能:node
NodeName
: 最简单的节点选择方式,直接指定节点,跳过调度器。NodeSelector
: 早期的简单控制方式,直接经过键—值对将 Pod 调度到具备特定 label 的 Node 上。NodeAffinity
: NodeSelector 的升级版,支持更丰富的配置规则,使用更灵活。(NodeSelector 将被淘汰.)PodAffinity
: 根据已在节点上运行的 Pod 标签来约束 Pod 能够调度到哪些节点,而不是根据 node label。nodeName 是 PodSpec 的一个字段,用于直接指定调度节点,并运行该 pod。调度器在工做时,实际选择的是 nodeName 为空的 pod 并进行调度而后再回填该 nodeName,因此直接指定 nodeName 实际是直接跳过了调度器。换句话说,指定 nodeName 的方式是优于其余节点选择方法。nginx
方法很简单,直接来个官方示例:git
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx nodeName: kube-01
固然若是选择的节点不存在,或者资源不足,那该 pod 必然就会运行失败。github
nodeSelector 也是 PodSpec 中的一个字段,指定键—值对的映射。
若是想要将 pod 运行到对应的 node 上,须要先给这些 node 打上 label,而后在 podSpec.NodeSelector 指定对应 node labels 便可。web
步骤以下:redis
kubectl label nodes kubernetes-node type=gpu
算法
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx nodeSelector: type: gpu
有些标签是对云提供商使用。
nodeSelector 经过 k-v 的方式很是简单的支持了 pod 调度限制到具备特定标签的节点上。而 nodeAffinity 根据亲和力 & 反亲和力极大地扩展了可以表达的约束信息。segmentfault
nodeAffinity 特性的设计初衷就是为了替代 nodeSelector。
nodeAffinity 当前支持的匹配符号包括:In、NotIn、Exists、DoesNotExists、Gt、Lt 。api
nodeAffinity 当前支持两种调度模式:
requiredDuringSchedulingIgnoredDuringExecution
: 必定要知足的条件,若是没有找到知足条件的节点,则 Pod 建立失败。全部也称为hard 模式
。preferredDuringSchedulingIgnoredDuringExecution
: 优先选择知足条件的节点,若是没有找到知足条件的节点,则在其余节点中择优建立 Pod。全部也称为 soft 模式
。两种模式的名字特长,这是 k8s 的命名风格。其中IgnoredDuringExecution
的意义就跟 nodeSelector
的实现同样,即便 node label 发生变动,也不会影响以前已经部署且又不知足 affinity rules 的 pods,这些 pods 还会继续在该 node 上运行。换句话说,亲和性选择节点仅在调度 Pod 时起做用。
k8s 社区正在计划提供
requiredDuringSchedulingRequiredDuringExecution
模式,便于驱逐 node 上不知足 affinity rules 的 pods。
来个官方示例,看下怎么玩:
apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: nodeAffinity: # 必须选择 node label key 为 kubernetes.io/e2e-az-name, # value 为 e2e-az1 或 e2e-az2. requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/e2e-az-name operator: In values: - e2e-az1 - e2e-az2 # 过滤掉上面的必选项后,再优先选择 node label key 为 another-node-label-key # value 为 another-node-label-value. preferredDuringSchedulingIgnoredDuringExecution: # 若是知足节点亲和,积分加权重(优选算法,会对 nodes 打分) # weight: 0 - 100 - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0
简单看下 NodeAffinity 的结构体,下面介绍注意事项时会涉及:
type NodeAffinity struct { RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm } type NodeSelector struct { NodeSelectorTerms []NodeSelectorTerm } type NodeSelectorTerm struct { MatchExpressions []NodeSelectorRequirement MatchFields []NodeSelectorRequirement }
配置相关的注意点:
nodeSelector
和 nodeAffinity
二者都指定,那 node 须要两个条件都知足,pod 才能调度。NodeSelectorTerms
,那 node 只要知足其中一个条件,pod 就能够进行调度。MatchExpressions
,那必需要知足全部条件,才能将 pod 调度到该 node。nodeSelector & nodeAffinity 都是基于 node label 进行调度。而有时候咱们但愿调度的时候能考虑 pod 之间的关系,而不仅是 pod 和 node 的关系。
举个例子,会有需求但愿服务 A 和 B 部署在同一个机房、机架或机器上,由于这些服务可能会对网路延迟比较敏感,须要低延时;再好比,但愿服务 C 和 D 又但愿尽可能分开部署,即便一台主机甚至一个机房出了问题,也不会致使两个服务一块儿挂而影响服务可用性,提高故障容灾的能力。
podAffinity 会基于节点上已经运行的 pod label 来约束新 pod 的调度。
其规则就是“若是 X 已经运行了一个或者多个符合规则 Y 的 Pod,那么这个 Pod 应该(若是是反亲和性,则是不该该)调度到 X 上”。
这里的 Y 是关联 namespace 的 labelSelector,固然 namespace 也能够是 all。和 node 不一样,pod 是隶属于 namespace 下的资源,因此基于 pod labelSelector 必须指定具体的 namespace;而 X 则能够理解为一个拓扑域,相似于 node、rack、zone、cloud region 等等,就是前面提到的 内置 Node 标签
,固然也能够自定义。
看下 pod affinity 涉及的结构体,便于进行功能介绍:
type Affinity struct { // NodeAffinity 前面介绍了 NodeAffinity *NodeAffinity // pod 亲和性 PodAffinity *PodAffinity // pod 反亲和性 PodAntiAffinity *PodAntiAffinity } type PodAffinity struct { // hard 模式, 必选项 RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm // soft 模式, 进行 node 优先 PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm } type PodAffinityTerm struct { LabelSelector *metav1.LabelSelector Namespaces []string TopologyKey string } type WeightedPodAffinityTerm struct { Weight int32 PodAffinityTerm PodAffinityTerm }
podAffinity 和 nodeAffinity 有类似的地方,使用了 labelSelector 进行匹配,支持的匹配符号包括:In、NotIn、Exists、DoesNotExists;
也支持两种调度模式 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
, 功能和 nodeAffinity 同样,这里就不在累述。
podAffinity 和 nodeAffinity 也有较大的差别,前面讲了 pod 是 namespace 资源,因此必然会须要配置 namespaces,支持配置多个 namespace。若是省略的话,默认为待调度 pod 所属的 namespace;若是定义了可是值为空,则表示使用 “all” namespaces。
还有一个较大的差异 TopologyKey
, 便于理解进行单独介绍。
TopologyKey 用于定义 in the same place
,前面也介绍了是拓扑域
的概念。
看下面的图,这两个 pod 到底该如何算在一个拓扑域?
若是咱们使用k8s.io/hostname
,in the same place
则意味着在同一个 node,那下图的 pods 就不在一个 place:
若是咱们使用failure-domain.k8s.io/zone
来表示一个 place,那下图的 pods 就表示在一个 zone:
固然咱们也能够自定义 node labels 做为 TopologyKey。好比咱们能够给一组 node 打上 rack
标签,那下图的 pods 表示在同一个 place:
原则上,topologyKey 能够是任何合法的 label key。可是出于性能和安全考虑,topologyKey 存在一些限制:
requiredDuringSchedulingIgnoredDuringExecution
模式,topologyKey 不能为空requiredDuringSchedulingIgnoredDuringExecution
模式下,LimitPodHardAntiAffinityTopology
权限控制器会限制 topologyKey 只能设置为 kubernetes.io/hostname
。固然若是你想要使用自定义 topology,那能够简单禁用便可。preferredDuringSchedulingIgnoredDuringExecution
模式下,topologyKey 为空则表示全部的拓扑域。截止 v1.12 版本,全部的拓扑域还只能是 kubernetes.io/hostname
, failure-domain.beta.kubernetes.io/zone
和 failure-domain.beta.kubernetes.io/region
的组合。来个官方示例,有三节点集群,须要分别部署 3 份 web 和 redis 服务。但愿 web 与 redis 服务共存,但须要保证各个服务的副本分散部署。
先建立 redis 集群:
apiVersion: apps/v1 kind: Deployment metadata: name: redis-cache spec: selector: matchLabels: app: store replicas: 3 template: metadata: labels: app: store spec: affinity: // pod 反亲和性, 打散 redis 各个副本 podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: "kubernetes.io/hostname" containers: - name: redis-server image: redis:3.2-alpine
再部署 web 服务,须要打散而且与 redis 服务共存,配置以下:
apiVersion: apps/v1 kind: Deployment metadata: name: web-server spec: selector: matchLabels: app: web-store replicas: 3 template: metadata: labels: app: web-store spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web-store topologyKey: "kubernetes.io/hostname" podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: "kubernetes.io/hostname" containers: - name: web-app image: nginx:1.12-alpine
注意1: pod affinity 须要进行大量处理,因此会明显减慢大型集群的调度时间,不建议在大于几百个节点的集群中使用该功能。
注意2: pod antiAffinity 要求对节点进行一致标志,即集群中的全部节点都必须具备适当的标签用于配置给 topologyKey,若是节点缺乏指定的 topologyKey 指定的标签,则可能会致使意外行为。