Kubernetes Pod调度

Kubernetes Pod调度

自动调度

在使用控制方式(Deployment、RC、RS等)部署Pod时,如果未指定NodeSelector、NodeAffonity、PodAffinity等的调度策略,那么就由Master的Scheduler经过一些列算法计算出Pod中的容器具体部署在某个节点上。
以Deployment为例,创建一个Deployment,保持ReplicaSet为3.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

上述创建出一个Deployment,并且Deployment创建出三个副本,通过查看每个副本的详细信息(describe)可以知道副本部署的节点位置。

NodeSelector(Node定向调度)

kubernetes Master上的Scheduler(kube-scheduler)服务负责实现Pod的调度,通过一些列的的复杂算法,为Pod计算出一个最佳的目标节点,整个过程时自动完成的,如果需要查看Pod具体被调度在那个节点上就需要查看Pod的详细信息。但是在实际应用中,我们通常需要将一些特定的Pod调度在指定Node上,所以在此基础上,我们可以选择通过Node上的标签(Label)和Pod的nodeSelector属性进行匹配,已达到该目的。

  1. 首先给Node打上相关的标签

    kubectl label nodes node1 zone=beijing
    # 给node1打上zone=beijing的标签
    
  2. 在Pod定义中添加nodeSelector

    apiVersion: v1
    kind: ReplicationController 
    metadata:
      name: redis-master
      labels:
        name: redis-master 
    spec:
      replicas: 1
      selector:
        name: redis-master
      template:
        metadata:
          labels:
            name: redis-master
        spec:
          containers:
          - name: master
            image: kubeguide/redis-master
            ports:
            - containerPort: 6379
          nodeSelector:
            zone: beijing			# 指定在含有zone=beijing的node上运行
    

通过上述文件创建出Pod,查看该Pod的详细信息则能发现,该Pod被调度到node1上。

需要注意的是,在指定Pod的nodeSelector时,如果该集群中没有包含相应的标签或者集群中多个node都存在相应的标签,这时候Pod可能面临着调度失败的情况。
但是在节点亲和性(NodeAffinity)越来越能够体现nodeSelector的功能,未来有可能被取代

NodeAffinity(Node亲和性调度)

NodeAffinity是Node亲和性的调度策略,用于替代nodeSelector的全新调度方式,目前有两种节点亲和性表达。

  • requiredDuringSchedulingIgnoredDuringExecution:必须满足指定的规则才可以调度Pod到指定的Node上,和nodeSelector很像,属于硬限制。
  • preferredDuringSchedulingIgnoredDuringExecution:强调有限满足指定规则,调度器尝试调度Pod到指定Node上,但是并不强求,属于软限制。多个优先级规则还可以设定权重,定义先后顺序。

IgnoredDuringExecution表示在Pod运行期间,Node上的相关标签被修改,不符合Pod亲和性的要求,这事系统忽略Node上的Label变化,该Pod正常运行。

@@ -0,0 +1,25 @@
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:	# 要求只运行在amd64的节点上
        nodeSelectorTerms:
        - matchExpressions:
          - key: beta.kubernetes.io/arch
            operator: In
            values:
            - amd64
      preferredDuringSchedulingIgnoredDuringExecution:	# 要求尽量运行在磁盘类型为ssd的节点上
      - weight: 1
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: with-node-affinity
    image: gcr.io/google_containers/pause:2.0

从上述文件中可以看出In操作符,在nodeAffinity语法支持的操作符包含In、NotIn、Exists、DoesNotExist、Gt、Lt。虽然没有节点排斥功能,但是可以用NotIn和DoesNotExist可以实现

nodeAffinity使用时注意:

  • 如果同时定义了nodeAffinity和nodeSelector,那么必须满足两个条件才能调度成功
  • 如果nodeAffinity定义了多个nodeSelectorTerms,那么满足其中一个就可以匹配成功
  • 如果在nodeSelectorTerms中有多个matchExpressions,则节点必须满足所有的matchExpressions才能实现调度成功

PodAffinity(Pod亲和性和互斥性调度)

在使用kubernetes的过程中往往会遇到希望两个或者多个Pod在同一个Node上运行,或者是希望两个或者多个Pod不在同一个Node上运行的情况,在这种应用场景下,就需要使用到Pod亲和性和互斥性了。

这里补充一个拓扑域的知识,在master添加节点的时候,会为每个Node创建相应的拓扑域,kubernetes同时内置了一些常用的默认拓扑域:

  1. kubernetes.io/hostname
  2. topology.kubernetes.io/region
  3. topology.kubernetes.io/zone
    以上的拓扑域由kubernetes自己维护。
    以下测试Pod亲和性
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-flag
  labels:
    security: "S1"
    app: "nginx"
spec:
  containers:
  - name: nginx
    image: nginx

通过以上代码创建一个Pod,并且带着security=S1和app=nginx标签

---
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:	# 希望调度到带有security=S1标签的Pod所在的节点
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: gcr.io/google_containers/pause:2.0

通过以上的代码创建出第二个Pod,在创建Pod之后u,可以用过kubectl get pods -o wide查看两个Pod在同一个Node上运行

---
apiVersion: v1
kind: Pod
metadata:
  name: anti-affinity
spec:
  affinity:
    podAffinity:	# 希望调度到带有security=S1标签的Pod所在的节点
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:	# 希望调度到不带有app=nginx标签的Pod所在的节点
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - nginx
        topologyKey: kubernetes.io/hostname
  containers:
  - name: anti-affinity
    image: gcr.io/google_containers/pause:2.0

通过以上的代码创建第三个Pod,使用kubectl get pods -o wide可以发现Pod被调度到一个新的Node上,并且该节点也是属于kubernetes.io/hostname拓扑域的节点。

