网页客服,欢迎咨询

汽车之家AutoKH混部技术实践

2025-01-06 20:10:01 43
  • 收藏
  • 管理
    总篇129篇 2022年第4篇前言随着之家基础设施规模不断扩大,资源管理面临新的挑战。通过调研Google Borg、阿里Fuxi、腾讯Caelus,同时结合大数据和容器是之家第一、二大的基础设施特点,经过探索和实践,发现混部技术在提升资源利用率有显著效果。何为混部简单说就是物理资源复用的一种方式,在相同物理资源上,通过技术手段,调配整合不同形式的资源,达到提升资源物理利用率的目的。当然保障服务稳定,是个大前提,需要对混部的业务进行严格的资源评估和控制,其中在线服务和离线任务这个组合尤为适合。AutoKH简介AutoKH就是一套基于K8S&Hadoop的离在线业务混部技术方案,在保证业务应用SLA的前提下,通过资源调度、优先级、隔离、退水等控制手段 ,充分利用相同节点上的CPU、内存、IO等资源,提升整体资源利用率。简单说就是物理资源复用的一种方式,在相同物理资源上,通过技术手段,调配整合不同形式的资源,达到提升资源物理利用率的目的。当然保障服务稳定,是个大前提,需要对混部的业务进行严格的资源评估和控制,其中在线服务和离线任务这个组合尤为适合。AutoKH是Autohome Kubernetes&Hadoop的缩写,这两大基础设施都拥有强大的资源管理能力,而资源管理极其重要的目标就是提升利用率、降低成本。?AutoKH架构AutoKH设计理念遵循云原生,零侵入K8S,兼容Hadoop当前生态,模块化设计,业务无感知,设计如下:1.两大核心入口,之家云A-One流水线发布在线应用,调度平台发布离线任务2.YarnOperator负责NM POD的创建、修改、扩容、以及注册和重载yarn resource manager3.cronhpa controller实现潮汐调度(后面有具体介绍),定时调度在线应用和离线任务资源4.核心组件ResourceController和KHAgent相互配合,在不重启离线NM实例条件下,实时动态CPU、内存资源管控,实现在/离线整体资源动态调度5.核心组件YarnScaleController watch ResourceController的kh对象,获取离线任务实际可用资源等信息,对离线任务进行动态调度6.Offline POD为k8s集群NM POD,承载离线任务,每个混部节点启动一个,以STS方式启动,配置节点反亲和,使用OnDelete更新策略YarnOperator一开始我们使用golang基于operator-sdk来开发operator,对于某些运维人员来说使用golang开发存在一定的学习成本,后来我们发现了kopf,全称Kubernetes?Operator Pythonic Framework (地址https://github.com /nolar/kopf),基于python,这样上手就很快了,Kopf用法简单但是功能很全,特点如下:1、一个python文件,就可以处理全部逻辑2、开箱即用,完成部署编排的创建、删除、更新、以及定时触发、守护机制,引入对应的装饰器即可3、支持k8s所以原生对象,集成业务逻辑很方便4、依赖crd和rbac单独审核部署,有利于集群安全5、文档比较友好,例子很全比如使用如下方法,即可创建一个StatefulSet和Services:@kopf.on.create("crd.autohome.com.cn","v1","nodemanagers")def?create_fn(body,?spec,?**kwargs):#?获取自定义属性的objectname?=?body["metadata"]["name"]namespace?=?body["metadata"]["namespace"]limit_cpu?=?spec["limit_cpu"]limit_memory?=?spec["limit_memory"]request_cpu?=?spec["request_cpu"]request_memory?=?spec["request_memory"]nm_replica?=?spec["nm_replica"]image?=?"xxxx.autohome.com.cn/bigdata/{}".format(spec["image"])nodemanager_svc?=?nodemanager_svc_yaml(namespace,?name)nodemanager_sts?=?nodemanager_sts_yaml(namespace,?name,?nm_replica,?limit_cpu,?limit_memory,?request_cpu,?request_memory,?image)kopf.adopt(nodemanager_svc,?owner=body)kopf.adopt(nodemanager_sts,?owner=body)api_core?=?kubernetes.client.CoreV1Api()api_apps?=?kubernetes.client.AppsV1Api()try:#?创建nodemanager?stsobj?=?api_core.create_namespaced_service(namespace,?nodemanager_svc)logging.info(f"Service?{obj.metadata.name}?created")obj?=?api_apps.create_namespaced_stateful_set(namespace,?nodemanager_sts)logging.info(f"Statefulset?{obj.metadata.name}?created")except?Exception?as?e:logging.error("Exception?when?calling?AppsV1Api -> create_namespaced_stateful_set:%s\n" % e)CronHPACronHPA是一种比较简单的混部方式,也是我们最初开始尝试的混部模式,我们称之为潮汐调度。以客服语音服务为例,工作时段09:00 ~ 22:00,YARN离线任务高峰时间段23:00~08:00,其他时段较低,类似这种在资源使用定期出现高峰和低谷场景,我们引入了CronHPA,对资源进行潮汐调度,定时自动实现提前扩缩 POD。结合客服坐席时段,相关业务应用每天08:00提前开始水平扩容POD,分配资源;23:00回收资源,形成固定周期。回收的资源可以分配给Hadoop进行离线务调度。部署CronHPA,需要创建CRD、RBAC、Controller,具体参看https://github.com/AliyunContainerService/kubernetes-cronhpa-controller ,如下是配置实例:主要配置说明:jobs:任务设定部分,支持多个任务并行- name:任务名称scale-down,代表缩容,name在该CronHPA声明中唯一schedule:0 0 23 * * * 代表任务每天23:00:00执行,配置和crontab类似,注意两点:1、创建时请严格按照cron格式说明进行设置,否则创建失败。?2、从秒开始,最前面的0,代表00秒targetSize:是本次任务需要缩容到0第二个任务scale-up,是每天08:00:00将该Deployment副本从0扩容到7scaleTargetRef:kind:指定要扩缩的工作负载对象Deployment,当然也可以是StatefulSetname: 就是要具体的Deployment名称ResourceControler & KH-Agent1、核心组件ResourceController,提供2个资源管理的CRD对象:a)?kh对象,用于离线节点资源CPU、MEM、资源回收延迟Time的自定义配置。b)?rcs对象,用于KHAgent资源上报,同时提供给YarnScaleController拉取资源信息的接口。2、核心组件KHAgent,以Daemonset编排形式启动在每个混部节点上,功能如下:a)?调用节点Docker SDK、k8s prometheus获取实际可用资源信息,上报给ResourceController的kh对象。b) 调用ResourceController的rcs对象,获取节点自定义配置,如果rcs中没有配置时,使用configmap的默认配置,rcs配置级别优先。c)?资源动态管理,根据资源阈值及当前节点实际CPU、MEM使用率,实时计算出离线资源期望、上限,根据设定阈值(CPU 50% MEM 80%)和容忍时间进行资源回收。YarnScaleControllerYarnScaleController作为连接k8s与Yarn的资源动态调整控制器,它每隔一段时间从k8s获取混部节点的资源变动信息,根据变动向Yarn发送节点资源变动指令,Yarn收到资源变动指令后,将变动应用到所有队列。如果是资源扩容,对NodeManager上已经运行的Task没有影响,如果是资源缩容,那么就需要决定回收节点上运行的Task,我们的策略是将等待回收的Task按照队列优先级、以及是否是AppMaster进行排序,并且最长等待3分钟(动态可调),若3分钟内Task没有运行完毕就会启动Kill任务机制,直到NodeManager上运行的Task资源满足最新的资源变动,在保证资源回收时,给离线任务一定的缓冲时间,同时也可以规避资源抖动造成离线任务频繁启停。环境整合不得不说,再将K8S和NM/DN机器打通时,踩了一些坑。主因是两大集群基础环境不一致,两套集群的机器基于各自标准,流水线化安装,系统、硬件也都是根据各自特性进行优化的,其中有个典型案例,就是kernel mod缺失,导致了一系列问题。1、calico-node未能就绪,报错如下:日志报错:Get http://localhost:9099/liveness: dial tcp [::1]:9099: connect: connection refused? event报错:Readiness probe failed: Threshold time for?bird?readiness?check:30s2、ipvs模式未启用,ipvsadm -ln命令执行条目不对,仅显示如下几行:?IP?Virtual?Server?version?1.2.1?(size=4096)Prot?LocalAddress:Port?Scheduler?Flags->?RemoteAddress:Port?Forward?Weight?ActiveConn?InActConn3、iptables 进程导致load average高,执行iptables -nvL -t nat 表被锁,报错:Another app is currently holding the xtables lock. Perhaps you want to use the -w option? 获取结果中 Chain KUBE-SEP-?和 Chain KUBE-SEP-?开头链总数很大(和集群规模相关),正常节点没有异常节点:#?iptables?-t?nat?-nvL?|?grep?Chain|?awk?'{print?$1,$2}'?|?awk?-F?'-'?'{print?$1,$2}'?|?sort?|?uniq?-c6105?Chain?KUBE?SEP2211?Chain?KUBE?SVC正常节点:2?Chain?cali?fip1?Chain?cali?nat1?Chain?cali?OUTPUT1?Chain?cali?POSTROUTING1?Chain?cali?PREROUTING1?Chain?INPUT1?Chain?KUBE?FIREWALL1?Chain?KUBE?LOAD2?Chain?KUBE?MARK1?Chain?KUBE?NODE1?Chain?KUBE?POSTROUTING1?Chain?KUBE?SERVICES1?Chain?OUTPUT1?Chain?POSTROUTING1?Chain?PREROUTING原因及解决方法:1、hadoop节点统一采用3.10内核,没有加载ip_vs如下模块,加载后解决?modprobe?--?ip_vs?&?modprobe?--?ip_vs_rr?&?modprobe?--?ip_vs_wrr?&?modprobe?--?ip_vs_sh?&?modprobe?--?nf_conntrack_ipv4?(需要放到开机启动里面)2、iptables链异常是因为模块未加载(日志截图如下),kubeproxy自动将ipvs模式改为了iptables模式,该模式性能差约30% kube-proxy报错:Failed to execute iptables-restore:failed?to?acquire?new?iptables?lock:?timed?out?waiting?for?the condition,先执行:?iptables?-F?;?iptables?-t?nat?-F?;?iptables?-t?mangle?-F?;?iptables?-X?;?iptables?-t?raw?-F?;iptables?-X?-t?nat?;?iptables?-X?-t?mangle;?iptables?-X?-t?raw,加载上述模块后,重启该节点kube-proxy?pod解决3、calico-node探活失败,也是因为iptable死锁造成?calico-node到calico-typh不通导致稳定性保障1、资源优先级执行在线业务资源优先原则,基于k8s Priority Classes、Cgroup cpuset、share、Quota等策略,结合自研控制器ResourceController、YarnScaleController、KH-Agent资源调度,在保障在线业务资源即时性的同时,做到离线业务及时缓解资源饥渴状态。1、calico-node未能就绪,报错如下:apiVersion:?scheduling.k8s.io/v1kind:?PriorityClassmetadata:name:?high-priority-onlinevalue:?2000000globalDefault:?falsedescription:?"for?high?priority?pod"---apiVersion:?scheduling.k8s.io/v1kind:?PriorityClassmetadata:name:?low-priority-offlinevalue:?1000000globalDefault:?falsedescription:?"for?low?priority?pod"PriorityClass 对象可以设置任何小于或等于 10 亿的 32 位整数值,值越大,优先级越高。high-priority-online用于在线业务pod,优先级高于离线业务pod所用策略low-priority-offline,当资源冲突时,优先调度在线业务pod。2、CPU绑核、独占、拓扑感知、降噪对于CPU节流、上下文切换、超线程敏感,或需要更高性能,低延迟等需求的应用来说,单独的resources ?limit隔离是不够的,需要进行CPU绑定、独占及防止干扰。基于k8s CPUManager(k8s v1.12 +)、结合numa Topology Manager(k8s v1.16 +)、以及full-pcpus-only选项(k8s v1.22 +),来规避上述问题。a). CPU Manager:通过 kubelet 参数 --cpu-manager-policy 来指定。支持两种策略:none: 默认策略,相当于没有启用cgroup cpuset的能力static: 允许为节点上具有某些资源特征的 pod 赋予增强的 CPU 亲和性和独占性,即启用cgroup cpusetstatic 策略针对具有整数型 CPU requests和limit的Guaranteed Pod(request:limit 为 1:1)调度到开启了cpu-manager的节点时,相应的 CPU 会被从共享池中移除,并放置到容器的 cpuset 中,即完成CPU绑核。Step1.配置前,查看当前节点状态[root@k8s?~]#?cat?/data/k8s/lib/kubelet/cpu_manager_state?{"policyName":"none","defaultCpuSet":"","checksum":3242152201}Step2.启动Guaranteed pod(request : limit是1:1)kubectl?run?centos7-debug?--image=thub.autohome.com.cn/zhanganning/centos7-debug:0.5--requests=cpu=1,memory=1Gi?--limits=cpu=1,memory=1GiStep3. 查看绑核情况[root@k8s?~]#?docker?inspect?cf1801ad494d?|?grep?-i?pid"Pid":?38589,[root@k8s?~]#?taskset?-c?-p?38589pid?38589's?current?affinity?list:?0-47Step4.? 配置绑核--cpu-manager-policy=static?到?/etc/sysconfig/kubelet,重启kubeletStep5. 等kubelet重启成功后,查看调度该节点上centos7-debug的pod效果[root@k8s?~]#?#?cat?/data/k8s/lib/kubelet/cpu_manager_state?{"policyName":"static","defaultCpuSet":"0-1,3-47","entries":{"487fcc6f1b457ea2e60e4b211de66de2bbc33eb45d75ce7a21d9eec76afa8626":"2"}[root@k8s?~]#?taskset?-c?-p?52977pid?52977's?current?affinity?list:?2该pod已经绑定到了逻辑核2,绑核生效。Step6.? 扩容centos7-debug的deployment的实例数到2[root@k8s?~]#?kubectl?scale?deployment?centos7-debug?--replicas=2[root@k8s?~]#?cat?/data/k8s/lib/kubelet/cpu_manager_state?{"policyName":"static","defaultCpuSet":"0-1,3-25,27-47","entries":{"2066655bdcd9b878fb85bfcfafb0eb8e00c769930f5547c116f2a6fa18f83dae":"26","487fcc6f1b457ea2e60e4b211de66de2bbc33eb45d75ce7a21d9eec76afa8626":"2"},"checksum":439515827}发现扩容的pod已经绑定到了逻辑核26,绑核生效,且独占逻辑核2和22。Step7. 启动一个Burstable pod,会调度其他逻辑核,验证非绑核[root@k8s?~]#?kubectl?run?centos7-debug-1?--image=thub.autohome.com.cn/zhanganning/centos7-debug:0.5?--requests=cpu=1,memory=1Gi?--limits=cpu=2,memory=2Gi[root@k8s?~]#?docker?inspect?037af04d463e?|?grep?-i?pid"Pid":?43749,[root@k8s?~]#?taskset?-c?-p?43749pid?43749's?current?affinity?list:?0,1,3-25,27-47?[root@k8s?~]#?cat?/data/k8s/lib/kubelet/cpu_manager_state?{"policyName":"static","defaultCpuSet":"0-1,3-25,27-47","entries":{"2066655bdcd9b878fb85bfcfafb0eb8e00c769930f5547c116f2a6fa18f83dae":"26","487fcc6f1b457ea2e60e4b211de66de2bbc33eb45d75ce7a21d75ce7a21d9eec76afa8626":"2"},"checksum":439515827}b). Topology Manager:首先说下NUMA(Non-Uniform Memory Access,非统一内存)架构,现代计算机的CPU架构多采用该架构。NUMA就是将CPU资源分开,以node 为单位进行分组,每个node都有着独有的CPU、MEM等资源,当一个NUMA节点内的资源相交互时,性能将会有很大的提升;但是,如果是两个NUMA节点之间的资源交互,将会变得很慢。如下测试可以发现,性能差距达到30%以上。开启拓扑感知,需要Kubernetes Version >= 1.16。#?numactl?--cpubind=0?--membind=1?dd?if=/dev/zero?of=/dev/shm/A?bs=1M?count=10241024+0?records?in1024+0?records?out1073741824?bytes?(1.1?GB,?1.0?GiB)?copied,?0.620549?s,?1.7?GB/s#?numactl?--cpubind=0?--membind=0?dd?if=/dev/zero?of=/dev/shm/A?bs=1M?count=10241024+0?records?in1024+0?records?out1073741824?bytes?(1.1?GB,?1.0?GiB)?copied,?0.46759?s,?2.3?GB/sStep1. 配置前,查看分配27 core的deployment启动效果[root@automan?~]#?cat?/var/lib/kubelet/cpu_manager_state|?jq?|?grep?nginx-1?|?awk?-F?'"'?'{print?$4}'?|?sort?-n1-5,7,9,11,13,15,17,19,21,23,25-27,29,31,33,35,37,39,41,43,45,47[root@automan?~]#?lscpu?#主要内容CPU(s):48On-line?CPU(s)?list:?0-47Thread(s)?per?core:2Core(s)?per?socket:12Socket(s):?2NUMA?node(s):2NUMA?node0?CPU(s):?0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46NUMA?node1?CPU(s):?1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47发现问题,逻辑core 2、4、26发生越界,跨numa node了,存在性能损耗风险。Step2. 增加配置--topology-manager-policy=single-numa-node后,查看效果[root@automan?~]#?kubectldescribe?pod?nginx-1-7856896f67-kkjrwName:?nginx-1-7856896f67-kkjrwStatus:?FailedEvents:Type?Reason?Age?From?Message----?------?--------?-------Normal?Scheduled33s?default-schedulerSuccessfully?assigned?default/nginx-1-7856896f67-kkjrw?to?automan-testWarningTopologyAffinityError33s?kubeletResources?cannot?be?allocated?with?Topology?localityWarningTopologyAffinityError4skubelet Resources cannot be allocated with Topologylocality发现之前可以启动成功pod,现在变成Failed状态,且报错Resources cannot be allocated with Topology locality,因为该pod没有找到最优的TopologyHint(涉及NUMA node个数是1),且为了验证效果将该deployment的nodeselecter指定这1个节点,则pod将无法启动成功,报TopologyAffinityError的事件,当然实际生产中不会发生这个情况,会调度到满足条件的worker上。c).?降噪:计算机领域有一个术语叫坏邻居(noisy neighbor), 用来描述云计算环境中, 有些客户应用大量抢占共享资源的行为,比如CPU、网络带宽, 磁盘I/O以及其他资源;坏邻居效应, 会造成应用性能下降、抖动的困扰。针对该问题,可在cpu manger static策略下,配置开启full-pcpus-only策略选项来规避。开启后,仅当所有容器的CPU请求都可以通过分配完整的物理内核来满足时,kubelet才会接纳pod。如果pod没有通过准入,它将被置于Failed状态并显示消息SMTAlignmentError。Step1. 增加配置前,分配3 逻辑core的pod启动正常#?cat?/var/lib/kubelet/cpu_manager_state|?jq?|?grep?nginx-1?|?awk?-F?'"'?'{print?$4}'?|?sort?-n2,4,26Step2. 增加配置后,查看pod状态--feature-gates=CPUManager=true,CPUManagerPolicyOptions=true?--cpu-manager-policy=static?--cpu-manager-policy-options=full-pcpus-only=true?--cpu-manager-reconcile-period=5s#?kubectldescribe?pod?nginx-1-6779c47979-dv5dqStatus:?FailedEvents:Type?Reason?Age?From?Message----?------?--------?-------Normal?Scheduled16s?default-schedulerSuccessfully?assigned?default/nginx-1-6779c47979-dv5dq?to?automan-testWarningSMTAlignmentError16s?kubeletSMT?Alignment?Error:?requested?3?cpus?not?multiplecpus?per?core?=?2发现分配3个逻辑核的pod变成了Failed状态,且发生SMTAlignmentError的事件,因为在开启了SMT的系统上,分配了单数个数逻辑核的pod,导致总有一个逻辑核无法占满1个物理核,不满足full-pcpus-only策略条件,造成pod同样变成Failed状态。Step3. 分配20(偶数个数)逻辑核pod,启动正常,独占10个物理core#?cat?/var/lib/kubelet/cpu_manager_state|?jq?|?grep?nginx-1?|?awk?-F?'"'?'{print?$4}'?|?sort?-n2,4,6,8,10,12,14,16,18,20,26,28,30,32,34,36,38,40,42,44#?lscpuCPU(s):48On-line?CPU(s)?list:?0-47Thread(s)?per?core:2Core(s)?per?socket:12Socket(s):?2NUMA?node(s):2NUMA?node0?CPU(s):?0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46NUMA?node1?CPU(s):?1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47#?cat?/proc/cpuinfo?|?grep?-E?'core?id|processor|physical?id'?|sed?'N;N;s/\n/?/g'|?grep?'0?core'?|?sort?-k?10processor?:?0?physical?id?:?0?core?id:?0processor?:?24?physical?id?:?0?core?id:?0processor?:?28?physical?id?:?0?core?id:?1processor?:?4?physical?id?:?0?core?id:?1processor?:?20?physical?id?:?0?core?id:?10processor?:?44?physical?id?:?0?core?id:?10processor?:?22?physical?id?:?0?core?id:?11processor?:?46?physical?id?:?0?core?id:?11processor?:?18?physical?id?:?0?core?id:?12processor?:?42?physical?id?:?0?core?id:?12processor?:?14?physical?id?:?0?core?id:?13processor?:?38?physical?id?:?0?core?id:?13processor?:?32?physical?id?:?0?core?id:?2processor?:?8?physical?id?:?0?core?id:?2processor?:?10?physical?id?:?0?core?id:?3processor?:?34?physical?id?:?0?core?id:?3processor?:?30?physical?id?:?0?core?id:?4processor?:?6?physical?id?:?0?core?id:?4processor?:?2?physical?id?:?0?core?id:?5processor?:?26?physical?id?:?0?core?id:?5processor?:?12?physical?id?:?0?core?id:?8processor?:?36?physical?id?:?0?core?id:?8processor?:?16?physical?id?:?0?core?id:?9processor?:?40?physical?id?:?0?core?id:?93、稳定性监测混部与非混部在线应用性能:流量、平响、错误数(单应用、汇总)混部与非混部离线任务:Job平均运行数量、时间、错误(集市队列、任务级别)混部在线调度:虚拟资源,物理资源的容量、已用(节点、在离线)、可用,组件可用性混部离线调度:资源可用、已用、扩容状态、次数、处理时4、在线双活分布基于k8s双活集群,50%负载混部策略,做到业务可灰度可回退。集成CI/CD,仅满足特定条件POD,自动触发发布规则,调度到混部集群。成果与展望目前,之家混部规模还在初期阶段,现达应用230+/ POD 400+,在线节点CPU利用率从平均10% 提升到50%。未来的发展,基于AutoKH架构,深挖资源预测(秒级)、资源防干扰(网络隔离、CgroupV2)、稳定性保障(eBPF 观测)等技术细节,实现更多的混部场景(中间件、DB、实时计算)。参考资料https://developer.aliyun.com/article/651202https://github.com/Tencent/caelushttps://github.com/nolar/kopfhttps://github.com/AliyunContainerService/kubernetes-cronhpa-controllerhttps://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/43438.pdf作者简介汽车之家张安宁智能数据中心2011年加入汽车之家,负责容器云建设与维护工作,致力于为公司提供稳定、高效的容器服务。阅读更多项目管理系统在之家的落地实践之家云API网关的前世今生浅谈WEB漏洞之SQL注入修复实践 汽车之家AutoKH混部技术实践
    上一页:汽车之家IM即时通讯平台技术分享 下一页:汽车之家APP基于Mach-O的探索与实践
    全部评论(0)