如何指定pod的运行节点?

一般情况下kubernets可以通过kube-scheduler默认的调度策略合理的将pod分配到可用的节点上, 但是随着pod数量的增加以及不同pod对资源的使用情况不同我们需要更加合理的分配集群中的资源, 所以对一些pod运行节点的控制是由必要的。

源于硬件和软件层多样性,我们需要将某个 pod 调度到某些特定的节点上,例如指定机房,存储类型,网络类型等等:

  • 指定机房调度:某些业务希望部署在指定的机房中。

  • 专属节点资源:某些机器属于某个业务独享,只有该业务方的容器才能调度到这些节点上运行。

  • 磁盘类型调度:计算节点的磁盘类型包含 ssd 和 sata,其中 sata 盘的 IO 性能较差。对于 IO 密集型 Pod,我们希望将其调度到磁盘类型为 ssd 的服务器上,对于非 IO 密集型 Pod,将其调度到磁盘类型为 sata 盘的服务器上。

  • 存储类型调度:不同节点可能支持不同的持久化存储模式,例如:local/ceph/gluster 等。我们需要根据 pod 要求的存储类型将其调度到相应的节点上。

  • 网络调度:根据网络信息调度到支持该网络的节点。

有两种方法 nodeSelector以及affinity 可以实现对应的需求

1.node label规划

K8S中pod的调度都是通过节点label实现的 , 所以对于除必要的节点label外对于其它用途也要做一些规划

Shortcut

Description

department

部门名称

edgenode

是否为边缘节点

dedicated

节点用途

设置参数

Shortcut

Description

department

devops,o2o,

edgenode

true

dedicated

job,edgenode,model,app,addons

为 node 设置 taint 与 label:

kubectl taint nodes node -l edgenode=true dedicated=edgenode department=devops edgenode=true:NoSchedule

or

kubectl label nodes node edgenode=true dedicated=edgenode department=devops
kubectl taint nodes node edgenode=true:NoSchedule

删除taint:

kubectl taint nodes node edgenode:NoSchedule-

删除node的label

kubectl label node node edgenode-

查看 node上的 taint:

kubectl describe nodes node

查看node1的label

kubectl get  nodes  --show-labels

2.nodeSelector

kubernetes中是通过label-selector机制进行节点选择,由scheduler调度策略MatchNodeSelector进行label匹配,调度pod到目标节点,该匹配规则是强制约束。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-test
  labels:
    k8s-app: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeSelector:
        department: devops 
      containers:
      - name: nginx
        image: nginx:1.7.9

3.affinity

affinity类型

类型包括:

  • NodeAffinity, node affinity

  • PodAffinity, pod亲和性(Inter-pod affinity)

  • PodAntiAffinity 反亲和性(anti-affinity)

限制方式:

  • requiredDuringSchedulingIgnoredDuringExecution 表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行。

  • preferredDuringSchedulingIgnoredDuringExecution 表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。

  • 软策略和硬策略的区分是有用处的,硬策略适用于 pod 必须运行在某种节点,否则会出现问题的情况,比如集群中节点的架构不同,而运行的服务必须依赖某种架构提供的功能;软策略不同,它适用于满不满足条件都能工作,但是满足条件更好的情况,比如服务最好运行在某个区域,减少网络传输等。这种区分是用户的具体需求决定的,并没有绝对的技术依赖。

匹配逻辑label

  • In: label的值在某个列表中

  • NotIn:label的值不在某个列表中

  • Exists:某个label存在

  • DoesNotExist:某个label不存在

  • Gt:label的值大于某个值(字符串比较)

  • Lt:label的值小于某个值(字符串比较)

如果nodeAffinity中nodeSelector有多个选项,节点满足任何一个条件即可;如果matchExpressions有多个选项,则节点必须同时满足这些选项才能运行pod 。需要说明的是,node并没有anti-affinity这种东西,因为NotIn和DoesNotExist能提供类似的功能。

NodeAffinity

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-test
  labels:
    k8s-app: nginx
