Crane-Scheduler 基于真实工作负载的调度插件_当前最新
将服务部署在Kubernetes集群上是当今许多企业的首选方案,其能帮助企业自动化部署、弹性伸缩以及容错处理等工作,减少了人工操作和维护工作量,提高了服务的可靠性和稳定性,有效实现了降本增效。
原生 kubernetes 调度器只能基于资源的 resource request 进行调度,然而 Pod 的真实资源使用率,往往与其所申请资源的 request/limit 差异很大,导致集群负载不均的问题。crane-scheduler基于集群的真实负载数据构造了一个简单却有效的模型,作用于调度过程中的 Filter 与 Score 阶段,并提供了一种灵活的调度策略配置方式,从而有效缓解集群中资源负载不均问题,真正实现将本增效。
(资料图)
背景
将服务部署在Kubernetes集群上是当今许多企业的首选方案,其能帮助企业自动化部署、弹性伸缩以及容错处理等工作,减少了人工操作和维护工作量,提高了服务的可靠性和稳定性,有效实现了降本增效。但kubernetes 的原生调度器只能通过资源请求来调度 pod,这很容易造成一系列负载不均的问题:
集群中的部分节点,资源的真实使用率远低于 resource request,却没有被调度更多的 Pod,这造成了比较大的资源浪费。而集群中的另外一些节点,其资源的真实使用率事实上已经过载,却无法为调度器所感知到,这极大可能影响到业务的稳定性。这些无疑都与企业上云的最初目的相悖,为业务投入了足够的资源,却没有达到理想的效果。crane-scheduler打破了资源 resource request 与真实使用率之间的鸿沟,着力于调度层面,让调度器直接基于真实使用率进行调度,使用最大化的同时排除了稳定性的后顾之忧,真正实现降本增效。
Kubernetes 调度框架
Kubernetes官方提供了可插拔架构的调度框架,能够进一步扩展Kubernetes调度器,下图展示了调度框架中的调度上下文及其中的扩展点,一个扩展可以注册多个扩展点,以便可以执行更复杂的有状态的任务。
图1 Pod 调度流程
Sort – 用于对 Pod 的待调度队列进行排序,以决定先调度哪个 Pod。Pre-filter – 用于对 Pod 的信息进行预处理。Filter – 用于排除那些不能运行该 Pod 的节点。Post-filter – 一个通知类型的扩展点,更新内部状态,或者产生日志。Scoring – 用于为所有可选节点进行打分。Normalize scoring – 在调度器对节点进行最终排序之前修改每个节点的评分结果。Reserve – 使用该扩展点获得节点上为 Pod 预留的资源,该事件发生在调度器将 Pod 绑定到节点前。Permit – 用于阻止或者延迟 Pod 与节点的绑定。Pre-bind – 用于在 Pod 绑定之前执行某些逻辑。Bind – 用于将 Pod 绑定到节点上。Post-bind – 是一个通知性质的扩展。Unreserve – 如果为 Pod 预留资源,又在被绑定过程中被拒绝绑定,则将被调用。对于调度框架插件的启用或者禁用,我们同样可以使用上面的KubeSchedulerConfiguration资源对象来进行配置。下面的例子中的配置启用了一个实现了filter和scoring扩展点的插件,并且禁用了另外一个插件,同时为插件 foo 提供了一些配置信息:
apiVersion: kubescheduler.config.k8s.io/v1alpha1kind: KubeSchedulerConfiguration...plugins: filter: enabled: - name: foo - name: bar disabled: - name: baz scoring: enabled: - name: foo disabled: - name: bazpluginConfig:- name: foo args: > foo插件可以解析的任意内容
扩展的调用顺序如下:
如果某个扩展点没有配置对应的扩展,调度框架将使用默认插件中的扩展。如果为某个扩展点配置且激活了扩展,则调度框架将先调用默认插件的扩展,再调用配置中的扩展。默认插件的扩展始终被最先调用,然后按照KubeSchedulerConfiguration中扩展的激活enabled顺序逐个调用扩展点的扩展。可以先禁用默认插件的扩展,然后在enabled列表中的某个位置激活默认插件的扩展,这种做法可以改变默认插件的扩展被调用时的顺序。Kubernetes 调度插件demo: https://github.com/cnych/sample-scheduler-framework。
crane-scheduler 设计与实现
总体架构
图2 Crane-scheduler 总体架构
动态调度器总体架构如上图所示,主要有两个组件组成:
Node-annotator定期从 Prometheus 拉取数据,并以注释的形式在节点上用时间戳标记它们。Dynamic plugin直接从节点的注释中读取负载数据,过滤并基于简单的算法对候选节点进行评分。同时动态调度器提供了一个默认值调度策略并支持用户自定义策略。默认策略依赖于以下指标:
cpu_usage_avg_5mcpu_usage_max_avg_1hcpu_usage_max_avg_1dmem_usage_avg_5mmem_usage_max_avg_1hmem_usage_max_avg_1d
在调度的Filter阶段,如果该节点的实际使用率大于上述任一指标的阈值,则该节点将被过滤。而在Score阶段,最终得分是这些指标值的加权和。
在生产集群中,可能会频繁出现调度热点,因为创建 Pod 后节点的负载不能立即增加。因此定义了一个额外的指标,名为Hot Value,表示节点最近几次的调度频率。并且节点的最终优先级是最终得分减去Hot Value。
关键代码实现
Node-annotation:定期从 Prometheus 拉取数据,并以注释的形式在节点上用时间戳标记它们。// /pkg/controller/annotator/node.gofunc (n *nodeController) syncNode(key string) (bool, error) { startTime := time.Now() defer func() { klog.Infof("Finished syncing node event %q (%v)", key, time.Since(startTime)) }() // 获取 nodeName, metricName nodeName, metricName, err := splitMetaKeyWithMetricName(key) // 通过 nodeName 获取 node 的具体信息 node, err := n.nodeLister.Get(nodeName) // 通过 nodeIP 或者 nodeName 获取并更新 node 的监控指标 err = annotateNodeLoad(n.promClient, n.kubeClient, node, metricName) // 获取 node hotVaule,并更新 err = annotateNodeHotValue(n.kubeClient, n.bindingRecords, node, n.policy) return true, nil}func annotateNodeLoad(promClient prom.PromClient, kubeClient clientset.Interface, node *v1.Node, key string) error { // 通过 nodeIp 查询 value, err := promClient.QueryByNodeIP(key, getNodeInternalIP(node)) if err == nil && len(value) > 0 { return patchNodeAnnotation(kubeClient, node, key, value) } // 通过 nodeName 查询 value, err = promClient.QueryByNodeName(key, getNodeName(node)) if err == nil && len(value) > 0 { return patchNodeAnnotation(kubeClient, node, key, value) } return fmt.Errorf("failed to get data %s{%s=%s}: %v", key, node.Name, value, err)}
Dynamic plugin:Dynamic plugin 修改 filter 和 score 阶段。// /pkg/plugins/dynamic/plugins.go// Filter - 检查一个节点的实际负载是否过高func (ds *DynamicScheduler) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status { node := nodeInfo.Node() // 读取 nodeAnnotation, nodeName nodeAnnotations, nodeName := nodeInfo.Node().Annotations, nodeInfo.Node().Name // filter - 过滤 for _, policy := range ds.schedulerPolicy.Spec.Predicate { // 获取采样时间 activeDuration, err := getActiveDuration(ds.schedulerPolicy.Spec.SyncPeriod, policy.Name) // 根据指标,判断 node 是否过载 if isOverLoad(nodeName, nodeAnnotations, policy, activeDuration) { return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("Load[%s] of node[%s] is too high", policy.Name, nodeName)) } } return framework.NewStatus(framework.Success, "")}// Score - 它从节点注释中获取度量数据,并支持实际资源使用最少的节点func (ds *DynamicScheduler) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) { // 通过 nodeName,获取 node 具体信息 nodeInfo, err := ds.handle.SnapshotSharedLister().NodeInfos().Get(nodeName) node := nodeInfo.Node() nodeAnnotations := node.Annotations // 计算得分和 hotValue score, hotValue := getNodeScore(node.Name, nodeAnnotations, ds.schedulerPolicy.Spec), getNodeHotValue(node) score = score - int(hotValue*10) // 计算总得分 finalScore finalScore := utils.NormalizeScore(int64(score), framework.MaxNodeScore, framework.MinNodeScore) return finalScore, nil}
使用流程
配置 prometheus 监测规则
syncPolicy: 用户可以自定义负载数据的类型与拉取周期。predicate: Filter 策略,若候选节点的当前负载数据超过了任一所配置的指标阈值,则这个节点将会被过滤。priority:在 Score 策略中配置相关指标的权重,候选节点的最终得分为不同指标得分的加权和。hotValue:定义调度热点规则,最终节点的 Priority 为上一小节中的 Score 减去 Hot Value。apiVersion: scheduler.policy.crane.io/v1alpha1kind: DynamicSchedulerPolicyspec: syncPolicy: ##cpu usage - name: cpu_usage_avg_5m period: 3m - name: cpu_usage_max_avg_1h period: 15m - name: cpu_usage_max_avg_1d period: 3h ##memory usage - name: mem_usage_avg_5m period: 3m - name: mem_usage_max_avg_1h period: 15m - name: mem_usage_max_avg_1d period: 3h predicate: ##cpu usage - name: cpu_usage_avg_5m maxLimitPecent: 0.65 - name: cpu_usage_max_avg_1h maxLimitPecent: 0.75 ##memory usage - name: mem_usage_avg_5m maxLimitPecent: 0.65 - name: mem_usage_max_avg_1h maxLimitPecent: 0.75 priority: ##cpu usage - name: cpu_usage_avg_5m weight: 0.2 - name: cpu_usage_max_avg_1h weight: 0.3 - name: cpu_usage_max_avg_1d weight: 0.5 ##memory usage - name: mem_usage_avg_5m weight: 0.2 - name: mem_usage_max_avg_1h weight: 0.3 - name: mem_usage_max_avg_1d weight: 0.5 hotValue: - timeRange: 5m count: 5 - timeRange: 1m count: 2
使用 crane-scheduler
这里有两种方式可供选择:
作为k8s原生调度器之外的第二个调度器。替代k8s原生调度器成为默认的调度器。作为k8s原生调度器之外的第二个调度器:在 podspec.schedulerName指定 crane scheduler。
apiVersion: apps/v1kind: Deploymentmetadata: name: cpu-stressspec: selector: matchLabels: app: cpu-stress replicas: 1 template: metadata: labels: app: cpu-stress spec: schedulerName: crane-scheduler hostNetwork: true tolerations: - key: node.kubernetes.io/network-unavailable operator: Exists effect: NoSchedule containers: - name: stress image: docker.io/gocrane/stress:latest command: ["stress", "-c", "1"] resources: requests: memory: "1Gi" cpu: "1" limits: memory: "1Gi" cpu: "1"
替代k8s原生调度器成为默认的调度器
修改kube调度器的配置文件(scheduler config.yaml)以启用动态调度器插件并配置插件参数:apiVersion: kubescheduler.config.k8s.io/v1beta2kind: KubeSchedulerConfiguration...profiles:- schedulerName: default-scheduler plugins: filter: enabled: - name: Dynamic score: enabled: - name: Dynamic weight: 3 pluginConfig: - name: Dynamic args: policyConfigPath: /etc/kubernetes/policy.yaml...
/etc/kubernetes/policy.yaml就是 4.3.1 中的DynamicSchedulerPolicy资源对象。
修改kube-scheduler.yaml,并将kube调度器映像替换为Crane schedule。... image: docker.io/gocrane/crane-scheduler:0.0.23...
安装 crane-scheduler-controller。kubectl apply ./deploy/controller/rbac.yaml && kubectl apply -f ./deploy/controller/deployment.yaml
真实环境测试
crane-sheduler 会将监控指标数据写在 node annotation 上。
node annotation
内存型服务测试
测试服务单副本实际占用 2C 20G ,申请资源 5C 40G。
memory
k8s默认调度器结果(%)。默认调度器根据 资源申请值request调度服务,且节点间分布不均衡。
当副本数到达12 个时,默认调度器出现了资源分配严重不均的情况。
crane-schedule调度器结果(%)。当启动11个服务的时候,node03中的mem_usage_avg_5m指标过高,禁止调度:
CPU型服务测试
测试服务单副本实际占用 8C 8G ,申请资源 12C 12G。
k8s默认调度器结果(%)。当启动9个服务的时候,出现 Insufficient cpu 的情况:
crane-schedule调度器结果(%)。当启动8服务的时候,node03中的mem_usage_avg_5m指标过高,禁止调度:
参考文献
http://kubernetes.p2hp.com/docs/。https://www.qikqiak.com/post/custom-kube-scheduler/。https://gocrane.io/zh-cn/docs/tutorials/dynamic-scheduler-plugin/。https://github.com/gocrane/crane-scheduler。https://blog.51cto.com/u_14120339/5363877。关键词:
您可能也感兴趣:
为您推荐
机票里面包含保险吗?飞机保险有必要买吗?
辽宁扩大缓缴社保费政策实施范围 补缴期间免收滞纳金
近十年财险业务快速发展 财产保险业风险保障水平不提升
排行
最近更新
- Crane-Scheduler 基于真实工作负载的调度插件_当前最新
- 千金药业: 公司参股的投资基金目前运营情况正常,暂无应披露...
- 山高环能:关于公司相关业务的毛利率请关注公司披露的《2022...
- 环球快资讯:共探 app 全球新增长,AppStare 与您相约 2023 ChinaJoy BTOB
- 石龟土茯苓煲鸡做法?
- 农村丧事为啥在棺材上放公鸡?有什么习俗讲究?
- 票子鸡的做法?|全球热门
- 孕妇感冒喝红糖水有什么好处 热文
- 牛肉汤怎么样做更好吃
- 车主保养车辆时发动机受损 法院认定汽车服务公司负全责_快资讯
- 信也科技宣布成立消费者权益保护委员会
- 合合信息“启信慧眼”赋能银行精准服务科创企业 视讯
- 黄金后面怎么走
- 贝博儿新纯羊•用生羊乳重新定义纯羊_世界微资讯
- 拉步甲:炫酷“外衣”真“拉风” 世界报道
- 世界热点!志愿服务展风采
- 大宗交易:永贵电器成交2641.2万元,折价8.99%(05-16)
- 银屑病治疗多项临床数据出炉 新药喜开悦(利生奇组单抗)表...
- 走出国门,全国产 AR 如何打动全球企业?
- 破译衰老密码,普凡缇水光胶原开启科学护肤新思路
- 文章不写半句空 文学名词_文章不写半句空
- 立可尿博客_立可尿
- 美国启动通用mRNA流感疫苗临床 有望提供长效保护无需年年接种
- 零碳科技,智美生活|固德威十年户用旗舰SDT G3全球上新
- 金联创天然橡胶基本面数据分析:5月或延续弱势姿态
- 菠菜鸡蛋火烧的做法?
- 当前聚焦:黑木抄鸡蛋能加菠菜吗?
- 坐月子能吃水果吗要注意什么
- 菠菜炒粉丝的做法?|天天最新
- 六种炒合菜的做法? 焦点关注