Kubernetes 1.6新特性系列 | 高级调度

作者注:这是深入Kubernetes 1.6特性系列的第四篇。

导读:
Kubernetes 1.6高级调度的新特性主要集中在四个方面:
Node的亲和性和反亲和性(Affinity/Anti-Affinity)
Node的污点和容忍(Taints and Tolerations)
Pod的亲和性和反亲和性(Affinity/Anti-Affinity)
自定义调度器

1、简介

Kubernetes的调度器在大多数情况下能过运行的很好,例如:它能够将Pod调度到有充足资源的Node上;它能够将一组Pod(ReplicaSet, StatefulSet,等)均匀的调度到不同的Node上;它尽力平衡各个节点的资源使用率等。

但有些时候您会想控制您的Pod如何调度,例如:可能您想让一些Pod被确定调度到某些使用特殊硬件的节点上,或者您想让交互频繁的服务一起调度,或者您想让一些Node只给特定的一些用户提供服务等等。最终,您对于应用程序调度和部署的需求永远要比Kubernetes提供的多。因此,Kubernetes 1.6提供了四个高级高级调度功能:节点亲和性和反亲和性,污点和容忍,Pod亲和性/反亲和性和自定义调度。这四个特性在Kubernetes 1.6版本中都是beta版。

2、Node亲和性/反亲和性

Node亲和性/反亲和性是在Node上设置如何被Scheduler选择的规则一种方式。此功能是自Kubernetes 1.0版本以来在Kubernetes中的nodeSelector的功能的通用化。规则是使用在Pod中指定和选择器上自定义的标签等用户熟悉的概念定义的,并且他们是必需的或者首选的,这取决于您希望调度程序强制执行他们的严格程度。

A必需的规则(Required)

只有满足必需的规则的Pod才会被调度到特定的Node上。如果没有Node匹配条件(加上所有其他所有正常的条件,例如为Pod请求提供足够的可用资源),否则Pod不会被调度。必需满足的规则在nodeAffinity的requiredDuringSchedulingIgnoredDuringExecution字段中指定。

例如,如果我们要求在多可用区域(Multiple Zones)的us-central1-a(GCE)区域中的节点上进行调度,则可以将以下的关联规则指定为Pod规范(Spec)的一部分:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: "failure-domain.beta.kubernetes.io/zone"
            operator: In
            values: ["us-central1-a"]

“IgnoredDuringExecution”意味着如果Node上的标签发生更改,并且亲和性的规则不再满足时选择忽略。这个在未来会计划实现。
“requiredDuringSchedulingRequiredDuringExecution”意味着一旦他们不满足节点亲和性规则,将从Node上驱逐不再匹配规则的Pod。

B首选的规则(Preferred)

首选规则意味着如果节点与规则匹配,则将优先选择它们,并且仅当没有优选节点可用时才选择非优选节点。您可以选择使用首选规则,而不是通过必需规则强制将Pod部署到GCE的us-central1-a区域中的节点上。使用首选规则,则需指定preferredDuringSchedulingIgnoredDuringExecution:

affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: "failure-domain.beta.kubernetes.io/zone"
            operator: In
            values: ["us-central1-a"]

Node的反亲和性能够使用负操作符(NotIn, DoesNotExist等)来表示。下面的例子说明了如何禁止您的Pod被调度到us-central1-a的区域中:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: "failur-domain.beta.kubernetes.io/zone"
          operator: NotIn
          values: ["us-central1-a"]

可以使用的操作符有:In, NotIn, Exists, DoesNotExist, Gt, 和Lt。

这个特性还有一些另外的使用场景,比如需要在调度上严格区别Node上的硬件架构,操作系统版本,或者专用的硬件等。

Node的亲和性和反亲和性在Kubernetes 1.6版本中是Beta版。

3、污点和容忍(Taints and Tolerations)

此功能允许您标记一个Node(“受污染”,“有污点”),以便没有Pod可以被调度到此节点上,除非Pod明确地“容忍”污点。标记的是Node而不是Pod(如节点的亲和性和反亲和性),对于集群中大多数Pod应该避免调度到特定的节点上的功能特别有用,例如,您可能希望主节点(Master)标记为仅可调度Kubernetes系统组件,或将一组节点专用于特定的用户组,或者让常规的Pod远离具有特殊硬件的Node,以便为有特殊硬件需求的Pod留出空间。

使用kubectl命令可以设置节点的“污点”,例如:

kubectl taint nodes node1 key=value:NoSchedule

创建一个污点并标记到Node,那些没有设置容忍的Pod(通过key-value方式设置NoSchedule,这是其中一个选项)不能调度到该Node上。其他污点的选项是PerferredNoSchedule,这是NoSchedule首选版本;还有NoExecute,这个选项意味着在当Node被标记有污点时,该Node上运行的任何没有设置容忍的Pod都将被驱逐。容忍将被添加到PodSpec中,看起来像这样:

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

除了将污点和容忍(Taints and Tolerations)特性在Kubernetes 1.6中移至Beta版外,我们还引入了一个使用污点和容忍的Alpha的特性:允许用户自定义一个Pod被绑定到Node上后遇到了诸如网络分区的问题时的行为(可能Pod希望长时间允许在这个Node上,或者网络分区会很快恢复),而不是现在默认的等待五分钟超时。更详细的信息,请参阅文档。

