openEuler AArch64架构vCPU热插拔技术内幕

image2
openEuler2024-06-27openEuler

openEuler 社区引领技术浪潮,早在openEuler 20.09 创新版本就率先使能并对外开放了 AArch64 架构 vCPU 热插特性。时隔四年,openEuler 24.03 LTS 版本补充了 vCPU 热拔能力,vCPU 热插拔特性得到正式完整的支持,其更好地满足开发者对 vCPU 资源的弹性伸缩需求,进一步延伸了该特性的使用场景及价值。本文将带各位读者更深入了解 vCPU 热插拔技术的价值、AArch64 架构下 vCPU 热插拔特性演进历史及基本实现原理。

特性价值

vCPU 热插拔技术可以在虚拟机运行状态下增加或减少 vCPU 数量,实现不中断业务条件下动态调整vCPU 资源。

资源弹性是云计算的核心优势之一,而 vCPU 热插拔是实现 CPU 算力弹性的关键技术之一。其价值不限于: 1)加快虚拟机启动速度。特别对于轻量化场景收益较大。比如 Kata 安全容器初始只配置 1个 vCPU,等启动完成后热插更多 vCPU。 2)按需使用资源,从而优化业务成本。开发者根据业务负载需求,在线调整虚拟机 vCPU 数量。负载大时增加资源,负载小时减少资源。

来自AArch64架构的挑战

在 AArch64 架构下,支持 vCPU 热插拔面临来自架构规范、Guest 内核、KVM 各方面的挑战。

AArch64 体系结构缺乏对物理 CPU 热插拔的设计,使得虚拟化场景 CPU 热插拔的设计缺乏现实物理世界的依据。特别是中断控制器 GIC,其默认所有 CPU 在启动时都已存在,不能在系统启动后进行调整。

由于物理系统不支持 CPU 热插拔,ARM Linux 内核也一直缺乏对该能力的支持。如果要在虚拟化场景实现该特性,必须先完善 Linux 内核,使得当其作为 Guest 内核时能正常支持该特性。

另外还面临着来自 KVM 的挑战。KVM 不支持动态调整 vCPU 对象数量,即当虚拟机开始运行后,KVM 将拒绝操作目标虚拟机增加或减少 vCPU。

特性演进

面对以上挑战,虚拟化场景下 CPU 热插拔特性的进度较缓慢。最早的实现来自华为,于 2019 年和 2020 年分别向主线社区提交了 Guest 内核侧补丁[1]及 QEMU 侧补丁[2],热插拔协议上参考了 x86 架构。但社区讨论认为 AArch64 架构暂时缺乏 CPU 热插拔相关规范,相关补丁不能先于规范合入主线。

此后华为和 ARM 公司深入合作,经过长期的努力,基本解决了不限于以上所述的挑战。2022 年,ACPI 6.5 规范发布,针对 AArch64 特殊的架构限制,在 MADT GICC 描述中加入了 ONLINE-CAPABLE 属性[3],用于支持虚拟化场景下 CPU 热插拔。基于该最新规范,ARM 公司开发了 Guest 内核侧补丁[4],华为开发了 QEMU 侧补丁[5]。相关补丁正在稳步推进,即将合入主线社区。

openEuler 社区完善并加固了以上补丁,在 openEuler 24.03 LTS 版本中提供了对该特性的稳定支持。

基本实现原理

**初始化流程:**为了应对来自体系结构和 KVM 架构的约束,QEMU 在创建具有热插能力的虚拟机时,必须为所有 vCPU 调用 KVM_CREATE_VCPU IOCTL 创建 KVM vCPU 对象,并为所有 vCPU 提前创建好 GIC irqs、GICR 和 GICC 等虚拟资源。但初始化时只需创建实际需要的 vCPU 线程,对于还未热插的 vCPU,其 vCPU 线程可以延迟到热插时再创建。

此外还涉及到两个关键的虚拟设备。CPU Control Device 负责管理整个vCPU热插拔的状态,GED Device 负责向 Guest 发送热插拔通知。Guest 内核通过虚拟 ACPI 表和 CPU Control Device 及GED 设备交互。相关的 ACPI 方法包括_EVT、_STA、_MAT 等。

image2 、 图1 vCPU 热插流程

