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