心简单世界就简单 發表於 2019-6-14 14:42:00

Kubernetes调度之亲和与反亲和

<blockquote>
<p>系列目录</p>
</blockquote>
<p>部署pod时,大多数情况下kubernetes的调度程序能将pod调度到集群中合适的节点上。但有些情况下用户需要对pod调度到哪个节点上施加更多控制,比如将特定pod部署到拥有SSD存储节点、将同一个服务的多个后端部署在不同的机器上提高安全性、将通信频繁的服务部署在同一个可用区域降低通信链路长度。用户对pod部署的节点施加控制都与"label selector"有关。</p>
<h2 id="nodeselector节点选择器">nodeSelector(节点选择器)</h2>
<p>nodeSelector也是标签选择器,是最简单、最直接控制pod部署node的方法,在daemonset用nodeSelector过滤可部署节点,以下是其普通的应用示例。</p>
<h2 id="步骤1为节点添加标签">步骤1:为节点添加标签</h2>
<ul>
<li>
<p>kubectl get nodes,返回集群中所有node。</p>
</li>
<li>
<p>kubectl node label <node-name> <label-key>=<label-value>,为node选定的node添加标签。如kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd</label-value></label-key></node-name></p>
</li>
<li>
<p>为pod configuration添加节点选择器:</p>
</li>
</ul>
<pre><code class="language-yml">apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
    env: test
spec:
containers:
- name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
nodeSelector:
    disktype: ssd

</code></pre>
<p>当运行kubectl create -f https://k8s.io/examples/pods/pod-nginx.yaml命令创建pod时,节点选择器选中有上述节点,如果没有符合条件的node则调度失败,为pod输出调度失败事件并指明失败原因。pod一直处于pending状态,直到找到合适的节点。</p>
<p>运行kubectl get pods -o wide查看调度程序为pod选定的节点。</p>
<h3 id="interlude-built-in-node-labels插曲内置节点标签">Interlude: built-in node labels(插曲:内置节点标签)</h3>
<p>上例中使用用户自定义标签,也可以使用系统自动生成的内置节点标签。不同kubernetes版本、不同的基础设备供应商,默认添加的内置节点标签可能不同,需查询相关文档确认,以下是kubernetes1.4的内置节点标签:</p>
<ul>
<li>kubernetes.io/hostname</li>
<li>failure-domain.beta.kubernetes.io/zone</li>
<li>failure-domain.beta.kubernetes.io/region</li>
<li>beta.kubernetes.io/instance-type</li>
<li>beta.kubernetes.io/os</li>
<li>beta.kubernetes.io/arch</li>
</ul>
<h2 id="亲和与反亲和affinity-and-anti-affinity">亲和与反亲和(Affinity and anti-affinity)</h2>
<p>nodeSelector只能基于节点标签控制pod部署node,并且选择器只支持“与”逻辑操作。亲和与反亲和特性目前处于测试阶段,相比于节点选择器,其更灵活,功能更强大,体现在以下三点:</p>
<ol>
<li>
<p>不仅仅是“与”,支持更多的逻辑表达式。</p>
</li>
<li>
<p>nodeSelector是硬性要求,亲和与反亲和支持软硬两种要求。</p>
</li>
<li>
<p>除了节点标签,亲和与反亲和支持根据节点上已经部署的pod进行节点选择,这一点很重要。比如不想将两种计算密集类型的pod部署在同一节点上,后部署pod可选择过滤。</p>
</li>
</ol>
<p>细分成两种类型的选择器:"节点亲和"与"内部pod亲和、反亲和"。节点亲和与nodeSelector相似,具备上述1、2两条优点。内部pod亲和依赖的是节点上已有pod的标签而不是节点标签,兼俱上述三个优点。因为节点亲和能完成nodeSelector所工作并且具备额外的优点,因此nodeSelector虽然还能用,但已经不再维护,并且将来可能删除。</p>
<h3 id="节点亲和测试特性">节点亲和(测试特性)</h3>
<p>节点亲和与nodeSelector工作原理相同,都是其于node标签选择节点。有两点不同,第一是节点亲和支持软硬两种节点选择requiredDuringSchedulingIgnoredDuringExecution、preferredDuringSchedulingIgnoredDuringExecution。前者是硬性条件必需满足,后者是软性条件,属于偏好部署。第二点是在选择节点时,节点亲和比nodeSelector支持更多更灵活的表达式,示例如下:</p>
<pre><code class="language-yml">apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
      preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