spec:
  replicas: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - preference:
              matchExpressions:
              - key: department
                operator: In
                values:
                - model
            weight: 1
          - preference:
              matchExpressions:
              - key: dedicated
                operator: In
                values:
                - app
            weight: 5
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: department
                operator: In
                values:
                - devops
      containers:
      - name: nginx
        image: nginx:1.7.9

这个 pod 同时定义了requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution两种nodeAffinity。第一个要求 pod 运行在特定 devops 的节点上,第二个希望节点最好有对应的department:model和dedicated:app标签, 根据权重决定了顺序NodeSelectorTerms可以有多个,之间是或的关系,满足任意一个既满足,MatchExpressions也可以有多个,他们之间是且的关系 必须都满足 preferredDuringSchedulingIgnoredDuringExecution值为列表,根据权重决定顺序MatchExpressions值为列表 关系为且,必须都满足

PodAffinity && PodAntiAffinity

PodAffinit是根据通过已运行在节点上的pod的标签而不是node的标签来决定被调度pod的运行节点,因为pod运行在指定的namespace所以需要自己指定运行pod的namesapce

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
  labels:
    app: pod-affinity-pod
spec:
  containers:
  - name: with-pod-affinity
    image: nginx
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - busybox-pod
        topologyKey: kubernetes.io/hostname
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - node-affinity-pod
          topologyKey: kubernetes.io/hostname

上面这个例子中的 POD 需要调度到某个指定的主机上,至少有一个节点上运行了这样的 POD:这个 POD 有一个app=busybox-pod的 label。podAntiAffinity则是希望最好不要调度到这样的节点:这个节点上运行了某个 POD,而这个 POD 有app=node-affinity-pod的 label。根据前面两个 POD 的定义,我们可以预见上面这个 POD 应该会被调度到一个busybox-pod被调度的节点上,而node-affinity-pod被调度到了该节点以外的节点

污点(Taints)与容忍(tolerations)

对于nodeAffinity无论是硬策略还是软策略方式,都是调度 POD 到预期节点上,而Taints恰好与之相反,如果一个节点标记为 Taints ,除非 POD 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度pod。

比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 POD,则污点就很有用了,POD 不会再被调度到 taint 标记过的节点。taint 标记节点举例如下:

$ kubectl taint nodes kube-node key=value:NoSchedule
node "kube-node" tainted

如果仍然希望某个 POD 调度到 taint 节点上,则必须在 Spec 中做出Toleration定义,才能调度到该节点,举例如下:

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

effect 共有三个可选项,可按实际需求进行设置:

  • NoSchedule:POD 不会被调度到标记为 taints 节点。

  • PreferNoSchedule:NoSchedule 的软策略版本。

  • NoExecute:该选项意味着一旦 Taint 生效,如该节点内正在运行的 POD 没有对应 Tolerate 设置,会直接被逐出

example

traefik 部署

设置label 和 taint, edgenode为特殊属性的节点所以需要设置taint

kubectl taint nodes node  edgenode=true:NoSchedule --overwrite 
kubectl  label nodes node department=devops edgenode=true service=traefik

资源文件

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-test
  labels:
    k8s-app: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: dedicated
                operator: In 
                values: 
                - edgenode

              - key: department
                operator: In
                values:
                - devops

      tolerations: 
      - key: "edgenode"
        operator: "Exists"
        effect: "NoSchedule"

      containers:
      - name: nginx
        image: nginx:1.7.9

运维应用部署

运维服务部署在固定的几台主机上, 每个主机设置department的label, 部署服务时指定该label

设置label

kubectl  label nodes node department=devops

资源文件

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-test
  labels:
    k8s-app: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity: 
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
                - key: department
                  operator: In
                  values:
                  - devops
      containers:
      - name: nginx
        image: nginx:1.7.9

pod尽可能的分配到多个节点上

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 1
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - nginx
                topologyKey: kubernetes.io/hostname

      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

pod强制分配到不同的node节点上

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - weight: 1
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - nginx
                topologyKey: kubernetes.io/hostname

      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

不同应用就近部署, 如app=nginx 和 app=frontend部署到相同节点

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - weight: 1
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - nginx
                topologyKey: kubernetes.io/hostname

      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

参考

最后更新于