<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>scheduler | 伪架构师</title>
    <link>/tags/scheduler/</link>
      <atom:link href="/tags/scheduler/index.xml" rel="self" type="application/rss+xml" />
    <description>scheduler</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Sat, 06 May 2017 09:33:47 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>scheduler</title>
      <link>/tags/scheduler/</link>
    </image>
    
    <item>
      <title>Kubernetes 的高级调度</title>
      <link>/post/adv-scheduler-in-k8s/</link>
      <pubDate>Sat, 06 May 2017 09:33:47 +0800</pubDate>
      <guid>/post/adv-scheduler-in-k8s/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://kubernetes.io/blog/2017/03/advanced-scheduling-in-kubernetes&#34; target=&#34;_blank&#34;&gt;Advanced Scheduling in Kubernetes
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kubernetes 的调度器能够满足绝大多数要求，例如保证 Pod 只在资源足够的节点上运行，会尝试把同一个集合的 Pod 分散在不同的节点上，还会尝试平衡不同节点的资源使用率等。&lt;/p&gt;

&lt;p&gt;不过有时候你希望控制 Pod 的调度。例如你希望确认某个 Pod 只运行在有特定硬件的节点上；或者想要让频繁互相通信的服务能就近部署；又或者你希望用独立的节点给部分用户提供服务。而且最终，用户总是比 Kubernetes 更了解自己的应用。&lt;/p&gt;

&lt;p&gt;所以 Kubernetes 1.6 提供了四个高级调度功能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;节点亲和/互斥&lt;/li&gt;
&lt;li&gt;Taint（污染、变质） 和 Tolerations（容忍、耐受）&lt;/li&gt;
&lt;li&gt;Pod 的亲和/互斥&lt;/li&gt;
&lt;li&gt;以及自定义调度&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上述功能在 Kubernetes 1.6 中还属于 Beta 阶段。&lt;/p&gt;

&lt;h2 id=&#34;节点亲和-互斥&#34;&gt;节点亲和/互斥&lt;/h2&gt;

&lt;p&gt;节点的亲和和互斥是一种设置调度器选择节点的规则。这个规则是 nodeSelector（1.0 开始就有的功能）的衍生物。这一规则使用类似给 Node 添加自定义标签，在 Pod 中定义选择器的方式。规则在调度器中可以有必要和推荐两种级别。&lt;/p&gt;

&lt;p&gt;必要的规则要求 Pod 必须调度到某指定节点上。如果没有符合条件（当然也包括通用的调度要求，例如节点必须有足够的资源）。如果没有符合要求的节点，Pod 就不会被调度，必要规则在 &lt;code&gt;nodeAffinity&lt;/code&gt; 的 &lt;code&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/code&gt; 字段中定义。&lt;/p&gt;

&lt;p&gt;例如在一个 GCE 上的多区域 Kubernetes 集群中，我们希望把 Pod 运行在一个 &lt;code&gt;us-central1-a&lt;/code&gt; 的区域中，我们可以在 Pod 中使用如下的亲和规则：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: &amp;quot;failure-domain.beta.kubernetes.io/zone&amp;quot;
            operator: In
            values: [&amp;quot;us-central1-a&amp;quot;]

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;IgnoredDuringExecution&lt;/code&gt; 表示在 Pod 已经成功运行后，如果 Node 的标签发生了变化导致其不再符合 Pod 的调度要求，Pod 依然会继续运行；&lt;code&gt;requiredDuringSchedulingRequiredDuringExecution&lt;/code&gt; 则相反，一旦出现这种变化，他会立即从 Node 上驱逐 Pod。&lt;/p&gt;

&lt;p&gt;推荐级别的规则表示优先选择符合规则要求的节点，如果找不到，则降级选择普通节点。我们可以用优先规则代替必要规则，选择&lt;code&gt;us-central1-a&lt;/code&gt;进行 Pod 的运行，只要修改成&lt;code&gt;preferredDuringSchedulingIgnoredDuringExecution&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;
affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: &amp;quot;failure-domain.beta.kubernetes.io/zone&amp;quot;
            operator: In
            values: [&amp;quot;us-central1-a&amp;quot;]

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;节点的互斥可以利用否定操作符来实现。所以如果让 Pod 避免运行在&lt;code&gt;us-central1-a&lt;/code&gt;，可以这样实现：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: &amp;quot;failure-domain.beta.kubernetes.io/zone&amp;quot;
            operator: NotIn
            values: [&amp;quot;us-central1-a&amp;quot;]

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可用的操作符包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In&lt;/li&gt;
&lt;li&gt;NotIn&lt;/li&gt;
&lt;li&gt;Exists&lt;/li&gt;
&lt;li&gt;DoesNotExist&lt;/li&gt;
&lt;li&gt;Gt&lt;/li&gt;
&lt;li&gt;Lt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;需要这一功能的场景还包括节点的硬件结构、操作系统版本或者特殊硬件等。节点的亲和与互斥在 Kubernetes 1.6 之中处于 Beta 阶段。&lt;/p&gt;