containers:
- name: with-node-affinity
    image: k8s.gcr.io/pause:2.0
</code></pre>
<p>符合条件的节点必需满足requiredDuringSchedulingIgnoredDuringExecution的条件,在此基础上,如果有节点满足<br>
preferredDuringSchedulingIgnoredDuringExecution的条件,则更倾向部署在后者上。查询表达式中可以使用的操作符有:<br>
<code>In</code>, <code>NotIn</code>, <code>Exists</code>, <code>DoesNotExist</code>, <code>Gt</code>, <code>Lt</code>等。</p>
<p>在requiredDuringSchedulingIgnoredDuringExecution中的matchExpressions可以包含多条选择表达式,相互之间是"逻辑与"的关系,必需同时满足。preferredDuringSchedulingIgnoredDuringExecution中的matchExpressions也可以有多条,因为它是软性条件,因为并非一定要全匹配,匹配的条目越多越符合条件。另外还可以为偏好中的表达式赋予不同的权重weight,可取的值在0-100之间,最后通过计算权重和决定那个节点更符合条件。</p>
<p>如果同时使用了nodeSelector与nodeAffinity,那么目标节点必需同时满足这两个选择器。</p>
<h3 id="内部pod亲和与反亲和测试特性">内部pod亲和与反亲和(测试特性)</h3>
<p>内部pod亲和与反亲和特性由kubernetes1.4版本初次引入,其基于节点上已部署pod的标签计算亲和与反亲和,如实现将两种通信频繁pod部署在相同节点,将两种计算密集型pod部署在不同节点等。</p>
<blockquote>
<p>提示:实现内部亲和与反亲和需要数量可观的计算步骤,会明显降低pod调度的速度,集群规模越大、节点越多降速越明显,呈指数级增长,需要在使用此特性时考虑对调度速度的影响。</p>
</blockquote>
<p>在配置时,内部pod亲和用podAffinity字段表示,内部pod反亲和用podAntiAffinity字段表示,其它与节点亲和一样,也有软硬两种选择器,每种选择器可以多个过滤条件。</p>
<p>内部pod亲和示例:</p>
<pre><code class="language-yml">apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
      topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
      podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
            operator: In
            values:
            - S2
          topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0
</code></pre>
<p>上例中的topologyKey用来缩小节点选择范围,其值可以是任何合法的节点标签,在大规模集群中,为此字段不指定或者指定错误值,可能引发巨大的性能、安全问题。因此,对其使用有如下限制:</p>
<ul>
<li>
<p>对于亲和与硬性反亲和,topologyKey字段值不能为空。</p>
</li>
<li>
<p>对于硬性反亲和,topoloygKey只能是kubernetes.io/hostname,除非禁止LimitPodHardAntiAffinityTopology允入控制器或者修改其实现。</p>
</li>
<li>
<p>对于软件反亲和,允许topoloygKey为空,表示对节点拓扑没有限制。</p>
</li>
<li>
<p>以上情况外,topologyKey可以是任何合法标签。</p>
</li>
</ul>
<h3 id="示例1用反亲和特性实现pod位置协商">示例1:用反亲和特性实现pod位置协商</h3>
<p>假设集群有五个工作节点,部署一个web应用,假设其用redis作内存缓存,共需要三个副本,通过反亲和将三个redis副本分别部署在三个不同的节点上,提高可用性,Deployment配置如下:</p>
<pre><code class="language-yml">apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
    matchLabels:
      app: store
replicas: 3
template:
    metadata:
      labels:
      app: store
    spec:
      affinity:
      podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
            matchExpressions:
            - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
      image: redis:3.2-alpine
</code></pre>
<p>反亲和会阻止同相的redis副本部署在同一节点上。</p>
<p>现在部署三个nginx web前端,要求三个副本不对分别部署在不同的节点上,通过与上列相似的反亲和实现。同时需要将三个web前端部署在其上已经部署redis的节点上,降低通信成本,通过亲和实现,配置如下:</p>
<pre><code class="language-yml">apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
    matchLabels:
      app: web-store
replicas: 3
template:
    metadata:
      labels:
      app: web-store
    spec:
      affinity:
      podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
            matchExpressions:
            - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
      podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
            matchExpressions:
            - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
      image: nginx:1.12-alpine
</code></pre>
<p>参考</p><br><br>
来源:https://www.cnblogs.com/tylerzhou/p/11023188.html
頁: [1]
查看完整版本: Kubernetes调度之亲和与反亲和