百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

看字节跳动容器化场景下,如何实现性能优化?

cac55 2024-09-29 09:30 43 浏览 0 评论

大规模用户容器的快速发展,字节跳动研发工程师教你稳准狠节约成本,优化性能。

如今在字节跳动的私有云平台上,托管了抖音、今日头条等大量在线服务的容器。随着这些业务的快速发展,字节跳动的 Kubernetes 集群数量和规模也越来越大,与此同时,机器的负载也会越来越高,给日常的运维工作带来了巨大的挑战。

尤其是性能问题,经过大量系统问题排查后发现,归根结底在于内核本身的 Cgroups 隔离性和可观测性不足,不能提供所需的数据,这就给线上运维工作带来了困难,进而也阻碍了真正提升资源利用率的进程。那么在字节跳动大规模的容器化场景下,如何实现既提高机器的利用率又能兼顾服务稳定性问题呢?

传统的系统监控,治标不治本

很多人第一时间想到利用传统的系统监控去处理,但是并不能。一个很重要的原因,我们常用到的系统监控,包括像 cAdvisor、Atop 等等,这些系统监控的一个弊端是,只能看到内核暴露出来的数据,也就是说,如果内核没有提供这块数据,我们就没有办法看到。如果我们希望通过修改内核,去支持一些问题的排查和新的特性,但内核的上线过程,又是一个非常痛苦和长期的过程。

我们的思路是,通过提升系统的可见性,去了解服务之间的相互影响,提升系统问题的排查效率,进而去提升资源利用率。

基于eBPF的系统监控,提升系统可见性

Kubernetes 实际上是一个面向应用的技术架构,其设计目标是让业务只用关心资源,而不是机器,因此它的监控系统也应该是服务级别的,而不是机器级别的。基于这些考虑,我们引入了eBPF。

eBPF 是一个在内核中运行的虚拟机,它可以运行用户实现的 eBPF 程序,在内核以本地代码的形式和速度去执行,它可以跟内核的 Trace 系统相结合,给我们提供几乎无限的可观测性。

eBPF 的基本原理——它所有的接口都是通过 bpf 系统调用来跟内核进行交互,eBPF 程序通过Clang/LLVM 编译产生 eBPF 的字节码,通过 bpf 系统调用,加载入内核,由 Verifier 去验证代码的安全性,再通过 JIT 编译实时的转化成 native 的 X86 的指令。

准确识别用户程序在各资源维度的具体行为

如果这个程序是跟内核的 Kprobe 相结合的,当它执行到我们 hook 那个内核函数时,就会回调我们自定义的 eBPF 程序。同时它也提供了一些 Map,用来存 KV,再通过 bpf 这个系统调用,去读取数据。这一套机制,就给我们提供了几乎无限的可观测性。那么我们就基于 eBPF 这个内核机制,实现了一个叫做 SysProbe 的系统监控,在 CPU 和内存层面,我们同样延续使用了Cgroups 的一些数据,同时增加了 throttle、组线程数和状态,以及实例 Load 等更能反映实例负载真实情况的一些指标。

在 Block IO 这层,通过 hook VFS 层的关键函数和系统调用,实现准确识别实例的读写行为。在网络 IO 这一层也是类似的情况,我们可以去探测服务的 Socket 级别的连接状态,以及实时的 SLA,比如说 SRTT 的抖动。基于这种线程级别的数据,我们可以把它聚合到容器级别,再聚合到实例级别,这样我们就得到了一个更准确的实例级别的监控。它的核心的思想就是准确识别用户程序在各个资源纬度的系统行为。

合理提升资源利用率

基于 eBPF 的系统监控,给我们提供了更准确的应用行为描述,继而我们可以在调度层面去做更多的事情,比如说我们可以基于这些数据构建一个实时 pipeline 来实现动态超售,从管控层面去提升资源利用率。

在调度器的 Priority 阶段,对资源部署影响最大的是 MostRequestedPriority,它会把机器上的CPU 内存尽量部署的更满,去减少 CPU 和内存的碎片。在网络 IO 和磁盘 IO 等没有被抽象成为可分配资源的场景下,我们会去把这两个纬度的资源打散,才真正的尽量去避免实例之间的相互影响,对于一些冷服务(升级不太频繁的服务),我们会主动去触发它的一些滚动,让它去更新套餐。既然我们把机器部署的更满,那么意味着,它们可能出现更多资源被饿死和排队的情况。当出现服务过载时,我们有必要去做一些驱逐,去保证机器上的资源处于一个合理的水位。

