内容简介:Kubernetes中的高级调度策略
在大多数业务场景下Kubernetes的默认调度策略都工作得很好,如 :在将一个Pod调度到节点之前,会首选确保改节点有足够的资源(如内存),并且它会试图平衡所有节点资源的使用率。
但是有时候我们会试图控制Pod的调度方式,比如:我们希望一些pod只会被调度到特定硬件的节点上,或者我们希望将通信比较频繁的service(被这些service选择到的pod)被调度到一个节点或者一个机房到节点上,又或者我们希望能指定一组节点提供给特定的用户的业务来使用。总结起来,也就是说我们希望我们能更清楚的知道应用在Kubernetes集群上到底是如何被调度或者部署的。所以从Kubernetes1.6开始,给我们提供了四种高级调度策略: node affinity/anti-affinity, taints and tolerations, pod affinity/anti-affinity, custom schedulers
,这些特性在Kubernetes1.6中处于beta阶段。
Node Affinity/Anti-Affinity
Node Affinity/Anti-Affinity
提供了一种方法,给一些节点设置一些规则,以确保这些节点被调度选中。这个特定是 nodeSelector
的一种泛化。给节点设置规则和给节点设置标签的方法是一模一样的,并且需要在pod中设置相应的选择器(类似 nodeSelector
),选择器的匹配规则可以是 required
或者 preferred
。
required
规则必须给严格匹配,才能将pod调度到该节点。如果没有被匹配(这里也会考虑到pod资源的使用量问题),则不会调度。在 nodeAddinity
中使用 requiredDuringSchedulingIgnoredDuringExecution
来定义 requeired
规则。
如,加入需要将pod调度到指定的节点上,在 pod Spec
中定义:
apiVersion: apps/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "region" operator: In values: ["aws"]
IgnoredDuringExecution
表示如果该节点的标签被改变了,pod还是可以继续运行,并不会重新调度到其他节点去。未来会有一个新特性 requiredDuringSchedulingRequiredDuringExecution
,表示一旦节点到标签被修改,即pod的 node affinity无法再匹配的时候,pod会马上被驱逐出该节点,重新调度到匹配到节点上(如果有匹配到的节点的)。
preferred
规则表示如果有可以匹配到的节点,则会优先将pod调度到该节点上,如果没有人和匹配到的节点,则会随机选择一个节点进行调度。
apiVersion: apps/v1beta1 kind: Deployment metadata: name: nginx-deployment annotations: scheduler.alpha.kubernetes.io/affinity: > { "nodeAffinity": { "preferredDuringSchedulingIgnoredDuringExecution": [ { "weight" : 10, "preference": { "nodeSelectorTerms": [ { "matchExpressions": [ { "key": "region", "operator": "In", "values": ["aws"] } ] } ] } } ] } } spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
Node anti-affinity
还可以执行取反操作,如:
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "region" operator: NotIn values: ["aws"]
可用的运算操作符有: In
, NotIn
, Exists
, DoesNotExist
. Gt
, 和 Lt
。
Taints and Tolerations
taints and tolerations
允许将某个节点做标记,以使得所有的pod都不会被调度到该节点上。但是如果某个pod明确制定了 tolerates
, 则可以正常调度到被标记的节点上。如,一般情况下我闷可以将master节点标记位不可以调度;或者将某些节点制定给特定的一组用户使用。
kubectl
可以标记节点:
kubectl taint nodes node01 key=value:NoSchedule
所有的pod都不会调度到该节点上,除非pod里面制定了 toleration
,这里指定的 effect
为 NoSchedule
,其他可用的 effect
有: PreferNoSchedule
是 NoSchedule
的 Prefer
版本, NoExecute
表示如果节点被 taint
,则运行在该节点上的所有pod的都会被驱逐出去,除非pod指定了相应的 toerate
。可以在 PodSpec
中指定 tolerations
:
tolerations: - key: "key" operator: "Equal" value: "value" effect: "NoSchedule"
这个特性在kubernetes1.6处在 beta
阶段,在1.6版本中还有一个 alpha
的特性,使用 taints
和 tolerations
指定pod在节点出现问题之后还可以绑定在该节点多长时间:
tolerations: - key: "node.alpha.kubernetes.io/unreachable" operator: "Exists" effect: "NoExecute" tolerationSeconds: 6000
(如果来匹配not ready的节点,则将key修改为 node.alpha.kubernetes.io/notReady
即可).
Pod Affinity/Anti-Affinity
节点的 affinity/anti-affinity
允许pod基于标签选择调度到哪个节点上。但是,有时候我们希望可以通过已定的规则确定pod与pod之间的亲密关系,如:我们有一些 front-ends pod在service s1中,这些pod需要和后段的pod进行频繁的通信,后端的pod运行在另外一个service s2中。这个时候我们就希望两个service的pod会被调度到同一个region的节点上,但是我们要是手动指定了调度的节点region,当region的网络出问题的时候,这些service将不能正常运行,此时我们希望这些pod能被重新调度到其他region的节点上。我们可以定义一些pod之间亲和性的规则来实现这个目的,假如我们给s1的pod设置标签为 service=s1
,为s2的pod设置标签为 service=s2
:
affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: service operator: In values: [“s1”] topologyKey: failure-domain.beta.kubernetes.io/zone
同样也支持 preferredDuringSchedulingIgnoredDuringExecution
。
我们也可以只一些标签规则,使两个service的pod不会调度到同一个节点上:
affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: service operator: In values: [“s1”] topologyKey: kubernetes.io/hostname
Custom Schedulers
如果kubernetes提供的多种调度策略均满足不了我们的业务需求,我们可以自定义调度器,实现pod的调度功能。每个pod正常情况下都会被默认的调度器调度,但是如果我们在pod的定义文件中有指定调度器名称(名称为自定义调度器名称),默认的调度器会忽略该pod,此时就允许我们自定义的调度器调度该pod,只需要在pod中定义 schedulerName
即可:
apiVersion: v1 kind: Pod metadata: name: nginx labels: app: nginx spec: schedulerName: my-scheduler #指定调度器名称 containers: - name: nginx image: nginx:1.10
下面是一个经过测试的 Python 自定义调度器:
#!/usr/bin/env python import requests import time import json SCHEDULER_NAME = "my-scheduler" API_SERVER = "http://127.0.0.1:8080" API_URL = { "pods": "/api/v1/pods", "nodes": "/api/v1/nodes", "binding": "/api/v1/namespaces/{0}/pods/{1}/binding" } def get_pods(url): pods = requests.get(url) pods_list = list() if pods.status_code == 200: pods_list = [{"name": x["metadata"]["name"], "namespace":x["metadata"]["namespace"]} for x in pods.json()["items"] if x["status"][ "phase"] == "Pending" and x["spec"]["schedulerName"] == SCHEDULER_NAME] return pods_list def get_nodes(url): nodes = requests.get(url) nodes_list = list() if nodes.status_code == 200: nodes_list = [x["metadata"]["name"] for x in nodes.json()["items"]] return nodes_list def chose_node(nodes): '''scheduler''' chosen = None for node in nodes: if node.endswith("179"): #测试用,只是选择了ip以179结尾的节点 chosen = node break return chosen def main(): pods_list = get_pods(API_SERVER + API_URL["pods"]) nodes_list = get_nodes(API_SERVER + API_URL["nodes"]) if pods_list == []: print "There is not pod need to be scheduled." return True for pod in pods_list: chosen = chose_node(nodes_list) if chosen == None: print "There is no node be chosen." return True data = {"apiVersion": "v1", "kind": "Binding", "metadata": {"name": pod["name"]}, "target": {"apiVersion": "v1", "kind": "Node", "name": chosen} } bind = API_SERVER + \ API_URL["binding"].format(pod["namespace"], pod["name"]) headers = {"Content-type": "application/json", "Accept": "application/json"} r = requests.post(bind, data=json.dumps(data), headers=headers) if r.status_code == 201: print "Assigned {0} to {1}.".format(pod["name"], chosen) else: print r.text time.sleep(3) if __name__ == "__main__": while True: time.sleep(1) main()
下边这个是官方提供的 shell 版调度器:
#!/bin/bash SERVER='localhost:8001' while true; do for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"') ; do NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"')) NUMNODES=${#NODES[@]} CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]} curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind" : "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/ echo "Assigned $PODNAME to $CHOSEN" done sleep 1 done
总结
在绝大多数情况下,kubernetes自带的默认调度器和一些高级的调度策略已经可以满足我们的需求了,但是同时kubernetes也提供了自定义调度器的功能,这使得我们可以实现一些符合特定业务需求的、经过特殊优化的调度器,kubernetes无论是直接使用,还是定制二次开发都提供了良好的支持或者接口。
以上所述就是小编给大家介绍的《Kubernetes中的高级调度策略》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- YARN资源调度策略
- Dubbo线程模型和调度策略
- Kafka 源码解析:延时任务调度策略
- libgo 源码剖析(2. libgo调度策略源码实现)
- 基于 Netty 的帧调度策略,自行实现流量控制及可靠性通信
- 影响K8S Pod分配和调度策略的两大关键特性
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。