&lt;h2 id=&#34;taint-污染-变质-和-tolerations-容忍-耐受&#34;&gt;Taint（污染、变质）和 Tolerations（容忍、耐受）&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;这俩名词让我非常挠头，不好下嘴。&lt;/p&gt;

&lt;p&gt;另外这里的阐述比起 Kubectl help taint 来说，清晰程度差了太多。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这一功能让用户可以把一个节点标记为 taint 的话，除非 Pod 被标识为可以耐受污染节点，否则不会有任何 Pod 被调度到该节点上。之所以把 taint 标记到节点而不是像亲和性一样标记在 Pod 上，是因为在这种情况下，绝大多数的 Pod 都不应该部署到 Taint 的节点上。例如用户可能希望把主节点保留给 Kubernetes 系统组件使用，或者把一部分节点保留给一组用户，或者把一组具有特殊硬件的服务器保留给有需求的 Pod。&lt;/p&gt;

&lt;p&gt;可以用 &lt;code&gt;kubectl&lt;/code&gt; 命令对节点进行 taint 操作：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl taint nodes node1 key=value:NoSchedule&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;在节点上创建了一个 tiant，一个 Pod 必须在 Spec 中做出这样的 Toleration 定义，才能调度到该节点：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;
tolerations: 
- key: &amp;quot;key&amp;quot;
  operator: &amp;quot;Equal&amp;quot;
  value: &amp;quot;value&amp;quot;
  effect: &amp;quot;NoSchedule&amp;quot;

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;effect 除了 NoSchedule 这个值之外，还有一个 Prefer 版本的 &lt;code&gt;PreferNoSchedule&lt;/code&gt;，另外还有一个 &lt;code&gt;NoExecute&lt;/code&gt; 选项，这个选项意味着这一 Taint 生效之时，如果该节点内正在运行的 Pod 没有对应的 Tolerate 设置，会被直接逐出。&lt;/p&gt;

&lt;p&gt;目前这一特性在 Kubernetes 1.6 升级为 Beta，我们加入了一个 Alpha 特性，可以指定在节点遇到问题的时候，该节点之上的 Pod 可以保持该绑定的时间长度（缺省五分钟）。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考：
&lt;a href=&#34;https://kubernetes.io/docs/user-guide/node-selection/#per-pod-configurable-eviction-behavior-when-there-are-node-problems-alpha-feature&#34; target=&#34;_blank&#34;&gt;https://kubernetes.io/docs/user-guide/node-selection/#per-pod-configurable-eviction-behavior-when-there-are-node-problems-alpha-feature&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;pod-的亲和与互斥&#34;&gt;Pod 的亲和与互斥&lt;/h2&gt;

&lt;p&gt;Node 的亲和与互斥特性允许用户通过对 Pod 的定义来选择运行的 Node。但是还有一种需求就是，Pod 的相互关系，例如对同一个服务里面的 Pod 进行分布或者集中，或者和其他服务的 Pod 如何相处？Pod 的亲和与互斥就应运而生，这一特性在 Kubernetes 1.6 中也处于 Beta 阶段。&lt;/p&gt;