关于驱逐的优化,主要保证在关键的资源维度上,包括 CPU、内存和磁盘空间等纬度,不出现排队和饿死的情况。对于在线服务来说,实例 Load 是一个非常重要的指标,因为它更能反映业务进程在内核调度器上的排队的情况,这对于我们在线服务来说非常的有用。同时我们也对Kubernetes 本身驱逐的机制做了一些优化,让它受到 pdb 约束,而不至于在一些极端情况下把一个服务的所有实例全部驱逐掉。

PCT99是影响终端用户体验的关键指标

基于 Kubernetes 管控层面调度上的优化,是否就能够把利用率提上去,同时业务也不受影响呢?这显然是不够的,对于在线服务来说,有一个很关键的指标——PCT99,在大型的后台的分布式系统里面,很有可能成为整个后台系统的一个瓶颈,最终影响到终端用户的体验。

这些问题是从 Kubernetes、Docker 这些层面是无法解释清楚的,我们必须要从更深层次去挖掘这个问题的原因。在此不得不提到 RPC 请求,探索一个完整的链路是怎样的一种形态,继而分析出来性能瓶颈究竟出在哪里。

Kubernetes QoS 的深层含义

上文提到,eBPF 是一个很好的内核性能分析利器,因此基于 eBPF 我们可以对一个 RPC 请求的完成链路的延迟进行拆解分析,并确定延迟主要发生在进程处理请求这个阶段,而 Kubernetes的 CPU QoS 配置是其最重要的一个影响因素。

Kubernetes 的资源模型主要包含两个维度,即 Request 和 Limit。Request 表现出来的是Cgroups 中的进程在 CFS 调度器上的 share,而 Limit 则是 CFS 上的带宽限制。带宽限制意味着,用户申请了两核,那么就只能使用两核的 CPU 时间。基于 Request 和 Limit 相互大小的差异,在 Kubernetes 管控层面,将 Pod 换分为三个 QoS 层级, 优先级由高到低依次是Guaranteed、Burstable 和 Besteffort。如果我们对服务进行了超售,那么 Request 就会小于Limit, 在我们的场景下,大部分业务 Pod 都被放置在 Burstable 层级。因此,超售在管控层面的表现是,机器上所有 Pod 的 Request 总和小于机器的可分配资源,进而小于机器上所有Pod 的 Limit 的总和。

Kubernetes 对于 Cgroups 进程的优先级配置,最终会反映到 CFS 调度器上,而调度延迟是引起响应延迟抖动的主要原因。这里需要简单介绍下 CFS 调度器的基本原理。CFS 设计初衷是保证运行中进程的公平性,它会给每一个进程赋予一个权重,同时还有一个 vruntime 的概念,用于表示每个进程的累计运行时间。CFS 保证当前运行的是拥有最小 vruntime 的进程,如果进程的优先级高,则 vruntime 累计速度慢,优先级低,则 vruntime 累计速度快。组调度就是在 CFS 的基础上添加了 task_group 的概念,表示一组进程。因此在引入组调度后,一个sched_entity 可能是一个进程,也可能是一个进程组。task_group 在每个核的队列上有一个自己 sched_entity 和一个 rbtree。基于这样一种设计,Cgroups 支持递归创建子 Cgroup。因此 QoS 的本质是 Pod 所在 Cgroup 在 CFS 上的 share 占总体的比例,并且逐层递归。

CFS 调度延迟分析

调度延迟的含义,不是进程的运行速度变慢了,而是因为调度算法导致进程本该去执行的时候没有办法分配到 CPU 时间。产生调度延迟主要包含两个方面。第一个方面是上文提到的Kubernetes 管控层面的超售,它本质上使 Cgroup 内进程在 CFS 队列上的 share 变小了,所以在一个调度周期内运行时间相对变小。另一方面,实际上也是影响最大的一个方面,即 CFS 的唤醒抢占。其含义是,进程等待的资源就绪时,进程状态被置为 running,并被加入到调度队列等待执行,内核认为刚唤醒的进程有要事要做,会给它一些 vruntime 的补偿,将它的vruntime置为当前队列中所有处于运行状态的进程的vruntime的最小值减去 L,L 默认12毫秒。例如,现在有两个进程 A 和 B,A 为 running 状态而 B 为 sleeping 状态。当 B 唤醒时,其 vruntime 被置为 A 的 runtime 减去 L,并很快抢占 A 的 CPU 时间。因此在 B 的运行过程中,就给 A 带来了调度延迟。