podAffinity使用时注意:

  • 除了设置Label Selector和topologyKey,还可以设定Namespace列表进行限制,同样使用Label Selector和Namespace进行选择,Namespace和topologyKey、Label Selector同级。如果省略这表示和当前Pod同一个命名空间,如果为“”空值,则表示所有命名空间。
  • 在所有关联requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全部满足后,系统才能将Pod调度到某个Node上。

Taints和Tolerations(污点和容忍)

污点表示Node拒绝Pod调度,被Taints标记的Node表示该Node暂时不接受Pod的调度,但是也是有效的工作节点,如果在这个阶段中,任然需要一些指定的Pod调度在这个节点上,则需要Tolerations属性来实现。
在默认情况下,Node上被设定一个或者多个污点之后,除非Pod存在相应的Tolerations属性,否则无法被调度到指定的Node,可以使用kubectl taint命令设定Node的污点信息:

kubectl taint nodes node1 key=value:Noschedule

以上命令为node1加上了一个taint,该taint的键为key,值为value,效果是NoSchedule,意味着除非Pod明确声明可以容忍这个Taint,否则不会被调度到这个node1上。
在Pod中需要做以下设定

tolerations:
 - key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
或者
tolerations:
 - key: "key"
  operator: "Exists"
  effect: "NoSchedule"

Pod的tolerations声明中的key和effect需要和taint保持一致,并且满足以下条件之一:

  • operator的值是Exists(无须指定value)

  • operator的值是Equal并且value相等
    另外两个特例:

  • 空的key配合Exists操作符能够匹配所有键值

  • 空的effect匹配所有effect

其中effect的值为NoSchedule,还可以设定为PreferNoSchedule,意思为优先,算作NoSchedule的软限制,一个Pod如果没有声明容忍这个污点,系统则会尽量避免把Pod调度到这个Node上,但不是强制的。

注意如果Node上存在多个taint,则toleration也需要匹配多个taint,如果在已经运行Pod的Node上添加新的taint,则Pod集训运行。

一般来说,如果Node设定了effect=NoExecute的Taint,那么在Node上运行的无对应的tolerations的Pod将会被驱逐,而有对应的tolerations则不会,但是系统允许给具有NoExecute效果的toleration加入tolerationSeconds字段,表明了Pod可以在Taint添加到Node上之后能延迟运行多长时间。

tolerations:
 - key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoExecute"
  tolerationSeconds: 3600 		# 秒

一般来说创建Taint和tolerations的作用大致如下:

  • 独占节点
  • 具有特殊硬件设备的节点
  • 当节点故障

Pod Priority Preemption(Pod优先级调度)

在集群中可能会出现集群内的的资源不足的情况,在此情况下,新建的Pod都会处于Pending状态,只能被动的等待其他Pod被删除,释放出资源之后才能正常创建,在kubernetes 1.8之后引入了Pod优先级抢占的机制(Pod Priority Preemption),在高优先级的Pod被创建时,如果资源不足,系统则会尝试释放低优先级的Pod,腾出空间给予高优先级的Pod使用。这种方式称为抢占式调度。

优先级抢占调度策略的核心行为分别是驱逐(Evication)和抢占(Preemption),使用场景不同,但是效果是相同的。
Evication是由kubelet控制的,当Node的资源不足时,kubelet会综合考虑Pod的优先级、资源申请和实际使用量等信息来计算哪些Pod需要被驱逐,当同样优先年纪的Pod被驱逐时,实际使用的资源量超过申请量最大倍数的高耗能Pod将被驱逐。
Preemption则是Scheduler控制的,当新的Pod因为资源问题无法被调度时,Scheduler可能驱逐低优先级的Pod实例来满足新的Pod的调度目标

创建PriorityClass,PriorityClass不属于任何命名空间

---
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

上述代码中定义了一个high-priority的优先级类别,优先级数字为1000000,数字越大,优先级越高,超过一亿的数字被系统保留用于系统组件。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

上述文件中priorityClassName指定了该Pod的优先级。

但是如果新建的Pod不希望发生优先级调度时,则可以添加新的属性preemptionPolicy,当它的值为preemptionLowerPolicy(默认)时,就会拥有抢占的功能,当设定为never时则失去抢占资源的能力,而是静静排队,等待调度机会。

需要注意的是使用优先级抢占的调度策略,可能会使某些Pod永远无法正常被调度,因此优先级调度不但增加了系统的复杂性,还可能会出现不稳定问题,因此在发生集群中资源不足的情况,优先考虑扩展集群,慎用!!

DaemonSet(每个Node调度一个Pod)

DaemonSet是一个资源对象,用于管理集群中没有Node上仅运行一份Pod实例副本。

DaemonSet的Pod调度和RC类似,使用内置的算法在每一个Node进行调度,也可以使用NodeSelector或者NodeAffinity指定满足条件的Node范围进行调度。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-cloud-logging
  namespace: kube-system
  labels:
    k8s-app: fluentd-cloud-logging
spec:
  template:
    metadata:
      namespace: kube-system
      labels:
        k8s-app: fluentd-cloud-logging
    spec:
      containers:
      - name: fluentd-cloud-logging
        image: gcr.io/google_containers/fluentd-elasticsearch:1.17
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
        env:
        - name: FLUENTD_ARGS
          value: -q
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: false
        - name: containers
          mountPath: /var/lib/docker/containers
          readOnly: false
      volumes:
      - name: containers
        hostPath:
          path: /var/lib/docker/containers
      - name: varlog
        hostPath:
          path: /var/log

上述文件定义了在每个Node上都启动一个fluentd容器,其中挂载了物理机的两个目录,/var/log和/var/lib/docker/containers

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值