&lt;p&gt;看一个例子。假设有一个叫 S1 的前端服务，会经常和一个叫 S2 的后端服务进行通信（南北通信模式），所以我们希望这两个服务能够被安排在同一个云服务区域，但是我们也不想做手工选择——一旦某个区域出了问题，我们希望这些 Pod 能够再次迁移到同一个区域。这里就可以定义 Pod 亲和性来达成这一目的了（假设我们给两组服务都设置 Label，service=s1/s2）：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;
affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: service
            operator: In
            values: [&amp;quot;S1&amp;quot;]
        topologyKey: failure-domain.beta.kubernetes.io/zone

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;和节点的亲和性类似，这里也有一个变量：&lt;code&gt;preferredDuringSchedulingIgnoredDuringExecution&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;Pod 的亲和性弹性很大。设想在性能测试的过程中，发现两个服务的容器处于同一个节点时，S1 的容器会干扰 S2 的容器的性能，这可能会是由缓存或者网络的拥堵造成的。或者出于安全考虑，我们不想两个服务共享同一个节点。要实现这种互斥操作，只要稍微改动一下哎上面的代码：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;podAffinity&lt;/code&gt; 改为 &lt;code&gt;podAntiAffinity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;topologyKey&lt;/code&gt; 改为 &lt;code&gt;kubernetes.io/hostname&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;自定义调度&#34;&gt;自定义调度&lt;/h2&gt;

&lt;p&gt;如果 Kubernetes 调度器的众多特性还没能满足你的控制欲，可以用自己独立运行的调度器来对指定的 Pod 进行调度。在 Kubernetes 1.6 中，多调度器特性也进入了 Beta 阶段。&lt;/p&gt;

&lt;p&gt;一般情况下，每个新 Pod 都会由缺省调度器进行调度。但是如果 Pod 中提供了自定义的调度器名称，那么缺省调度器就会忽略该 Pod，转由指定的调度器把该 Pod 分配给节点。下面举例说明。&lt;/p&gt;

&lt;p&gt;代码中的 Pod 指定了&lt;code&gt;schedulerName&lt;/code&gt;字段：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  schedulerName: my-scheduler
  containers:
  - name: nginx
    image: nginx:1.10

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果我们在不部署自定义调度器的情况下，创建这个 Pod，缺省调度器会忽略这个 Pod，后果是他会在&lt;code&gt;Pending&lt;/code&gt;状态下停滞不前。所以我们需要为他创建一个&lt;code&gt;schedulerName&lt;/code&gt;值为&lt;code&gt;my-scheduler&lt;/code&gt;的调度器。&lt;/p&gt;

&lt;p&gt;可以用任何语言来实现简单或复杂的调度器。下面的简单例子是用 Bash 实现的——随机指派一个节点。注意首先要运行&lt;code&gt;kubectl proxy&lt;/code&gt;来支持这一脚本的运行。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
SERVER=&#39;localhost:8001&#39;
while true;
do
    for PODNAME in $(kubectl --server $SERVER get pods -o json | jq &#39;.items[] | select(.spec.schedulerName == &amp;quot;my-scheduler&amp;quot;) | select(.spec.nodeName == null) | .metadata.name&#39; | tr -d &#39;&amp;quot;&#39;)
;
    do
        NODES=($(kubectl --server $SERVER get nodes -o json | jq &#39;.items[].metadata.name&#39; | tr -d &#39;&amp;quot;&#39;))
        NUMNODES=${#NODES[@]}
        CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
        curl --header &amp;quot;Content-Type:application/json&amp;quot; --request POST --data &#39;{&amp;quot;apiVersion&amp;quot;:&amp;quot;v1&amp;quot;, &amp;quot;kind&amp;quot;: &amp;quot;Binding&amp;quot;, &amp;quot;metadata&amp;quot;: {&amp;quot;name&amp;quot;: &amp;quot;&#39;$PODNAME&#39;&amp;quot;}, &amp;quot;target&amp;quot;: {&amp;quot;apiVersion&amp;quot;: &amp;quot;v1&amp;quot;, &amp;quot;kind&amp;quot;
: &amp;quot;Node&amp;quot;, &amp;quot;name&amp;quot;: &amp;quot;&#39;$CHOSEN&#39;&amp;quot;}}&#39; http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
        echo &amp;quot;Assigned $PODNAME to $CHOSEN&amp;quot;
    done
    sleep 1
done

&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;更多&#34;&gt;更多&lt;/h2&gt;

&lt;p&gt;Kubernetes 1.6 的 release notes 中提供了更多的这些特性的相关信息，其中尤其包括了如果已经使用了 Alpha 版本如何进行升级的问题。&lt;/p&gt;

&lt;h2 id=&#34;鸣谢&#34;&gt;鸣谢&lt;/h2&gt;

&lt;p&gt;文中描写的功能，包括 Alpha 和 Beta 阶段的功能，都是社区中来自 Google、华为、IBM 以及 Red Hat 等公司的工程师的努力成果。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
