【K8S专栏】Kubernetes调度管理(上)

简介: 【K8S专栏】Kubernetes调度管理

基本介绍

在日常工作中,每个机场都有调度室,用来管理飞机应该从哪里降落,停在什么地方。在Kubernetes也有这样的调度器,主要作用就是将Pod安排到合适的节点上。

Kubernetes中的调度器是kube-scheduler,工作流程如下:

  1. 在集群中所有Node中,根据调度算法挑选出可以运行该Pod的所有Node;
  2. 在上一步的基础上,再根据调度算法给筛选出的Node进行打分,筛选出分数最高的Node进行调度;
  3. 将Pod的spec.nodeName填上调度结果的Node名字;

其原理图如下:

640.png

由上图可知,Kubernetes的调度器核心是两个相互独立的控制循环。1、Informer Path其主要作用是启动一个Informer来监听Etcd中Pod,Node,Service等与调度器相关的API对象的变化。当一个Pod被创建出来后,就被通过Informer Handler将待调度的Pod放入调度队列中,默认情况下,Kubernetes的调度策略是一个优先级队列,并且当集群信息发生变化的时候,调度器还会对调度队列里的内容进行一些特殊操作。而且Kubernetes的默认调度器还负责对调度器缓存(scheduler cache)进行更新,以执行调度算法的执行效率。

2、Scheduler Path其主要逻辑是不断从队列中出一个Pod,然后调用Predicates进行过滤,然后得到一组Node(也就是可运行Pod的所有Node信息,这些信息都是来自scheduler cache),接下来调用Priorities对筛选出的Node进行打分,然后分数最高的Node会作为本次调度选择的对象。调度完成后,调度器需要将Pod的spec.nodeName的值修改为调度的Node名字,这个步骤称为Bind。

但是在Bind阶段,Kubernetes默认调度器只会更新scheduler cache中的信息,这种基于乐观假设的API对象更新方式被称为Assume。在Assume之后,调度器才会向API Server发起更新Pod的请求,来真正完成Bind操作。如果本次Bind失败,等到scheduler cache更新之后又会恢复正常。

正是由于有Assume的原因,当一个Pod完成调度需要在某个Node节点运行之前,kubelet还会进行一步Admit操作来验证该Pod是否能够运行在该Node上,作为kubelet的二次验证。

常用的预算策略有:

  • CheckNodeCondition
  • GeneralPredication:

HostName, PodFitsHostPort, MatchNodeSelector, PodFitsResources

  • NoDiskConflict

优先级和抢占机制

正常情况下,当一个Pod调度失败后,它会被搁置起来,直到Pod被更新,或者集群状态发生变化,调度器才会对这个Pod进行重新调度。但是有的时候我们不希望一个高优先级的Pod在调度失败就被搁置,而是会把某个Node上的一些低优先级的Pod删除,来保证高优先级的Pod可以调度成功。

Kubernetes中优先级是通过ProrityClass来定义,如下:

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

其中的value就是优先级数值,数值越大,优先级越高。优先级是一个32bit的整数,最大值不超过10亿,超过10亿的值是被Kubernetes保留下来作为系统Pod使用的,就是为了保证系统Pod不会被抢占。另外如果globalDefault的值设置为 true的话表明这个PriorityClass的值会成为系统默认值,如果是false就表示只有在申明这个PriorityClass的Pod才会拥有这个优先级,而对于其他没有申明的,其优先级为0。

如下定义Pod并定义优先级:

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

上面的PriotiryClassName就是定义我们的PriorityClass,当这个Pod提交给Kubernetes之后,Kubernetes的PriorityAdmissionController会自动将这个Pod的spec.priority字段设置为我们定义的值。而当这个Pod拥有这个优先级之后,高优先级的Pod就可能比低优先级的Pod先出队,从而尽早完成调度。

而当一个高优先级的Pod调度失败后,其抢占机制就会被触发,这时候调度器就会试图从当前的集群中寻找一个节点,使得这个节点上的一个或多个低优先级的Pod被删除,然后这个高优先级的Pod就可以被调度到这个节点上。当抢占发生时,这个高优先级Pod并不会立即调度到即将抢占的节点上,调度器只会将这个Pod的spec.nominatedNodeName的值设置为被抢占节点的Node名字,然后这个Pod会重新进入下一个调度周期,然后会在这个周期内决定这个Pod被调度到哪个节点上。在这个重新调度期间,如果有一个更高的优先级Pod也要抢占这个节点,那么调度器就会清空原Pod的nominatedNodeName的值,而更高优先级的Pod将会抢占这个值。

