Comment on page
亲和性和反亲和性
通过 Kubernetes 你可以将一个 pod 限制或倾向于在某些特定节点运行。有几种方式可以达 到这个目的,它们都通过 label selectors 进行选择。通常情况下,这样的约束是不必要的,因为调度程序会自动进行合理的调度(如通过一系列的评分机制将 pods 合理分配到最优节点上,而不会将 pod 分配在没有足够资源的节点上等)。但是在某些情况下,可能需要更多的策略控制,例如,将 pod 调度到 SSD 的计算节点上,或者将两个通信比较频繁的不同服务 pod 调度到同一个可用域。
labels
在 K8s 中是一个很重要的概念,作为一个标识,Service、Deployments 和 Pods 之间的关联都是通过 label
来实现的。而每个节点也都拥有 label
,通过设置 label
相关的策略可以使得 pods 关联到对应 label
的节点上。nodeSelector
是最简单的约束方式。nodeSelector
是 PodSpec 的一个字段。通过
--show-labels
可以查看当前 nodes 的 labels
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
minikube Ready <none> 1m v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/
hostname=minikube
如果没有额外添加 nodes
labels
,那么看到的如上所示的默认标签。我们可以通过 kubectl label node
命令给指定 node 添加 labels
:$ kubectl label node minikube disktype=ssd
node/minikube labeled
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
minikube Ready <none> 5m v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=minikube
当然,你也可以通过kubectl label node
删除指定的labels
(标签 key 接-
号即可)$ kubectl label node minikube disktype-node/minikube labeled$ kubectl get node --show-labelsNAME STATUS ROLES AGE VERSION LABELSminikube Ready <none> 23m v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=minikube
创建测试 pod 并指定
nodeSelector
选项绑定节点:$ cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
$ kubectl create -f nginx.yaml
pod/nginx created
查看 pod 调度的节点,即我们指定有
disktype=ssd
label 的 minikube 节点:$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 1m 172.18.0.4 minikube
nodeSelector
可以很方便的解决以上比较简单的需求,但是它还不够灵活。比如我想以机架为单位,部署的服务可以很好的分散在不同机架的服务器上,此时 nodeSelector
就并不是那么管用了。因此,Kubernetes 引入了亲和性和反亲和性概念。affinity/anti-affinity 特性还处 于 beta 状态,相比
nodeSelector
来说有几点优势:- 1、提供更多的表示式(不仅仅是 and 匹配)
- 2、可以指定 “软” 规则限制而不仅仅是硬限制,因此即使调度器不能满足它的规则,也可以正常调度 pod
- 3、可以针对 node 上运行的 pods 指定
labels
,而不仅仅局限于 node 本身
affinity 特性拥有两种类型,一种是 node affinity,一种是 pod affinity/anti-affinity。node affinity 类似
nodeSelector
,但同时拥有上文提到的 1、2 两点优势,pod affinity/anti-affinity 针对 pods 指定 labels
,同时拥有以上三点优势。K8s 在 1.2 的时候以 alpha 的特性引入 node affinity。node affinity 通过 node
labels
约束 pod 调度节点。Node affinity 有两种类型:requiredDuringSchedulingIgnoredDuringExecution
(硬限制,同nodeSelector
)preferredDuringSchedulingIgnoredDuringExecution
(软限制)
看以下例子:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- 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
以上规则表达的意思是,该 Pod 只能被调度到拥有
kubernetes.io/e2e-az-name=e2e-az1
或者 kubernetes.io/e2e-az-name=e2e-az2
标签的节点上,其中在满足之前标签条件的同时更倾向于调度在拥有 another-node-label-key=another-node-label-value
标签的节点上。新的 node affinity 支持
In
、NotIn
、Exists
、DoesNotExist
、Gt
、Lt
操作符。可以使用 NotIn
、DoesNotExist
来实现反亲和性,也可以通过 node taints 来实现。如果同时指定
nodeSelector
和 nodeAffinity
,两者同时满足才会被调度。如果 nodeAffinity
中指定了多个 nodeSelectorTerms
,只要满足其中一个 nodeSelectorTerms
匹配条件即可调度。如果 nodeSelectorTerms
中有多个 matchExpressions
,那么自由满足所有的条件才会被调度。pod 亲和性和反亲和性在 K8s 1.4 版本引入,它基于运行在 node 上的 pod 标签来限制 pod 调度在哪个节点上,而不是节点的标签。
pod 亲和性和反亲和性需要大量的计算,会显著降低集群的调度速度,不建议在大于几百个节点的集群中使用。
pod 反亲和性要求集群中的所有节点必须具有
topologyKey
匹配的标签,否则可能会导致意外情况发生。同 node affinity,pod 亲和性和反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
(硬限制)preferredDuringSchedulingIgnoredDuringExecution
(软限制)
不同的是,pod 通过
podAntiAffinity
设置反亲和性,如下例子:apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0
以上示例表示,pod 必须调度在至少运行一个
security=S1
标签的 pod 的节点上(更准确的说,这个 pod 可以运行在节点 N 上,如果该节点有标签 key 为 failure-domain.beta.kubernetes.io/zone
,而且运行着标签为 security=S1
的实例)。另外,反亲和规则表明最好不要调度到运行有 security=S2
标签的 pod 的节点上(更准确的说,如果这个节点拥有标签 key 为 failure-domain.beta.kubernetes.io/zone
,但运行有 security=S2
标签的 pod,那么这个节点就不会被优先选择调度)。podAffinity
和 podAntiAffinity
支持 In
、NotIn
、Exists
、DoesNotExist
四种表达式。原则上,
topologyKey
可以为任何合法的键值对。但是因为性能和安全的原因,有以下限制:- 1、针对
podAffinity
和podAntiAffinity
中的requiredDuringSchedulingIgnoredDuringExecution
,topologyKey
为空是不允许的 - 2、针对
podAntiAffinity
中的requiredDuringSchedulingIgnoredDuringExecution
,准入控制器选项LimitPodHardAntiAffinityTopology
可以把topologyKey
限制为kubernetes.io/hostname
,如果想自定义值,可以修改准入控制器或者直接禁用 - 3、针对
podAntiAffinity
中的preferredDuringSchedulingIgnoredDuringExecution
,topologyKey
为空,则代表所有拓扑(仅限于kubernetes.io/hostname
,failure-domain.beta.kubernetes.io/zone
andfailure-domain.beta.kubernetes.io/region
) - 4、除了以上情况,
topologyKey
可以为任意合法的键值对
除了
labelSelector
和 topologyKey
,还可以指定 namespace 的 labelSelector
作为匹配。labelSelector
和 topologyKey
属于同一级别,如果未定义或设置为空值,那么默认为定义 pod affinity 和 anti-affinity 所在的空间。在一个三个节点的集群中,一个 web 应用程序依赖内存存储,如 redis,我们想 web 程序尽可能的和缓存调度在同一个节点上。redis 的 Deployment 配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
redis-cache 规则表达 pod 不被调度在同一个节点上。web-server 规则设置如下,表达 pod 不被调度在同一个节点上,并且必须调度在运行标签
app=store
pod 的节点上。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
最近更新 4yr ago