Kubernetes中的高级调度策略

栏目: 服务器 · Nginx · 发布时间: 6年前

内容简介: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 ,这里指定的 effectNoSchedule ,其他可用的 effect 有: PreferNoScheduleNoSchedulePrefer 版本, NoExecute 表示如果节点被 taint ,则运行在该节点上的所有pod的都会被驱逐出去,除非pod指定了相应的 toerate 。可以在 PodSpec 中指定 tolerations

tolerations: 
- key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"

这个特性在kubernetes1.6处在 beta 阶段,在1.6版本中还有一个 alpha 的特性,使用 taintstolerations 指定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中的高级调度策略》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创业36条军规

创业36条军规

孙陶然 / 中信出版社 / 2011-12 / 39.00元

《创业36军规》的作者孙陶然是一位数次成功创业的创业者,书中的内容有关创业的方方面面,从创业目的到股东选择,从经营到管理,从找方向到项目细节不一而足,写给每位心怀创业理想或正在创业路上的读者。 很多教人成才的书,作者未必成才;很多教人炒股的书,作者并不炒股;很多教人创业的书,作者不曾成功创业。一起来看看 《创业36条军规》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码