在k8s中有几大核心组件,Scheduler调度器是其中的一个。调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod。调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。
对每一个新创建的 Pod 或者是未被调度的 Pod,kube-scheduler 会选择一个最优的 Node 去运行这个 Pod。然而,Pod 内的每一个容器对资源都有不同的需求,而且 Pod 本身也有不同的资源需求。因此,Pod 在被调度到 Node 上之前,根据这些特定的资源调度需求,需要对集群中的 Node 进行一次过滤。
在一个集群中,满足一个 Pod 调度请求的所有 Node 称之为可调度节点。如果没有任何一个 Node 能满足 Pod 的资源请求,那么这个 Pod 将一直停留在未调度状态(Pending)直到调度器能够找到合适的 Node。
调度器先在集群中找到一个 Pod 的所有可调度节点,然后根据一系列函数对这些可调度节点打分,然后选出其中得分最高的 Node 来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定。
在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、亲和以及反亲和要求、数据局域性、负载间的干扰等等。
调度流程
kube-scheduler 给一个 pod 做调度选择包含两个步骤:
过滤(断言) predicate 打分(优先级)priority
过滤阶段会将所有满足 Pod 调度需求的 Node 选出来。例如,PodFitsResources 过滤函数会检查候选 Node 的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个 Node 列表,里面包含了所有可调度节点;通常情况下,这个 Node 列表包含不止一个 Node,如果这个列表是空的,代表这个 Pod 不可调度。
在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的 Node。根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。
最后,kube-scheduler 会将 Pod 调度到得分最高的 Node 上。如果存在多个得分最高的 Node,kube-scheduler 会从中随机选取一个。
实现过滤和打分的手段主要有固定节点调度和节点标签调度、亲和和反亲和、污点和容忍度。如果容器对资源进行了请求(例如CPU、内存),那么节点当前可用内存和CPU也是调度需要考虑的。
1.固定节点调度NodeName
nodeName 是节点选择约束的最简单方法,但是由于其自身限制,通常不使用它。nodeName 是 PodSpec 的一个字段。如果它不为空,调度器将忽略 pod,并且运行在它指定节点上的 kubelet 进程尝试运行该 pod。因此,如果 nodeName 在 PodSpec 中指定了,则它的优先级最高。
使用 nodeName 的方式选择节点存在一些限制:
如果指定的节点不存在,pod将会一直处于Pending状态。
如果指定的节点没有资源来容纳 pod,pod 将会调度失败并且其原因将显示为,比如 OutOfmemory 或 OutOfcpu。
云环境中的节点名称并非总是可预测或稳定的。
下面上一个例子
vi test-schedule.yaml apiVersion: v1 kind: Pod metadata: name: test-schedule spec: nodeName: k8s-node1 containers: - name: nginx image: nginx ports: - containerPort: 80 name: http kubectl apply -f test-schedule.yaml kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-schedule 1/1 Running 0 7s 10.244.1.15 k8s-node1 <none> <none>
2.节点标签调度NodeSelector
nodeSelector 是 PodSpec 的一个字段。 它包含键值对的映射。为了使 pod 可以在某个节点上运行,该节点的标签中必须包含这里的每个键值对。最常见的用法的是一对键值对。
我们现在k8s-node2节点添加一个标签,如果需要覆盖已有标签可以添加--overwrite
参数
kubectl label node k8s-node2 gpu=true kubectl get node --show-labels
如果要取消这个标签,只需要把key的=value换成-即可,如下命令
kubectl label node k8s-node2 gpu-
然后我们重新编辑下刚才的test-schedule.yaml,使用NodeSelector使其调度到k8s-node2
vi test-schedule.yaml apiVersion: v1 kind: Pod metadata: name: test-schedule spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: http nodeSelector: gpu: "true" kubectl apply -f test-schedule.yaml kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-schedule 1/1 Running 0 26s 10.244.2.11 k8s-node2 <none> <none>
发现已经调度到了k8s-node2节点了,注意gpu的value要加上双引号
3.节点亲和性和反亲和性
这个可以查看我之前的笔记:https://sulao.cn/post/827.html