** 热插流程: **开发者通过 device_add QMP 命令向 QEMU 发出 vCPU 热插请求。QEMU 解析命令,创建 vCPU 线程使之与初始化时提前创建的 KVM vCPU 对象相关联,然后调用 KVM 初始化vCPU 状态并运行。随后,QEMU 依照 ACPI 规范,修改当前热插 vCPU 的 _STA 状态为 PRESENT 且 ENABLE ,操作 GED 设备发送中断给 Guest。Guest 响应中断,执行 ACPI 表中的 CSCN 方法获取到新插入的 vCPU 信息,最后通知内核 CPU 热插拔模块初始化该 CPU。

image2

图2 vCPU 热拔流程

**热拔流程:**开发者通过 device_del QMP 命令向 QEMU 发出 vCPU 热拔请求。QEMU 解析QMP 命令,首先标记需要热拔 vCPU 为 removing 状态,然后通过 GED 设备向 Guest 发出中断。Guest 执行 ACPI CSCN 方法了解到需要拔出的 vCPU 编号,通知内核 CPU 热插拔模块清理下线该 CPU。完成之后,Guest 执行 _EJ0 方法通知后端 QEMU 清理 vCPU 资源。QEMU 修改 vCPU 的 _STA 状态为 PRESENT 且 DISABLE。

未来展望

如今资源在线伸缩已经成为了很多行业业务场景的关键需求,vCPU 热插拔特性将支撑 ARM 平台服务器打开更大云数据中心市场。未来 openEuler 社区将持续完善虚拟化技术生态,引领技术创新,打造高性能强安全轻量化虚拟化平台,为各行各业创造更大价值。

参考资料

[1] 2019 年华为公司提交的 AArch64 vCPU 热插拔 Guest 内核侧代码:https://lwn.net/Articles/792409/

[2] 2020 年华为公司提交的 AArch64 vCPU 热插拔 QEMU 侧代码:https://lore.kernel.org/qemu-devel/20200613213629.21984-2-salil.mehta@huawei.com/

[3] ACPI6.5 规范在 GICC flag 中新增“Online Capable”标志位:https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#gicc-cpu-interface-flags

[4] 2023 年 ARM 公司提交的 AArch64 vCPU 热插拔 Guest 内核侧代码:https://lore.kernel.org/lkml/20230203135043.409192-1-james.morse@arm.com/)

[5] 2023 年华为公司提交的 AArch64 vCPU 热插拔 QEMU 侧代码:https://lore.kernel.org/qemu-devel/20230926100436.28284-1-salil.mehta@huawei.com/


【免责声明】本文仅代表作者本人观点,与本网站无关。本网站对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文仅供读者参考,由此产生的所有法律责任均由读者本人承担。

Agith:openEuler 运维变更观测工具

openEuler2023-11-18eBPF

针对 openEuler 运维变更过程观测困难的问题,华为 2012 服务实验室 OSMind 团队开发了基于 eBPF 的变更观测工具—— Agith。Agith 可以识别与变更相关的行为,并将变更过程表示为一种拓扑结构——变更影响面。通过变更影响面可以完成变更告警、审计、根因定位、依赖分析等功能。

背景

云计算成为信息时代的算力底座,服务千家万户。作为重要的基础实施,稳定压倒一切。但同时云计算也在追求扩大规模以及适配上层业务。因此带来频繁的变更难免会引发故障。

变更任务大致可以分为两类。第一类白屏变更是通过运维工具执行操作,适用于版本变更、资源扩缩容、灾备倒换等流程固定的任务。但是灵活性差,只能执行标准流程。另一类黑屏变更需要运维人员登录系统,通过执行命令来完成变更过程。黑屏变更简单灵活,适合中小型企业的 IT 运维与复杂的运维任务,例如根因分析或故障修复。黑屏变更是变更不确定性的主要来源,也更容易引发故障,需要加强可观测性。

目前对于变更过程的主要观测方法是记录变更过程中运维人员输入的所有命令。在变更结束后,通过审计监察发现潜在的风险。这种方式需要大量的专家经验。因为命令日志很难表示变更过程。例如 2022-09-12 00:00:00.0 张三 obs\_cmd.sh 1818 华东 10.164.179.21,包含时间、人员、主机IP,主机集群(华东)和最重要的命令(obs\_cmd.sh 1818)。但是如果没有专家经验,这条命令是无法解读的。

Agith与变更影响面

Agith 是 Agent Smith 的缩写。这是致敬《黑客帝国》的 Smith 探员。虽然 Smith 探员是电影中的反派角色,但从运维工程师的视角来看,探员是 Matrix 系统中最优秀的运维工程师。Matrix 系统每时每刻有数十亿的流量接入,但是探员却可以感知轻微的变更异常,并通过最近的节点登入以处理故障(Kill Neo)。这种观测能力正是 Agith 的设计目标。系统每时每刻都在处理大量的请求,包括正常的上层服务与变更任务。Agith 需要从中区分出变更任务,将变更的行为记录下来,整理为变更影响面拓扑图。

Agith 是一款面向变更过程的观测工具。相比只记录命令名,Agith 更关注命令的行为。这种区别体现在最终输出上。传统方法输出的是运维人员的所有输入,组织为日志数据。而 Agith 的输出的是变更影响面,如下图所示:

图1 变更影响面示例

橙色节点表示进程,紫色节点表示文件,蓝色节点表示一个远程节点。每种类型节点的都有对应的属性信息展示在右侧列表中。例如进程节点属性信息有进程工作路径、执行命令名、PID 等。文件节点有 inode、文件路径。远程节点有 IP-Port 地址,交互信息等。节点之间的边存储了系统调用类型,例如进程节点与文件节点可调用类型有 read、write、unlink(删除)。

图1的变更中,运维人员登录主机后执行了一条命令 obs_cmd.sh 1818,随即退出。这个操作反应在图中是进程节点P1(bash)创建了进程节点P2(obs_cmd.sh 1818)。进程节点 P2 在执行中又创建了一个 python 脚本(进程节点P3)与一个 shell 脚本(进程进程 P4)。进程节点 P3 创建了一个文件节点 F1。而进程节点 P4 修改了文件 F1,并且通过 curl 命令(进程节点 P5)访问了一个远程节点 N1 的 1818 端口。点击蓝色节点可以看到这次访问的 URL 链接。

相比命令日志,Agith 更关注命令的行为。这种方法将无限的命令映射到有限的行为上,方便运维人员理解。只要有这张变更影响面拓扑图,任何一个运维人员都可以理解此次变更的操作,不需要复杂的专家经验。

Agith 架构

Agith 在设计中采用数据流与控制流分离的策略。eBPF 模块、Consumer、Repository 和 Monitor 可以组成数据筛选-采集-整理-输出的数据流(图 2 中实线)。控制流(图 2 中虚线)则以 Controller 模块为核心,其他模块受 Controller 模块的统一管理。启动时 Controller 模块检测环境,分析配置文件,检查启动参数。然后依次启动部署各个模块。当程序结束时管理各个模块完成清理任务后依次退出。

图2 Agith架构图

eBPF 模块

eBPF 模块包含 eBPF Probes、Traces、Targets 三个部分。这个模块承担数据的筛选工作。筛选方法是一种基于动态目标的变更监控技术。该技术首先建立了两类 map。第一类是 Target map。Target map 保存了监控目标的标识符。例如进程的 pid、文件的 inode、网络的 IP。第二类是Trace map。Trace map 用于存储探针获取的数据。

eBPF Probe 是针对特定的系统调用编写的探针程序。这些程序被触发时,首先根据 Target map 判断系统调用是否与监控目标相关。如果不相关直接返回。如果相关会收集数据写入Trace map。同时 eBPF Probe 会修改 Target map。例如在创建进程的 clone 系统调用处挂载探针程序。当任何程序执行 clone 系统调用,探针会被触发。探针程序检测进程 pid 是否在 Target map 中。如果不存在就返回继续执行 clone。如果存在,会将返回值即子程序的 pid 写入 Trace map,然后将子程序的 pid添加到 Target map 中。这样子程序也被纳入监控范围内了。

Consumer 模块

Consumer 模块承担采集工作,即读取并缓存 Trace map 的数据。这个过程涉及读写速率控制,数据异常处理,数据融合等。

Consumer 读取的数据类似 strace 得到系统调用记录。这些数据采用“主谓宾”的结构体存储。例如 pid:411962, syscall:read, ret: 18, time:974333207983984, ready:1, obj:{fd:3, i\_ino:2505217} 是一条监控 read 系统调用得到的记录。“主语”是 pid:411962,表示一个进程号为 411962 的进程。“谓语”是 syscall:read,表示读取操作。“宾语”是 obj:{fd:3, i\_ino:2505217} 表示是一个文件,文件句柄号是 3,inode 编码是 2505217。除此之外还有这次系统调用的时间和返回值。这条记录的含义是进程 411962 读取了文件 2505217。这条信息非常简陋。进程执行的程序名是什么?读取的文件名是什么?这些信息包含在之前的记录中。例如进程名包含在在 exec 系统调用中,文件名包含在 openat 系统调用中。

Repository & Monitor

Repository 承担整理与输出工作。它存储 Consumer 读取的记录,将信息填充到变更影响面图中。例如打开一个新文件,会创建一个文件节点,并在进程与文件之间连接一条边。除此之外 Repository 负责向 Monitor 模块传递信息。

Monitor 模块负责告警。如果在采集数据的过程中发现高危操作,例如删除重要的配置文件, Monitor 模块会发送告警。Monitor 的数据来源于 Repository。因为只有 Repository 存储的图中才能掌握完整的上下文信息。仅仅依靠 Consumer 的记录不足以判定是否是高危操作。

应用场景

Agith功能是观测变更过程,最终得到的变更影响面可以应用在风险告警、根因定位、变更审计、依赖分析等。

风险告警可以直接使用 Agith。首先在配置文件中声明含有风险的行为,例如修改某个文件,访问某项服务,删除进程等等。Agith 在整理过程中发现这类数据,会向配置文件中的邮箱发送告警信息。例如美联航故障事件中删除文件的行为,Agith 可以发现这种异常操作。目前邮件告警功能在开发中,敬请期待。

根因定位是变更影响面最重要的用途。相比命令日志,变更影响面数据含有更细粒度的行为数据,可以更容易地发现与故障相关的命令行为。

变更审计可以在变更后检查是否有超出预期的行为。对于重要的变更操作,可以在灰度环境中先执行一遍,获取变更影响面。然后在目标环境中执行,得到另一份变更影响面。将两份数据比较,可以发现在变更操作中有没有错误操作。

依赖分析是获取一个服务在运行时依赖的各种本地资源与周边服务。这个功能虽然与变更无关。但是只要在终端中启动这个服务,Agith 就可以获取该服务的所有行为,从而得到所依赖的各种资源,例如动态依赖库,配置文件等。

路线图

当前 Agith 的功能只能覆盖进程、文件、网络的一部分行为。但是黑屏变更过程中命令行为远远超过这部分。

表1 Agith开发计划

表1是未来 Agith 计划覆盖的变更行为。变更行为的整理是一个复杂的过程。我们一开始采用的方案是自底向上。无论什么样的上层服务,从OS的视角来看无非5类行为:进程,内存,文件,网络,外设。只要对这5类行为监控,就可以覆盖所有的命令行为。但是实践并非如此。

首先过度抽象将失去数据的价值。例如执行 mysql、docker、kubernetes 的命令,都是向已有的服务进程发送命令,可以统一为进程交互行为。但是如果只有交互的进程名,根本不能从中判定风险。其次过度抽象意味着所有的底层行为都是需要记录的,会产生巨大的数据冗余。例如一次进程启动,会频繁申请内存,记录这些数据没有意义。

由此我们认识到观测这张网既不能太粗,也不能太细,应当结合运维需求灵活调整。所以我们采用了一种自顶向下的方法。首先收集 239万条变更命令,逐条去分析变更命令,得出这条命令所产生的数据。然后根据数据的相似性合并。例如文件都会有文件名,容器类操作有容器 id 或者镜像 id。合并过程中会舍弃两者差别的数据项。如果这个数据项对于运维价值不大,就是可以舍弃的。如果这个数据对运维很重要,说明这个合并是错误的。在整理的过程中有太多收益与成本的博弈,相信未来还会不断演进。我们最终梳理出需要监控的行为如下图。

图3 运维变更行为

欢迎加入

如果您对 Agith 有任何建议,欢迎在 https://gitee.com/openeuler/Agith/issues 提出,也可以添加小助手微信,加入 eBPF 技术交流微信群。如果有合作意向,可以与笔者联系(shangguandongdong1@huawei.com)。


【免责声明】本文仅代表作者本人观点,与本网站无关。本网站对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文仅供读者参考,由此产生的所有法律责任均由读者本人承担。