4、Pod的亲和性和反亲和性

Node的亲和性和反亲和性允许您基于节点的标签来限制Pod的调度行为。但是,如果您想要指定Pod的亲和关系时这种方法就无法奏效了。例如,如何实现在一个服务或者相关的服务中聚合Pod或将Pod均匀分布?为此,您可以使用Pod的亲和性和反亲和性特性,它在Kubernetes 1.6中也是Beta版。

我们来看一个例子。假设您在服务S1中有个前端应用,它经常与服务S2的后端应用进行通信。因此,您希望这两个服务共同位于同一个云提供商的区域中,但您不需要手动选择区域(如果有些区域暂时不可用),希望将Pod重新调度到另一个(单个的)区域。您可以像这样指定Pod的亲和性和反亲和性规则(假设您将该服务的Pod命名为*”service=S2″,另外一个服务的Pod为”service=S1″):

affinity:
  podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
      matchExpressions:
      - key: service
        operator: In
        values: [“S1”]
    topologyKey: failure-domain.beta.kubernetes.io/zone

如同Node的亲和性和反亲和性,也有一个preferredDuringSchedulingIgnoredDuringExecution变量。

Pod的亲和性和反亲和性非常灵活。想象一下,您已经分析了您的服务的性能,发现来自服务S1的容器会干扰运行在同一Node上的服务S2的容器,这可能是由于缓存干扰效应或者网络连接饱和引起,或者也许是由于安全性问题,您不会想要S1和S2的容器共享一个Node。要实现这些规则,只需对上述代码段进行两次更改即可将podAffinity更改为podAntiAffinity”,并将topologyKey更改为kubernetes.io/hostname*即可。

5、自定义调度器

如果Kubernetes Scheduler的这些特性没能足够满足您控制负载的需求,您可以在将任意子集的Pod的调度任务交给您自己的自定义调度器,自定义的调度器可以跟默认的调度器(Kubernetes Scheduler)一起运行,或者替代默认的调度器。多调度器(Multiple schedulers)在Kubernetes 1.6中是Beta版。

在默认的情况下,每个新的Pod都使用默认的调度器进行调度。但是如果您提供了自定义调度器的名称,默认的调度器会忽略Pod的调度请求,允许您的自定义调度器对Pod进行调度。看看下面的例子:
在Pod的规范(Spec)中制定调度器的名字:

apiVersion: v1
kind: Pod
metadata:  
  name: nginx    
  labels:    
    app: nginx
spec:
  schedulerName: my-scheduler
  containers:
  - name: nginx
    image: nginx:1.10

如果我们在没有部署自定义调度器的情况下创建了这个Pod,结果会是默认的调度器将忽略这个Pod,它将处于Pending状态。因此,我们需要一个自定义调度程序来查找和调度其schedulerName字段为my-scheduler的Pod。

自定义调度语言可以用任何语言编写,调度策略根据需要可以简单也可以复杂。这是一个非常简单的例子,它使用Bash编写,它可以为Pod随机分配一个Node。请注意,您需要让它与kubectl proxy一起运行:

#!/bin/bash
SERVER='localhost:8001' # Proxy address for apiServer
while true;
do
    # Get all pods, and pod's properties
    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
        # Get all nodes
        NODES=($(kubectl --server $SERVER get nodes -o json 
                | jq '.items[].metadata.name' 
                | tr -d '"'))
        NUMNODES=${#NODES[@]}
        
        # Choose a node randomly
        CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
        
        # Bind a pod($PODNAME) to a node($CHOSEN)
        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 1.6的release notes关于这个特性有更多的介绍,包括您已经使用了这些(其中一个或者多个)特性的Alpha版本改如何进行配置的细节(这些事必需的,因为对于这些特性,从Alpha到Beta的转变是突破性的)。

6、致谢

这里描述的功能,包括Alpha和Beta版本,都是真正的社区努力,涉及Google,华为,IBM,Red Hat等的工程师。

7、参与其中

参与每周的 community meeting:

·                    在Stack Overflow上发布问题

·                    在Twitter @Kubernetesio 上关注最新的更新for latest updates

·                    在 Slack (room#sig-scheduling)上关注社区

非常感谢你的贡献。

–Ian Lewis, Developer Advocate, and David Oppenheimer, Software Engineer,Google

翻译于  http://blog.kubernetes.io/2017/03/advanced-scheduling-in-kubernetes.html

8、加入容器时代

容器时代公众号的目的是希望能够传播容器技术和理念,让更多的人能够享受到这场技术革命带来的好处。虽然我们不是大牛,对容器技术生态的了解和认识也还不够深刻,但是我们乐于分享,乐于交流。不管读者的你是学生,还是工程师,甚至都不是,我们都欢迎你加入进来,可以一起写文章、翻译文章,也可以一起分享经验、聊聊踩过的那些坑!为此特意建立了一个微信群,长按识别下方二维码加入,我们以梦为马,一起前行!

2017042401

(微信群已满,请添加k8stimes或者扫下面二维码添加好友,小助手会将您拉入群聊)

 

K8S中文社区微信公众号

评论 抢沙发

登录后评论

立即登录