早期在我们场景下,很多业务设置的套餐过小,比如 2C4G,他们利用率不高,但是调用下游超时的情况缺非常频繁。在当时无法从系统层面解释的情况下,业务只能通过扩容去解决,所有早期我们上容器的时候,没有节省资源,反而增加了资源的浪费。其次,对于延迟极其敏感的服务,我们在 Kubernetes 加入了一些特性,支持 share 和 Request 的计算解耦,给予业务进程一些 share 的补偿。

CFS 带宽限制和调优方法

CPU share 保证的是进程可用 CPU 时间的下限,但无法阻止进程使用超过其 quota 的 CPU 时间,因此还需要 Limit,也就是带宽限制来严格控制上限。对于 Limit,通常有两种调节技术。一种是最直观的纵向调节,也就是加大套餐核数,比如有 2C 改为 6C。这样调节的不足之处是,在数据层面,其利用率也随之变低,而这通常是业务同学不愿意看到的地方,因为业务同学会认为这是你的内核隔离机制的不完善导致他们不得不承受一个低的 ROI 的后果。另一种方式,我们其实可以横向来调节带宽限制的 quota 回填周期,即 cfs_period_us,同时保证 cfs_quota_us与 cfs_period_us 的比值不变。如下图,矩形表示 quota 大小,曲线以下面积表示进程使用的CPU 时间。在 100ms 时,曲线以下面积大于矩形面积,但是在 300ms 的长度上来看,前者却比后者小。通过这种横向调节技术,基本可以彻底解决在线业务被 throttle 挂起导致 RPC 超时的问题。

另一方面,我们并不仅仅从系统层面去做调优,实际上业务层面可以一起来配合做这样一件事情。在我们的场景下,很多在线业务使用 golang 编写。在 golang 场景下,调度延迟的问题特别常见,其原因主要有两点,一是 golang 中常用的异步超时逻辑,即一个 select 下会有一个timer 的 channal 和一个请求处理完成的 channel,根据上文分析唤醒抢占,timer 的响应一定会比请求处理的响应时间快,因为 server 端的 reponse 数据已经达到了client 端,但是因为调度延时而拖慢了时间,因而出现超时。另一个原因是,golang 的 GC、work steal 协程调度算法,非常容易在短时间内产生大量并发线程而被 CFS 带宽限制给 throttle。因此我们会在微服务框架中默认限制并发度大小。对于 python 服务,我们则通过 lxcfs 给容器一个虚拟的 /proc和 /sys 视图,业务进程在启动时就会只启动所分配核数的 worker 进程数。

Cgroups 调优案例

第一个服务案例。我们的场景下,Service Mesh 的 Proxy 是跟业务进程是跑在同一个容器里面的。它增加了这个容器里面的线程数,开启 mesh 以后,这个黑色的曲线是开启的,蓝色是没有开启的,我们会发现黑色比蓝色要稍微差一点,开启了 mesh 以后,他们的早晚高峰的 PCT99 抖动问题都非常的严重。我们做了一件非常简单的事情,把多个套餐合并成了一个大套餐,但实际上它整体的利用率并没有改变,整体的资源的申请量也没有改变,但是可以看到效果非常的显著,它甚至比之前没有开启 mesh 的效果还要好。

第二个服务是一个典型的在短时间内有大量的 burst 请求的服务,它会出现大量的调用下游超时,于是我们就给它就做了 period 调整,把 period 时间拉长,同时配合用户态去限制它的线程并发度,而且 share 保持不变,这时可以看到调用下游超时的情况完全消失了。

第三个服务案例,效果与上一个类似。先给它做了一个 period 调整,我们可以看到5月31号到6月1号期间,效果相对于之前来说已经好了很多,这个时候我们只调整拉长了 period,但是并没有完全解决他们的性能问题。我们又把它的套餐也调了一下,结果就看到超时情况完全消失了,效果非常的理想。

写在最后

字节跳动经过近几年来高速发展,集结和掌握了一批优质的技术资源,出品了今日头条与抖音两大国民级App,想必其中运维与架构技术定是日趋成熟的,在容器化场景下,优化性能的优势也日益凸显。如果觉得本篇文章值得学习,欢迎继续关注Archsummit全球架构师峰会北京2019,点击「了解更多」即刻享有限时7折大力优惠名额,有任何问题欢迎联系票务小姐姐灰灰:15600537884 (微信同号)

