Pod的调度在默认情况下是Scheduler Controller 采用默认算法的全自动调度,在实际使用中并不满足我们的需求,因为并不能事先掌握pod被调度到哪个Node之上,所以kubernetes又提供了好几种方式让我们自已选择调度到什么Node中,比如有NodeSelector(定向调度)、NodeAffinity(Node亲和性)、PodAffinity(Pod亲和性)。
NodeSelector调度算法比较简单,NodeSelector 只调度到某个拥有特定标签的Node上,如果没有满足条件的Node,那么此Pod将不会被运行,即使在集群中还有可用Node列表,这就限制了它的使用场景,现在基本上被NodeAffinity取代了,NodeAffinity在NodeSelector的基础之上的进行了扩展,使调度更加灵活,除了有一个必须要满足条件的Node之外,还要可设置优先条件,下面来看下NodeAffinity的使用方式:
一、NodeAffinity
NodeAffinity 亲和性有两种表达方式:
◎ RequiredDuringSchedulingIgnoredDuringExecution :必须满足指定的规则才可以调度Pod到Node上,相当于硬限制。
◎ PreferredDuringSchedulingIgnoredDuringExecution:强调优先满足指定的规则,相当于软限制,并不强求,如果多个优先级规则,还可以设置权重,以定义执行顺序。
IgnoredDuringExecution 表示 ,如果一个pod所在的节点 在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略Node上Label的变化,该pod继续在该节点上运行。
下面来看一些示例:
apiVersion: v1 kind: Pod metadata: name: ubuntu-aff labels: os: centos spec: affinity: #亲和性设置 nodeAffinity: #设置node亲和性 requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: #设置node拥有的标签 - key: disktype operator: In values: - hdd #表示一定会调度在拥有此标签(disktype=hdd)的Node上。 如果将requiredDuringSchedulingIgnoredDuringExecution改为 preferredDuringSchedulingIgnoredDuringExecution 那么就不一定会调度到有disktype=hdd标签的Node,它会看情况,如果有这个标签的Node负载已经是很高的了,那么就会调度到负载较低的Node上
从上面的配置中可以看到,它操作符operator为In,NodeAffinity语法支持的操作符有In、NotIn、Exists、
DoesNotExist、Gt、Lt虽然没有节点排斥功能,但是用NotIn和DoesNotExist就可以实现排斥功能了。
NodeAffinity规则设置的注意事项如下:
◎ 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能最终运行在指定的
Node上。
◎ 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
nodeSelectorTerms 属性是设置要调度到的node的标签的,因此nodeSelectorTerms 如果有多个说明是可以调度到不同的标签,即不同的Node之上,所以只需要匹配一个就行了。
◎ 如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的
matchExpressions 才能匹配成功。
matchExpressions 是在nodeSelectorTerms 下,设置具体的一个Node标签,设置有多个
nodeSelectorTerms ,意味着匹配的Node需要有多个标签,且多个标签必须同时要有。
上面的例子中,node亲和性条件只有一个,可以设置多个条件:
apiVersion: v1 kind: Pod metadata: name: ubuntu-aff labels: os: centos spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: #第一条件 nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/arch operator: In values: - amd64 preferredDuringSchedulingIgnoredDuringExecution: #第二个条件 - weight: 1 preference: matchExpressions: - key: disktype operator: In values: - hdd containers: - name: ubuntu-aff image: 10.3.1.15:5000/ubuntu:16.04 env: #在容器内配置环境变量 - name: Test value: "7890" command: ["bash","-c","while true;do date;sleep 1;done"]
如上,创建pod时,节点亲和性有两个,也就是说如果某个Node运行这个Pod,那么这个Node需要满足的条件有两个,
第一个条件是 属性为 requiredDuringSchedulingIgnoredDuringExecution ,此为硬性条件,必须要满足的:beta.kubernetes.io/arch=amd64
第二个条件属性为 preferredDuringSchedulingIgnoredDuringExecution ,此为软性条件,即优先满足的条件,尽可能会满足,如果无法满足就退而求其次,运行在其它Node之上。
那么调度时该如何选择调度到哪个Node上?
为什么要设置两个条件呢,因为满足第一个条件的Node有很多,即拥有beta.kubernetes.io/arch=amd64的标签的
Node有很多,这里第二个条件是说 要尽量运行在 disktype=hdd的Node上。
总的来说,即 只运行在arch=amd64的node上,在此条件之下优先运行在disktype=hdd的Node上。
二、PodAffinity: Pod亲和性与互斥性调度
根据节点上正在运行的pod的标签来调度,而非node的标签,要求对节点和Pod两个条件进行匹配,其规则为:如果在具有标签X的Node上运行了一个或多个符合条件Y的Pod,那么Pod应该运行在此Node上,如果是互斥,则拒绝运行在此Node上。 也就是说根据某个已存在的pod,来决定要不要和此pod在一个Node上,在一起就需要设置亲和性,不和它在一起就设置互斥性。X指的是一个集群中的节点、机架、、区域等概念,通过Kubernetes内置节点标签中的key来进行声明,这个key的名字为topologyKey,用来表达节点所属的拓朴结构之下。
pod的亲和性表达方式与Node亲和性是一样的表达方式。
kubernetes内置标签:
○ kubernetes.io/hostname
○ failure-domain.beta.kubernetes.io/zone
○ failure-domain.beta.kubernetes.io/region
○ beta.kubernetes.io/instance-type
○ beta.kubernetes.io/os
○ beta.kubernetes.io/arch
1、创建参照Pod
#pod affinity的参照Pod apiVersion: v1 kind: Pod metadata: name: pod-flag labels: # 定义多个标签,以便其它pod设置亲和与互斥 app: nginx security: s1 spec: containers: - name: nginx-1-10 image: 10.3.1.15:5000/nginx:1.10 ports: - containerPort: 80 #查看调度到哪个Node之上: root@ubuntu:# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE pod-flag 1/1 Running 0 4m 192.168.150.232 10.3.1.16 #运行在Node 10.3.1.16上
2、创建一个pod,与参照的pod必须要在同一个node上,即,具有亲和性。
apiVersion: v1 kind: Pod metadata: name: with-pod-affinity labels: os: ubuntu spec: affinity: #亲和性设置 podAffinity: #设置pod亲和性 requiredDuringSchedulingIgnoredDuringExecution: #必须要满足的条件 - labelSelector: #与哪个pod有亲和性,在此设置此pod具有的标签 matchExpressions: #要匹配的pod的,标签定义,如果定义了多个matchExpressions,则所有标签必须同时满足。 - key: security #标签的key operator: In #操作符 values: - s1 #标签的值,即拥有label security=s1 topologyKey: kubernetes.io/hostname #节点所属拓朴域 #上面表达的意思是此处创建的Pod必须要与拥有标签security=s1的pod在同一Node上. containers: - name: wish-pod-affinity image: 10.3.1.15:5000/ubuntu:16.04 env: - name: Test value: "7890" command: ["bash","-c","while true;do date;sleep 1;done"] # 因为pod是属于某个命名空间的,所以设置符合条件的目标Pod时,还可以指定哪个命名空间或全部命名空间里的Pod, # namespace的定义与labelSelector同级,如果不指定命名空间,则与此处创建的pod在一个namespace之中
root@ubuntu:# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE with-pod-affinity 1/1 Running 0 11s 192.168.150.252 10.3.1.16 pod-flag 1/1 Running 0 1h 192.168.150.232 10.3.1.16 #的确是在同一个Node上。 #如果在创建时,pod状态一直处于Pending状态,很有可能是因为找不到满足条件的Node。
3、创建一个pod,与参照的pod,一定不能在同一个Node上,即具有互斥性。
apiVersion: v1 kind: Pod metadata: name: with-pod-antiffinity labels: os: ubuntu spec: affinity: #亲和性设置 podAntiAffinity: #设置pod反亲和性 requiredDuringSchedulingIgnoredDuringExecution: #必须要满足的条件 - labelSelector: #与哪个pod有反亲和性,互斥性,在此设置此pod具有的标签 matchExpressions: - key: app #标签的key operator: In values: - nginx #标签的值,即拥有label app=nginx topologyKey: kubernetes.io/hostname #节点所属拓朴域 #上面表达的意思是此处创建的Pod必须要与拥有标签app=nginx的pod不在在同一个Node上.如果所有 Node将无法满足条件,则创建的pod一直处于Pending状态。 containers: - name: wish-pod-antiaffinity image: 10.3.1.15:5000/ubuntu:16.04 env: - name: Test value: "7890" command: ["bash","-c","while true;do date;sleep 1;done"]
root@ubuntu15:/data/yaml# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE pod-flag 1/1 Running 0 1h 192.168.150.232 10.3.1.16 with-pod-affinity 1/1 Running 0 1h 192.168.150.252 10.3.1.16 with-pod-antiffinity 1/1 Running 0 1m 192.168.77.204 10.3.1.17 #可以看到与参照pod不在同一个node之上。
更多的的Pod亲和性调度信息可以参考官方文档。