实现原理:Kubernetes用两个队列来实现抢占算法:ActiveQ和unschedulableQ。

  1. ActiveQ:凡是在ActiveQ里的Pod,都是下一个周期需要调度的对象,所以当Kubernetes创建一个新的Pod,这个Pod就会被放入ActiveQ里;
  2. unschedulableQ:专门用来存放调度失败的Pod;

那么如果一个Pod调度失败,调度器就会将其放入unschedulableQ里,然后调度器会检查这个调度失败的原因,分析并确认是否可以通过抢占来解决此次调度问题,如果确定抢占可以发生,那么调度器就会把自己缓存的所有信息都重新复制一份,然后使用这个副本来模拟抢占过程。如果模拟通过,调度器就会真正开始抢占操作了:

  1. 调度器会检查牺牲者列表,清空这些Pod所携带的nominatedNodeName字段;
  2. 调度器会把抢占者的nominatedNodeName的字段设置为被抢占的Node名字;
  3. 调度器会开启Goroutine,同步的删除牺牲者;

接下来调度器就会通过正常的调度流程,把抢占者调度成功。在这个过程中,调度器会对这个Node,进行两次Predicates算法:

  1. 假设上述抢占者已经运行在这个节点上,然后运行Predicates算法;
  2. 调度器正常执行Predicates算法;

只有上述者两个都通过的情况下,这个Node和Pod才会被 绑定。

高级调度

上面介绍的是Kubernetres默认的调度策略,有时候默认的调度策略不能满足我们的需求,比如想把Pod调度到指定的节点,或者不让某些节点调度Pod。这时候就要用到更高级的调度策略,主要有如下几种:

  • nodeSelector
  • nodeName
  • nodeAffinity
  • podAffinity
  • podAntiAffinity
  • 污点调度

nodeSelector

nodeSelector也可以叫做节点选择器,其原理是通过在节点上定义label标签,然后Pod中指定选择这些标签,让Pod能够调度到指定的节点上。

比如给kk-node01指定env=uat标签,命令如下:

$ kubectl label nodes kk-node01 env=uat

现在在Pod的YAML清单中配置nodeSelector,如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  nodeSelector:
    env: uat

这样,该Pod就会调度到kk-node01节点上,如果该Pod指定为env=prod,则调度不到kk-node01节点。

nodeName

nodeName也是节点选择器,和nodeSelector不同之处在于nodeName是直接指定节点名,这属于强调度,定义方式如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  nodeName: kk-node01     # 节点名字

nodeAffinity

nodeAffinity叫做节点亲和性调度,其调度方式比nodeSelector和nodeName更强大。

目前,nodeAffinity支持两种调度策略:

  • preferredDuringSchedulingIgnoredDuringExecution
  • requiredDuringSchedulingIgnoredDuringExecution

preferredDuringSchedulingIgnoredDuringExecution表示如果有Node匹配,则优先调度到该Node,如果没有,可以根据配置调度到其他节点。requiredDuringSchedulingIgnoredDuringExecution则表示必须满足条件的节点才允许调度。

定义preferredDuringSchedulingIgnoredDuringExecution的例子如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-preferred
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
          - key: disktype
            operator: In
            values: ["ssd", "harddisk"]
        weight: 60

requiredDuringSchedulingIgnoredDuringExecution的例子如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values: ["ssd", "harddisk"]

其中,operator支持In,NotIn, Exists, DoesNotExist. Gt, and Lt。

podAffinity

上面介绍的nodeSelector,nodeName,nodeAffinity都是针对节点的,下面介绍的podAffinity和podAntiAffinity则是针对Pod。

podAffinity表示Pod亲和性调度,意识就是把Pod调度到与它比较紧密的Pod上,如下:

apiVersion: v1
kind: Pod
metadata:
  name: fronted
  labels:
    app: myapp
    row: fronted
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: backend
  labels:
    app: db
    row: backend