相关推荐

如何屏蔽色情网站?_怎么能屏蔽网站

一、基础防御:全网DNS劫持阻断1.修改全网DNS服务器推荐DNS:安全DNS:CleanBrowsing(成人内容过滤):185.228.168.168/185.228.169.168Open...

容器、Pod、虚拟机与宿主机网络通信全解:看这一篇就够了

在日常开发与部署过程中,很多人一开始都会有这样的疑惑:容器之间是怎么通信的?容器怎么访问宿主机?宿主机又如何访问容器?Kubernetes中Pod的网络和Docker容器一样吗?容器跨机器是...

Win11专业版找不到共享打印机的问题

有很多深度官网的用户,都是在办公室上班的。而上班就需要使用打印机,但更新win11系统后,却出现同一个办公室里面的打印机都找不到的问题,这该如何处理呢?其实,可能是由于我们并没有打开共享打印机而造成的...

常用电脑快捷键大全,摆脱鼠标依赖,建议收藏

Ctrl+C复制Ctrl+X剪切Ctrl+V粘贴Ctrl+Z撤销Ctrl+Y重做Ctrl+B加粗Ctrl+A全选所有文件Ctrl+S保存Ctrl+N新建Ctrl+O打开Ctrl+E...

Win11实现自动追剧Jellyfin硬解,免NAS复杂操作

大家好,欢迎来到思赞数码。本期将详细介绍如何通过安装和配置Sonarr、Radarr、Prowlarr、qBittorrent和Jellyfin,打造一套自动化的影视管理系统。很多人认为,要实现自动追...

微软Win11安卓子系统WSA 2308.40000.3.0更新推送下载

IT之家9月21日消息,微软官方博客今日宣布,已面向所有WindowsInsider用户推送了Windows11安卓子系统的2308.40000.3.0版本更新。本次更新和之前...

路由器总掉线 一个命令就能猜出八九分

明明网络强度满格或有线图标正常,但视频卡成PPT、网页刷不开、游戏动不了,闲心这些问题很多小伙伴都碰到过。每次都要开关路由、宽带/光猫、插拔网线……一通忙。有没有啥办法能快速确定故障到底在哪儿,方便处...

windows电脑如何修改hosts文件?_windows怎么修改hosts

先来简单说下电脑host的作用hosts文件的作用:hosts文件是一个用于储存计算机网络中各节点信息的计算机文件;作用是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中...

win10广告弹窗ShellExperienceHost.exe

win10右下角老是弹出广告弹窗,排查为以下程序引起,但是这个是系统菜单的程序不能动:C:\Windows\SystemApps\ShellExperienceHost_cw5n1h2txyewy\S...

Win10 Mobile预览版10512/10166越狱解锁部署已被黑客攻破

看起来统一的WindowsPhone和Windows越加吸引人们的关注,特别是黑客们的好奇心。XDA论坛宣称,在Win10Mobile预览版10512/10166上,已取得越狱/解锁部署突破,比如可...

6款冷门小众软件,都是宝藏,建议收藏

真的很不错(。-ω-)zzzBearhttps://bear.app/cn/Bear是一个漂亮,灵活的Markdown的写作工具。它一样只支持苹果家的全平台。它一出现就惊艳四方,就被AppSto...

如何让不符合条件的设备升级Windows 11

如果你是最近(6月24日之后)加入WindowsInsider项目并且你的设备并不符合升级条件,那么当你在尝试升级Windows11的时候可能会看到以下错误:你的PC不符合Wi...

windows host文件怎么恢复?局域网访问全靠这些!

windowshost文件怎么恢复?windowshost文件是常用网址域名及其相应IP地址建立一个关联文件,通过这个host文件配置域名和IP的映射关系,以提高域名解析的速度,方便局域网用户使用...

Mac Hosts管理工具---SwitchHosts

switchhosts!formac是一款帮助用户快速切换hosts文件的工具,switchhosts!formac能够帮助你快速方便的打造个人专用的网络环境,支持本地和在线两种方式,并且支持...

「浅谈趣说网络知识」 第十二弹 老而不死的Hosts,它还很有用

【浅谈趣说网络知识】第十二弹老而不死的Hosts,它还很有用什么时候才觉得自己真的老了,不是35岁以上的数字,不是头上的点点白发,而是不知觉中的怀旧。风口上的IT界讲的就是"长江后浪推前浪...

取消回复欢迎 发表评论: