共计 8418 个字符,预计需要花费 22 分钟才能阅读完成。
丸趣 TV 小编今天带大家了解怎么进行 Scheduling Framework 应用实践,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着丸趣 TV 小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着丸趣 TV 小编一起深入学习“怎么进行 Scheduling Framework 应用实践”的知识吧。
Kubernetes 是目前最受欢迎的⾃动化容器管理平台,它提供了灵活的声明式容器编排、自动部署、资源调度等功能。Kube-Scheduler 作为 Kubernetes 的核心组件之一,主要负责整个集群资源的调度功能,根据特定的调度算法和策略,将 Pod 调度到最优的工作节点上面去,从而更加合理、充分地利用集群资源。但是随着 Kubernetes 部署的任务类型越来越多,原生 Kube-Scheduler 已经不能应对多样的调度需求:比如机器学习、深度学习训练任务对于协同调度功能的需求;高性能计算作业,基因计算工作流对于一些动态资源 GPU、网络、存储卷的动态资源绑定需求等。因此自定义 Kubernetes 调度器的需求愈发迫切,下面讨论了扩展 Kubernetes 调度程序的各种方法,然后使用目前最佳的扩展方式 Scheduling Framework,演示如何扩展 Scheduler。
01 自定义调度器的方式
02 Scheduling Framework 解析
如下图所示,调度框架提供了丰富的扩展点,在这幅图中,Filter 相当于之前 Predicate 预选模块, Score 相当于 Priority 优选模块,每一个扩展点模块都提供了接口,我们可以实现扩展点定义的接口来实现自己的调度逻辑,并将实现的插件注册到扩展点。
Scheduling Framework 在执行调度流程时,当运行到扩展点时,会调用我们注册的插件,通过执行自定义插件的策略,满足调度需求。此外,一个插件可以在多个扩展点注册,用以执行更复杂或有状态的任务。
Scheduling Framework 每次调度一个 Pod,都分为调度周期和绑定周期两部分来执行,调度周期为 Pod 选择一个节点,绑定周期则调用 Apiserver,用选好的 Node,更新 Pod 的 spec.nodeName 字段。调度周期和绑定周期统称为“Scheduling Context”(调度上下文)。调度周期是串行运行的,同一时间只能有一个 Pod 被调度,是线程安全的;而绑定周期因为需要访问 Apiserver 的接口,耗时较长,为了提高调度的效率,需要异步执行,即同一时间,可执行多个 bind 操作,是非线程安全的。
如果 Pod 被确定为不可调度或存在内部错误,那么调度周期或绑定周期将被中止。Pod 将返回队列并等待下一次重试。如果一个绑定周期被终止,它将触发 Reserve 插件中的 UnReserve 方法。
Scheduling Cycle 的扩展点 QueueSort
用于给调度队列排序,默认情况下,所有的 Pod 都会被放到一个队列中,此扩展用于对 Pod 的待调度队列进行排序,以决定先调度哪个 Pod,QueueSort 扩展本质上只需要实现一个方法 Less(Pod1, Pod2) 用于比较两个 Pod 谁更优先获得调度,同一时间点只能有一个 QueueSort 插件生效。
PreFilter
用于对 Pod 的信息进行预处理,或者检查一些集群或 Pod 必须满足的前提条件,比如 Pod 是否包含指定的 annotations 或 labels,如果 PreFilter 返回了 error,则调度过程终止。
Filter
用于排除那些不能运行该 Pod 的节点,对于每一个节点,调度器将按顺序执行 Filter 扩展,如果任何一个 Filter 将节点标记为不可选,则余下的 Filter 扩展将不会被执行。如果对默认调度器提供的预选规则不满意,可以在配置中禁用默认调度器的预选算法,在这个扩展点只执行自己自定义的过滤逻辑。Node 节点执行 Filter 策略是并发执行的,所以在同一调度周期中多次调用过滤器。
PostFilter
实现此扩展的插件是在 Filter 阶段之后被调用,仅当没有为 Pod 找到可行的节点时才调用。如果有任何 PostFilter 插件将节点标记为可调度节点,则后面的 PostFilter 插件就不会被调用了。一个典型的 PostFilter 实现是抢占,它试图通过抢占其他 Pod 来使 Pod 可调度。
PreScore
在预选后被调用,通常用来在 Score 之前进行一些信息生成或者记录日志和监控信息
Score
实现此扩展的插件为已通过过滤阶段的所有节点进行打分,调度器将针对每一个节点调用 Score 扩展。
NormalizeScore
在 NormalizeScore 阶段,调度器将会把每个 Score 扩展对具体某个节点的评分结果和该扩展的权重合并起来,作为最终评分结果,评分结果是一个范围内的整数。如果 Score 或 NormalizeScore 返回错误,则调度周期将中止。
Reserve
此扩展点为 Pod 预留的在要运行节点上的资源,目的是避免调度器在等待 Pod 与节点绑定的过程中调度新的 Pod 到节点上时,发生实际使用资源超出可用资源的情况。(因为绑定 Pod 到节点上是异步发生的)。这是调度过程的最后一个步骤,Pod 进入 Reserved 状态以后,要么在绑定失败时,触发 Unreserve 扩展,要么在绑定成功时,由 PostBind 扩展结束绑定过程。
Permit
Permit 扩展,发生在 Pod 使用 Reserve 插件预留资源之后, Bind 扩展点 bind 之前,主要有三种策略,批准、拒绝、等待。
1)approve(批准):当所有的 Permit 扩展都批准了 Pod 与节点的绑定,调度器将继续执行绑定过程
2)deny(拒绝):如果任何一个 Permit 扩展 deny 了 Pod 与节点的绑定,Pod 将被放回到待调度队列,此时将触发 Unreserve 扩展
3)wait(等待):如果一个 Permit 扩展返回了 wait,则 Pod 将保持在 Permit 阶段,直到被其他扩展 approve,如果超时事件发生,wait 状态变成 deny,Pod 将被放回到待调度队列,此时将触发 UnReserve 扩展
Binding Cycle 的扩展点 PreBind
扩展用于在 Pod 绑定之前执行某些逻辑。这个插件引入的原因,是有一些资源,是在不在调度 Pod 时,立即确定可用的节点的资源,所以调度程序需要确保,这些资源已经成功绑定到选定的节点后,才能将 Pod 调度到此节点。例如,PreBind 扩展可以将一个基于网络的数据卷挂载到节点上,以 Pod 可以使用。如果任何一个 PreBind 扩展返回错误,Pod 将被放回到待调度队列,此时将触发 Unreserve 扩展。
Bind
Bind 扩展会调用 apiserver 提供的接口,将 Pod 绑定到对应的节点上。
PostBind
PostBind 是一个信息扩展点。成功绑定 Pod 后,将调用 PostBind 插件,可用于清理关联的资源。
UnReserve
是一个通知性质的扩展,如果为 Pod 预留了资源,Pod 又在被绑定过程中被拒绝绑定,则 Unreserve 扩展将被调用。Unreserve 扩展应该释放已经为 Pod 预留的节点上的计算资源。在一个插件中,Reserve 扩展和 UnReserve 扩展应该成对出现。
03 使用 Scheduling Framework 自定义 Scheduler
自定义插件需要两个步骤:
1)实现插件的接口
2)注册插件并配置插件
3.1 实现插件的接口
这里我们实现 QueueSort 扩展点,先看看 QueueSort 扩展点定义的接口:
// QueueSortPlugin is an interface that must be implemented by QueueSort plugins.// These plugins are used to sort pods in the scheduling queue. Only one queue sort// plugin may be enabled at a time.type QueueSortPlugin interface { Plugin // Less are used to sort pods in the scheduling queue. Less(*QueuedPodInfo, *QueuedPodInfo) bool}
默认的调度器会优先调度优先级较高的 Pod , 其具体实现的方式是使用 QueueSort 这个插件,默认的实现,是对 Pod 的 Priority 值进行排序,但当优先级相同时,再比较 Pod 的 timestamp , timestamp 是 Pod 加入 queue 的时间。我们现在想先根据 Pod 的 Priority 值进行排序,当 Priority 值相同,再根据 Pod 的 QoS 类型进行排序,最后再根据 Pod 的 timestamp 排序。
Qos 类型如下:
1)Guaranteed : resources limits 和 requests 相等
2)Burstable : resources limits 和 requests 不相等
3)BestEffort : 未设置 resources limits 和 requests
具体是,Guaranteed 优先级 高于 Burstable,Burstable 优先级高于 BestEffort。
插件的实现,其实我们只需要实现 QueueSortPlugin 的 Less 方法:
package qosimport ( v1 k8s.io/api/core/v1 k8s.io/apimachinery/pkg/runtime k8s.io/kubernetes/pkg/api/v1/pod v1qos k8s.io/kubernetes/pkg/apis/core/v1/helper/qos framework k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1)// Name is the name of the plugin used in the plugin registry and configurations.const Name = QOSSort // Sort is a plugin that implements QoS class based sorting.type Sort struct{}var _ framework.QueueSortPlugin = Sort{}// Name returns name of the plugin.func (pl *Sort) Name() string { return Name}// Less is the function used by the activeQ heap algorithm to sort pods.// It sorts pods based on their priorities. When the priorities are equal, it uses// the Pod QoS classes to break the tie.func (*Sort) Less(pInfo1, pInfo2 *framework.PodInfo) bool { p1 := pod.GetPodPriority(pInfo1.Pod) p2 := pod.GetPodPriority(pInfo2.Pod) return (p1 p2) || (p1 == p2 compQOS(pInfo1.Pod, pInfo2.Pod)) || (p1 == p2 pInfo1.Timestamp.Before(pInfo2.Timestamp))}func compQOS(p1, p2 *v1.Pod) bool { p1QOS, p2QOS := v1qos.GetPodQOS(p1), v1qos.GetPodQOS(p2) if p1QOS == v1.PodQOSGuaranteed { return true } if p1QOS == v1.PodQOSBurstable { return p2QOS != v1.PodQOSGuaranteed } return p2QOS == v1.PodQOSBestEffort}// New initializes a new plugin and returns it.func New(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) { return Sort{}, nil}
注意:一个 Plugin 可以实现多个扩展点。即在一个 Plugin 中既可以实现 Filter,又可以实现 Score,也可以再实现 PreBind。
3.2 注册和配置插件
1)注册指向默认调度器中注册插件。
2)配置是通过配置来决定哪些插件需要被初始化。
3.2.1 在 Sheduler 中注册已经实现的 Qos 插件
func main() { rand.Seed(time.Now().UnixNano()) command := app.NewSchedulerCommand( app.WithPlugin(qos.Name, qos.New), ) logs.InitLogs() defer logs.FlushLogs() if err := command.Execute(); err != nil { os.Exit(1) }}
3.2.2 通过配置来使用 Qos 插件,并部署自定义调度器
1)配置
通过配置让 Sheduler 知道那些插件需要被初始化,如下面指定了 QueueSort 的插件 Qos,其他的扩展点的插件没有被指定,则都会 Kube-Scheduler 的默认的实现。可以看到,schedulerName 字段代表扩展的调度器名称,plugins 字段中各个扩展点的插件名称,enable 代表该扩展点关于运行你的插件。
apiVersion: v1kind: ConfigMapmetadata: name: scheduler-config3 namespace: kube-systemdata: scheduler-config.yaml: | apiVersion: kubescheduler.config.k8s.io/v1alpha1 kind: KubeSchedulerConfiguration schedulerName: qos-scheduler leaderElection: leaderElect: true lockObjectName: qos-scheduler lockObjectNamespace: kube-system plugins: queueSort: enabled: - name: QOSSort
2)接着为调度器创建 RBAC
kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: qos-crrules: - apiGroups: - * resources: - * verbs: - * - nonResourceURLs: - * verbs: - * ---apiVersion: v1kind: ServiceAccountmetadata: name: qos-sa namespace: kube-system---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: qos-crb namespace: kube-systemroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: qos-crsubjects: - kind: ServiceAccount name: qos-sa namespace: kube-system
3)配置调度器的 Deployment
apiVersion: apps/v1kind: Deploymentmetadata: name: qos-scheduler namespace: kube-system labels: component: qos-schedulerspec: replicas: 1 selector: matchLabels: component: qos-scheduler template: metadata: labels: component: qos-scheduler spec: imagePullSecrets: - name: hybrid-regsecret serviceAccount: qos-sa priorityClassName: system-cluster-critical volumes: - name: scheduler-config3 configMap: name: scheduler-config3 containers: - name: qos-scheduler image: hub.baidubce.com/kun/sxy/qos-scheduler:v1.0.0 imagePullPolicy: Always args: - /qos-sample-scheduler - --config=/scheduler/scheduler-config.yaml - --v=3 resources: requests: cpu: 50m volumeMounts: - name: scheduler-config3 mountPath: /scheduler
4)使用 kubectl apply 部署后,可以看到 qos-scheduler 已经启动了
$ kubectl get pods -n kube-systemNAME READY STATUS RESTARTS AGEqos-scheduler-79c767954f-225mr 1/1 Running 0 44m
5) 使用自定义调度器调度 Pod
在 Pod 中的 spec.schedulerName 上指定 qos-scheduler,自定义调度器将会为 Pod 执行调度逻辑。
apiVersion: v1kind: Podmetadata: name: test labels: app: testspec: schedulerName: qos-scheduler containers: - image: nginx name: nginx ports: - containerPort: 80
创建后,可以看到 Pod 已经被正常调度, 并启动成功。
$ kubectl get podsNAME READY STATUS RESTARTS AGEtest 1/1 Running 0 15s
04 使用调度框架的其他示例 4.1 Coscheduling(协同调度)
有时候,我们需要使用协同调度,类似于 Kube-batch 的功能(也称为“Gang 调度”)。Gang 调度允许同时安排一定数量的 Pod。如果 Gang 的所有成员不能同时调度,他们都不应该调度。Scheduling Framework 中的 Gang 调度可以使用“Permit”插件完成。
1)主调度线程逐个处理 Pod 并为它们预留节点,每个 Pod 都会调用准入阶段的 Gang 调度插件。
2) 当它发现 Pod 属于一个 Gang 时,它会检查 Gang 的属性。如果没有足够的成员定期或处于“等待”状态,该插件返回“等待”。
3) 当数字达到期望值时,处于等待状态的所有 Pod 均被批准并发送进行绑定。
4.2 Dynamic Resource Binding(动态资源绑定)
在调度 Pod 时, 有一些动态的资源比如 volumn,还不属于备选节点的资源,调度程序需要确保此类集群级资源绑定到选定的节点,然后才能将 pod 调度到有此类资源的节点的节点。可以使用 Scheduling Framework 的 PreBind 扩展点实现插件,完成这种动态资源的绑定功能。
感谢大家的阅读,以上就是“怎么进行 Scheduling Framework 应用实践”的全部内容了,学会的朋友赶紧操作起来吧。相信丸趣 TV 丸趣 TV 小编一定会给大家带来更优质的文章。谢谢大家对丸趣 TV 网站的支持!