spec:
  containers:
  - name: db
    image: busybox
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values: ["myapp"]
        topologyKey: kubernetes.io/hostname

这表示把后端pod和前端pod调度在一起。

podAffinity也有preferredDuringSchedulingIgnoredDuringExecution和requiredDuringSchedulingIgnoredDuringExecution,也就是硬亲和和软亲和,其使用情况和nodeAffinity一样。

podAntiAffinity

上面介绍了pod的亲和性,这里介绍的podAntiAffinity则是Pod的反亲和性,也就是说不将这类Pod调度到一起。在日常工作中,这种亲和性使用频率还比较高。微服务很少有单Pod,基本都是多个Pod,为了提高应用的高可用,不会将同应用的多个Pod调度到同一台机器上,这时候就要用到podAntiAffinity,如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname
      containers:
      - name: nginx
        image: nginx
相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
24天前
|
Kubernetes 监控 数据安全/隐私保护
K8s好看的管理页面Rancher管理K8S
K8s好看的管理页面Rancher管理K8S
36 4
|
19小时前
|
存储 运维 Kubernetes
Docker+Kubernetes/K8s+Jenkins视频资料【干货分享】
Docker+Kubernetes/K8s+Jenkins视频资料【干货分享】
Docker+Kubernetes/K8s+Jenkins视频资料【干货分享】
|
22小时前
|
存储 Kubernetes Docker
Kubernetes(K8S)集群管理Docker容器(概念篇)
Kubernetes(K8S)集群管理Docker容器(概念篇)
|
12天前
|
JSON Kubernetes Go
无缝集成:在IntelliJ IDEA中利用Kubernetes插件轻松管理容器化应用
无缝集成:在IntelliJ IDEA中利用Kubernetes插件轻松管理容器化应用
24 0
无缝集成:在IntelliJ IDEA中利用Kubernetes插件轻松管理容器化应用
|
10天前
|
Kubernetes 搜索推荐 Docker
使用 kubeadm 部署 Kubernetes 集群(二)k8s环境安装
使用 kubeadm 部署 Kubernetes 集群(二)k8s环境安装
53 17
|
24天前
|
Kubernetes 安全 网络安全
搭建k8s集群kubeadm搭建Kubernetes二进制搭建Kubernetes集群
搭建k8s集群kubeadm搭建Kubernetes二进制搭建Kubernetes集群
106 0
|
26天前
|
Kubernetes API 调度
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(水平扩展控制)
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(水平扩展控制)
59 0
|
1月前
|
Kubernetes Cloud Native Docker
【云原生】kubeadm快速搭建K8s集群Kubernetes1.19.0
Kubernetes 是一个开源平台,用于管理容器化工作负载和服务,提供声明式配置和自动化。源自 Google 的大规模运维经验,它拥有广泛的生态支持。本文档详细介绍了 Kubernetes 集群的搭建过程,包括服务器配置、Docker 和 Kubernetes 组件的安装,以及 Master 和 Node 的部署。此外,还提到了使用 Calico 作为 CNI 网络插件,并提供了集群功能的测试步骤。
219 0
|
1月前
|
弹性计算 运维 Kubernetes
容器服务ACK常见问题之线上的K8s一直waiting如何解决
容器服务ACK(阿里云容器服务 Kubernetes 版)是阿里云提供的一种托管式Kubernetes服务,帮助用户轻松使用Kubernetes进行应用部署、管理和扩展。本汇总收集了容器服务ACK使用中的常见问题及答案,包括集群管理、应用部署、服务访问、网络配置、存储使用、安全保障等方面,旨在帮助用户快速解决使用过程中遇到的难题,提升容器管理和运维效率。
|
1月前
|
Kubernetes 网络安全 调度
容器服务ACK常见问题之容器服务ACK的eci调度卡住如何解决
容器服务ACK(阿里云容器服务 Kubernetes 版)是阿里云提供的一种托管式Kubernetes服务,帮助用户轻松使用Kubernetes进行应用部署、管理和扩展。本汇总收集了容器服务ACK使用中的常见问题及答案,包括集群管理、应用部署、服务访问、网络配置、存储使用、安全保障等方面,旨在帮助用户快速解决使用过程中遇到的难题,提升容器管理和运维效率。

推荐镜像

更多