首页
/ Firecracker 集成测试体系深入指南:从 pytest 运行、A/B 回归对比到调试实战

Firecracker 集成测试体系深入指南:从 pytest 运行、A/B 回归对比到调试实战

2026-09-08 15:04:43作者:羿妍玫Ivan

本文系统性讲解 Firecracker(secure and fast microVMs for serverless computing)仓库内置的整套集成测试体系。这套体系以 pytest 为核心、以 tools/devtool 为统一入口,覆盖功能、性能与安全契约,并配套 Rust 原生集成测试、Sanitizer 构建测试和用于性能回归的 A/B-Testing 框架。读完本文,你将掌握 Firecracker 集成测试的完整运行方式、fixture 与标记的使用规则、如何编写和调试测试,以及如何利用 A/B 测试在不依赖“ground truth”的前提下做性能与安全回归分析。

测试体系的定位:守护安全、质量与性能契约

tests/ 目录下的所有测试,其使命是"uphold the security, quality, and performance contracts of Firecracker"——即守护 Firecracker 的安全、质量与性能三大约定。测试代码分布在仓库的 tests/(Python 集成测试)、src/vmm/tests/(Rust 集成测试)与 .buildkite/(CI 流水线生成)三个层面,配合测试产物(guest 内核与 rootfs,见 tests/data)共同工作。

整体设计原则(详见 tests/README.md 的 "Implementation Goals" 一节)可以概括为三条:

  • 手动与 CI 两用:既能在开发机上轻松手动运行,也能无缝运行在持续集成环境;
  • 测试相互独立且自包含:每个测试期望一个干净的环境,并保证在结束后留下干净环境(超时退出);
  • 永远使用最新依赖与资源:测试运行时拉取最新依赖,避免因陈旧资源产生偏差。

快速起步:用 devtool 运行测试

测试系统建立在 pytest 之上,而 tools/devtool 是一个便捷包装脚本:它会自动从 S3 下载所需的测试产物(guest 内核、rootfs 等),随后在 Docker 容器内调用 pytest。tools/devtool help 提供全部用法说明,test 命令的具体参数见 devtool 的 help 输出

环境依赖

运行完整测试套件前需要确认主机满足:

  • 一台 裸金属 Linux 主机,uname -r >= 5.10,且 KVM 已启用(存在 /dev/kvm 设备节点);
  • Docker
  • awscli version 2(用于下载 S3 上的测试产物)。

这些依赖意味着虚拟化层套娃(如 VM 里跑测试)一般不可行——测试需要直接操作 KVM 与宿主机资源。

运行 PR CI 同款的全量测试

运行所有在 PR CI 中执行的测试(即排除带 pytest.mark.nonci 标记的用例):

tools/devtool -y test

-y--unattended)表示无人值守运行,自动对一切确认提示回答 yes。

运行性能测试

部分性能测试要求对宿主机做性能调优(例如为 hugetlbfs 预留内存)。运行这类测试必须额外加 --performance 标志,它会调整宿主机电源状态(C-state、P-state 等)以获得一致的性能表现:

tools/devtool -y test --performance

定向运行:目录、文件与单个测试

只运行特定目录或文件中的测试时,将 -- 之后的内容原样透传给 pytest(注意:所有路径都相对 tests 目录,而非仓库根目录):

tools/devtool -y test -- integration_tests/performance/test_boottime.py
# 或
tools/devtool -y test --performance -- integration_tests/performance/test_huge_pages.py

运行文件中的某一个测试函数,使用 pytest 的 :: 语法定位:

tools/devtool -y test -- integration_tests/performance/test_boottime.py::test_boottime

用 -k 按名称子串筛选

pytest 的 -k 选项允许按测试名称中的子串过滤。这在指定测试函数的参数化(parametrize)取值时尤其有用,例如下面的命令会运行所有 microVM 内存为 1024MB 的 boottime 测试:

tools/devtool -y test -- -k 1024 integration_tests/performance/test_boottime.py::test_boottime

不使用 devtool:直接在容器或本机跑 pytest

如果不想借助 devtool 的能力,可在容器内直接运行 pytest(先进入 dev 容器再执行):

tools/devtool -y shell -p
pytest [<pytest argument>...]

也可以在开发机上原生运行:

python3 -m pytest [<pytest argument>...]

注意:Python 集成测试要求 root 权限——tests/conftest.py 在会话启动时即校验 os.geteuid() == 0,否则直接抛 PermissionError

通过 FC_TEST_* 环境变量调整测试行为

测试框架运行时会读取少量 FC_TEST_* 环境变量,它们被 tools/devtool 加入 allowlist 并自动转发进 dev 容器(tools/devtool 中通过 env | grep -P "^(AWS_EMF_|BUILDKITE|CODECOV_|FC_TEST_)" 收集并注入容器环境)。因此在宿主机上直接设置即可生效:

环境变量 作用
FC_TEST_SKIP_ARTIFACT_COPY=1 跳过把 CI 产物拷贝到容器内 /srv/test_artifacts 的步骤(devtool test_debug 使用)。对应逻辑见 tools/test.sh:置位后仅创建空目录。
FC_TEST_DUMP_ON_FAILURE=1 测试失败时额外收集"重型"事后产物:完整内存快照(post_failure.mem + post_failure.vmstate)、测试用 id_rsa 私钥副本,以及 uVM chroot 根目录下的每个普通文件。默认关闭;轻量产物(host-dmesg.log 与 guest 串口控制台日志)则总是被收集。nightly 性能流水线会开启此选项。收集逻辑位于 tests/conftest.py 的 microvm_factory teardown
FC_TEST_DEVELOPMENT_ENVIRONMENT=1 跳过那些依赖特定宿主机构型、且在开发环境上运行没有价值的测试。

测试输出的去向

测试运行结果与常规输出进入 stdout,错误进入 stderr。默认情况下,测试执行过程中 stdout/stderr 会被捕获,只有测试失败时才会在最终的失败报告里打印。想要边跑边看实时输出(无论成败),加 -s 标志:

tools/devtool -y test -- -s

pytest 的详细行为由 tests/pytest.ini 控制,例如:默认 addopts 包含 --tb=short -vv --durations=10 --showlocals -m 'not nonci and not no_block_pr',并生成 --json-report --json-report-file=../test_results/test-report.json;默认单测超时为 300 秒;pytest 缓存目录被重定向到 ../build/pytest_cache,避免污染源码树。

三层测试体系:Python、Rust 与 Sanitizer

Firecracker 的测试并非只有 Python 集成测试一层。文档将测试划分为三个互补层次。

Python 集成测试(HTTP API 驱动)

pytest 驱动的集成测试通过 Firecracker 的 HTTP API 来配置 VMM 并与之通信——即把 Firecracker 当作一个运行中的服务来"黑盒"验证。这一层覆盖了功能(tests/integration_tests/functional)、性能(tests/integration_tests/performance)与安全(tests/integration_tests/security)三大类用例。

Rust 集成测试(程序化 API 驱动)

与 HTTP 集成测试并行,vmm crate 自带一批原生 Rust 集成测试(见 src/vmm/tests,入口为 integration_tests.rs),它们不经过 HTTP,直接通过程序化 API(例如 build_and_boot_microvmPrebootApiControllerRuntimeApiController)驱动 VMM。cargo test 时 Cargo 会自动收集这些测试,它们同样计入代码覆盖率。

只运行 Rust 集成测试:

cargo test --test integration_tests --all

与单元测试不同,Rust 集成测试每个都运行在独立的进程中,且 Cargo 会将它们打包进一个新的 crate,这带来两个已知的副作用:

  1. 只能调用 pub 函数——这恰好使 VMM 能以"程序化用户"的视角被消费。如果某个函数必须被测试使用却还不是 pub,在把它加入公共接口之前,请慎重思考它是否在概念上真的需要暴露。

  2. VMM 正确退出的前提是 exit code 0(这是资源正确清理所必需的);但 Cargo 并不期望测试进程自行结束,因此无法正常收集其输出。实际输出形态大致是:

    cargo test --test integration_tests
    running 3 tests
    test test_setup_serial_device ... ok
    

从源码看(integration_tests.rs),test_build_and_boot_microvm 会先断言"未配置 boot source 时报 MissingKernelConfig 错误",再对 pci_enabledmemory_hotplug 的各组合逐一构建并引导启动一个 microVM,最后断言其以 FcExitCode::Ok 退出。这类测试同样贯彻了"可独立、自包含"的设计目标。

Sanitizer 构建测试

Firecracker 还设有一个专门的构建测试,在 sanitizer 下运行 Rust 集成测试:

tools/devtool -y test -- -m nonci integration_tests/build/test_sanitizers.py

注意这里显式用了 -m nonci 来覆盖 pytest.ini 的默认标记过滤。对应的 Buildkite sanitizer 流水线由 .buildkite/pipeline_sanitizers.py 生成。

A/B 测试:一种不需要 ground truth 的回归策略

A/B-Testing 是一种将某个测试函数在两个不同环境(A 与 B)中分别执行、再对两次输出做比较的测试策略。它的优势在于不需要在仓库里固化一份 ground truth——基准答案由 A 环境动态生成。Firecracker 的 A/B 测试通常比较两个不同 commit 编译出的二进制(例如 A 是 main 分支 HEAD,B 是针对 main 的 PR 分支 HEAD)。

采用 A/B 策略的场景是 ground truth 出现了这两种情况之一:

  • 会因代码库外部因素而改变:例如安全测试——若某个依赖被发布了 CVE,相关测试就会失败;
  • 过于复杂或变化过快,不适合固化在代码库中:例如大而全的性能基准数据。

A/B 测试的核心机制在 tests/framework/ab_test.py 中实现,其中 git_ab_test() 将指定 revision 检出到临时目录并分别执行 test_runnergit_ab_test_host_command_if_pr() 则区分 PR 语境与非 PR 语境——以 cargo audit 测试为例:PR 内运行时比较 main HEAD 与 PR HEAD 的审计输出是否一致;脱离 PR 运行时则直接断言环境自身状态(即任何现存依赖都无公开 RustSec advisory)。

Functional A/B-Tests(功能一致性)

部分功能型 A/B 测试(例如 test_vulnerabilities.py)会对比 PR 目标分支(如 main)与 PR head 的状态。但在本地运行时,pytest 并不知道当前 commit 属于哪个 PR,无法自行完成 A/B 对比。解决办法是伪造一个 PR 环境——设置 BUILDKITE_PULL_REQUESTBUILDKITE_PULL_REQUEST_BASE_BRANCH 两个环境变量:

BUILDKITE_PULL_REQUEST=true BUILDKITE_PULL_REQUEST_BASE_BRANCH=main ./tools/devtool test -- integration_tests/security/test_vulnerabilities.py

Performance A/B-Tests(性能对比)

性能 A/B 测试有专门的长时编排框架,它们不进入 PR 前 CI,而是安排在合并后(post-merge)定时运行。典型代表是 快照恢复延迟测试——这类测试自身不含任何断言,只通过 aws_embedded_metrics 库发射数据序列。当由编排脚本 tools/ab_test.py 执行时,这些数据序列被收集:脚本用两个不同的 Firecracker 二进制各跑一遍测试,将 A、B 两次运行中维度(dimensions)匹配的数据序列一一对应,并对每条序列执行非参数检验;凡差异达到统计显著水平的数据序列,其关联指标会被打印。显著性判定策略可用 tools/ab_test.py --help 配置。

手动执行一个 A/B 测试的完整命令形态为:

tools/devtool -y test --ab [optional arguments to ab_test.py] run --binaries-a <dir A> --binaries-b <dir B> [optional --artifacts-a <name A> --artifacts-b <name B>] --pytest-opts <test specification>

其中 <dir A><dir B> 是存放待比较性能的 firecracker 与 jailer 二进制的目录;<name A><name B> 是 A/B 运行各自使用的产物名。

devtool build 从任意 git 对象(commit SHA、分支、tag 等)编译二进制,产物会放到 build 下的子目录:

tools/devtool -y build --rev main --release
tools/devtool -y build --rev HEAD --release
tools/devtool -y test --no-build --ab -- run --binaries-a build/main --binaries-b build/HEAD --pytest-opts integration_tests/performance/test_boottime.py::test_boottime

如需下载自定义产物,用 ./tools/devtool download_ci_artifacts <S3 URI>...,产物会放入 build/artifacts 目录。

编写 A/B 兼容测试的三个要点

  • 每条需要做 A/B 的指标必须发射多个数据点:非参数检验作用在数据序列上而非单个数据点上,单一数据点无法构成序列;
  • 在 dimension 集合中放入 performance_test key 且值为测试名tools/ab_test.py 靠 dimensions 匹配两条数据序列——同名序列只有在 dimensions 完全一致时才配对。当 pytest 把 --pytest-opts 展开成多个用例时,若不同用例对不同的数据序列使用了相同 dimensions,脚本会失败并打印违规序列名,因此 performance_test 键是必需的;
  • 把全部 pytest 参数纳入 dimension 集合:例如一个按 vCPU 数参数化的 boottime 测试,若发射序列的 dimensions 只有 {"performance_test": "test_boottime"},脚本将无法区分不同 microVM 规格的数据序列而把它们合并。务必让每个用例的维度对其自身唯一。

局限:只能对 Rust 代码做 A/B

编排式 A/B 只检测体现在 Firecracker 二进制上的性能差异:tools/ab_test.py 只 checkout 被传入的 revision 并执行 cargo build 生成二进制,不会在被 checkout 的 revision 语境下运行集成测试;A、B 两次运行都从同一个 docker 容器、同一份集成测试代码触发。这意味着无法用它评估"只改 Python 代码"(例如开启日志)的影响——只有 Rust 代码可被 A/B。唯一的例外是工具链差异:若两个 revision 都带 rust-toolchain.toml,脚本会用该 revision 指定的工具链编译,而非容器内预装的工具链。

Buildkite 中的自动化与手动调度

每次合并后,只要 PR 触及任何 Rust 代码,系统就会自动跑 A/B 测试,流水线由 .buildkite/pipeline_perf.py 生成。若要在 Buildkite 手动调度一次 A/B,需要在 "New Build" 弹窗的 "Environment Variables" 字段中设置 REVISION_AREVISION_B

超越 commit 对比:任意环境间的比较

自动化的 A/B 套件只支持跨 commit 范围,但脚本也可手动用于任意环境的对比——例如同一二进制在不同宿主机上的表现。做法是:先在各环境里用 devtool 以普通(非 A/B)方式运行测试,这会产出包含 metrics.jsontest_results 目录;然后让 tools/ab_test.py 分析这些目录:

tools/ab_test.py analyze <path to A `test_results`> <path to B `test_results`>

随后输出与前述流程相同的统计分析结果。

生成 A/B 结果可视化(pdf 或 table),使用 tools/ab_plot.py,它消费与 ab_test.py 相同的 metrics.json

 ./tools/ab_plot.py <path to A `test_results`> <path to B `test_results`> --output_type pdf

也可通过 devtool 在预装了依赖的 dev 容器里执行:

 ./tools/devtool sh ./tools/ab_plot.py <path to A `test_results`> <path to B `test_results`> --output_type pdf

注意:对排列组合很多的测试,生成 pdf 输出可能需要一些时间。

A/B 故障排查:一个典型报错

tools/ab_test.py analyze 报出类似错误:

$ tools/ab_test.py analyze <first test-report.json> <second test-report.json>
Traceback (most recent call last):
  File "/firecracker/tools/ab_test.py", line 412, in <module>
    data_a = load_data_series(args.report_a)
  File "/firecracker/tools/ab_test.py", line 122, in load_data_series
    for line in test["teardown"]["stdout"].splitlines():
KeyError: 'stdout'

请检查 AWS_EMF_ENVIRONMENTAWS_EMF_NAMESPACE 是否设置为 local。尤其当数据来自 .buildkite/pipeline_perf.py 生成的 Buildkite 流水线时,务必传入 --step-param env/AWS_EMF_NAMESPACE=local --step-param env/AWS_EMF_SERVICE_NAME=local

添加 Python 测试:理解 fixture 体系

新测试可以放在 tests/ 下任意(既有或新建的)子目录中,文件按 test_*.py 命名,pytest 即会自动发现。

fixture 的两个层次

pytest 中定义的 fixture 让"构造一台 Firecracker microVM"变得极其简单。测试框架把 fixture 分为两层,README 与 conftest.py 的设计注释 保持一致:

消费型(Consumer)fixture——按 microVM 生命周期从早到晚逐步替你完成初始化,按需选择其一:

fixture 已替你完成的初始化 你还需要做什么
uvm 仅构建(已 chroot)的 microVM 自行驱动 spawn / basic_config / start
uvm_configured uvm + spawn + basic_config(及适用的 cpu_template 添加设备后调用 start
uvm_booted uvm_configured + add_net_iface + start 直接可 ssh
uvm_restored uvm_booted,然后做快照并从快照恢复 针对恢复态做验证
uvm_any 启动态恢复态,通过 uvm_lifecycle 自动参数化 用于需要覆盖两种生命周期的用例

维度(Dimension)fixture——控制 microVM 的参数,由上面的消费型 fixture 组合引用,测试也可以直接请求它们。默认值如下:

fixture 默认值 / 行为
guest_kernel 自动参数化到所有受支持 guest 内核(列表见 docs/kernel-policy.md
rootfs_mode "ro"(squashfs,默认)或 "rw"(ext4)
rootfs guest_kernel + rootfs_mode 组合出的 rootfs 磁盘路径(5.10 用 Ubuntu 24.04,其余用 Amazon Linux 2023)
pci_enabled 自动参数化 True / False
cpu_template 默认 None
huge_pages 默认 HugePagesConfig.NONE
vcpu_count / mem_size_mib 默认 2 / 256

用 pin 辅助函数限制维度

要把某个维度限制到特定值或子集,可使用 tests/framework/artifacts.pytests/framework/utils_cpu_templates 提供的辅助函数:

from framework.artifacts import pin_guest_kernel, GUEST_KERNEL_DEFAULT, ACPI_GUEST_KERNELS

@pin_guest_kernel(GUEST_KERNEL_DEFAULT)  # 单一内核,用于不依赖 guest 的测试
def test_foo(uvm): ...

@pin_guest_kernel(ACPI_GUEST_KERNELS)    # 子集
def test_bar(uvm): ...

pin_guest_kernel 本质上是 pytest.mark.parametrize("guest_kernel", ..., indirect=True) 的封装(见 artifacts.py)。

同样的模式也可用在模块级:pytestmark = pin_guest_kernel(...)。但应谨慎使用模块级 pin:默认优先使用 per-test 装饰器,把 pytestmark 留给那些"文件主题本身就要求该限制"的文件,例如:

  • 专测某个特性且该特性本身就依赖特定内核的文件(test_vmclock.py 需要 ACPI;性能热插拔内存测试 因 virtio-mem 成熟度需要内核 >= 6.1);
  • 专门枚举某个维度的文件(如 test_cpu_all.py——该文件的使命就是把每个 CPU template 都跑一遍);
  • 性能测试文件:测量必须基于固定基底,结果才可在时间维度上比较。

对"便利性" pin(例如"本测试与 guest 内核无关,固定到默认内核以加快 CI"),请用 per-test 装饰器。模块级 pytestmark 会悄悄继承到下一个新增的测试上,哪怕那个测试本应覆盖多内核。

关键约束:pytest 的 parametrize 标记不会合并——在同一个维度上同时使用模块级 pytestmark 与 per-test parametrize 会报 "duplicate parametrization" 错误,二选一即可。

推荐做法:不依赖 guest 内核的 Firecracker 功能测试用 @pin_guest_kernel(GUEST_KERNEL_DEFAULT) 保持 CI 快速;涉及 guest 与 Firecracker 交互的测试则保留默认,让每个受支持内核都跑一遍。

大页(huge pages)维度

huge_pages 默认为 HugePagesConfig.NONE(匿名内存)。NONEHUGETLBFS_2MB 的区别只影响性能特征(缺页粒度、UFFD 填充时间、EPT violation 计数),不影响功能正确性。因此,专测内存子系统的性能测试(内存热插拔、balloon 页上报/hinting、快照/UFFD 恢复、EPT 等)应显式在两种变体上参数化:

@pytest.mark.parametrize("huge_pages", HugePagesConfig)
def test_my_perf_thing(uvm_booted, huge_pages):
    ...

功能测试保持默认即可,无需声明该维度。

Markers:nonci 与 no_block_pr

Firecracker 用两个特殊 pytest marker(注册于 tests/pytest.ini)区分测试的运行语境:

  • nonci不进入 PR CI 流水线,而是按各自的 cron 计划在独立流水线中运行(如上面的 Sanitizer 测试);
  • no_block_pr:在"可选"的 PR CI 流水线中运行,该流水线不阻塞 PR 合并。

不带任何 marker 的测试在每个 PR 都会运行,且必须通过才能合并。

添加 Rust 测试

新增 Rust 集成测试只需在 src/vmm/tests/integration_tests.rs 中增加一个标注 #[test] 的函数即可,Cargo 会自动发现并执行。这层测试用于验证 VMM 的程序化 API,与 HTTP 层集成测试互补。

操作 Guest 文件系统

框架提供在 guest 文件系统上读写文件的辅助方法。例如,覆盖 guest 的 init 进程、之后取回一份日志:

def test_with_any_microvm_and_my_init(test_microvm_any):
    # [...]
    test_microvm_any.slot.fsfiles['mounted_root_fs'].copy_to(my_init, 'sbin/')
    # [...]
    test_microvm_any.slot.fsfiles['mounted_root_fs'].copy_from('logs/', 'log')

copy_to() 的源路径相对宿主根目录,目标路径相对 mounted_root_fs 根目录;copy_from() 反之。

警告:在 guest 运行期间向 guest 文件系统拷贝文件属于未定义行为。

调试与故障排查实战

只重跑上次失败的测试

--last-failed 参数只运行上一次运行中失败的测试。做大规模改动导致多个测试失败时,这是最快的收敛手段。

容器内直接运行测试

避免每次测试都进出 Docker,可直接在 Docker 会话内运行:

tools/devtool -y shell --privileged
tools/test.sh integration_tests/functional/test_api.py

test.sh 会先在 /srv 建立 TMPDIR、为安全测试准备 cgroup v2 嵌套环境、按需把测试产物硬链接到 /srv/test_artifacts,随后在 tests 目录下执行 pytest。

用 pdb 单步调试失败用例

追加 --pdb,测试失败时即落入 pdb,可检查局部变量与调用栈,使用标准 Python REPL:

tools/devtool -y test -- -k 1024 integration_tests/performance/test_boottime.py::test_boottime --pdb

改用 ipython 的 ipdb

tools/devtool -y shell --privileged
export PYTEST_ADDOPTS=--pdbcls=IPython.terminal.debugger:TerminalPdb
tools/test.sh -k 1024 integration_tests/performance/test_boottime.py::test_boottime

devtool 已封装了一个更易输入的等价命令:

tools/devtool -y test_debug -k 1024 integration_tests/performance/test_boottime.py::test_boottime

test_debug 内部会带上 --no-build --no-kvm-check --no-build-dir-check --no-artifacts-check(见 tools/devtool),并设置 FC_TEST_SKIP_ARTIFACT_COPY=1

交互式连接 guest 控制台

启用控制台的辅助函数必须在启动 Firecracker 进程之前调用:

uvm.help.enable_console()
uvm.spawn()
uvm.basic_config()
uvm.start()
...

随后若掉入 pdb,可打开一个连接到控制台(经 screen)的 tmux 标签页:

uvm.help.tmux_console()

复现间歇性(flaky)测试

循环运行目标测试,失败时自动落入 pdb:

while true; do
    tools/devtool -y test -- integration_tests/functional/test_balloon.py::test_deflate_on_oom -k False --pdb
done

用 -n 并行运行测试

通过 pytest-xdist 并行。注意并非所有测试都能并行——buildperformance 目录下的测试不应并行。默认串行执行;-n 控制并行度:单独 -n 会按 CPU 数起满 worker,可能过多。经验法则是用一半 CPU:8 核(超线程)笔记本可用 -n4;.metal 上 8 是个好数字,更多收益递减。--dist worksteal 使用 worksteal 调度:

tools/devtool -y test -- integration_tests/functional -n$(expr $(nproc) / 2) --dist worksteal

把 gdb 附加到运行中的 uvm

先让测试失败并落入 PDB,例如:

tools/devtool -y test_debug integration_tests/functional/test_api.py::test_api_happy_start --pdb

然后在 ipdb 提示符中启动 gdbserver:

ipdb> test_microvm.help.gdbserver()

它会输出如何运行 GDB 以附加到该 gdbserver 的指引。

用不同版本的 Firecracker 运行测试

集成测试通常会在初始化时现场编译 Firecracker,但也支持针对另一个版本(例如某个历史 release)运行:

./tools/devtool test -- --binary-dir ../v1.8.0

--binary-dir 指定的目录至少需要包含两个二进制:firecrackerjailer--binary-dir 选项在 tests/conftest.py 中注册,并被 microvm_factory fixture 消费。同理,--custom-cpu-template 可让所有测试默认套用某个自定义 CPU template,除非被测试覆盖。

在 Docker 之外运行测试

已在 Ubuntu 22.04 与 AL2023 上验证。AL2 因 Python 太老(3.8)不可用:

# Fedora/AmazonLinux 请替换为 yum
sudo apt install python3-pip
sudo pip3 install pytest ipython requests psutil tenacity filelock "urllib3<2.0" requests_unixsocket aws_embedded_metrics pytest-json-report pytest-timeout
cd tests
sudo env /usr/local/bin/pytest integration_tests/functional/test_api.py

警告:这等于以 root 身份运行整套测试!这解释了上文 conftest 中 root 权限校验的原因——测试会创建系统级资源(网络命名空间、cgroup 等)。

Sandbox:IPython 交互式 REPL

tools/devtool -y sandbox

这会把你丢进一个 IPython REPL,可在其中与一台 microVM 交互:

uvm.help.print_log()
uvm.get_all_metrics()
uvm.ssh.run("ls")
snap = uvm.snapshot_full()
uvm.help.tmux_ssh()

devtool sandbox -- --help 可查看更多选项。若不使用 Docker,可参考 README 提供的原生运行路径(同样需要以 root 运行并安装 pytest ipython requests psutil tenacity filelock "urllib3<2.0" requests_unixsocket,再以 sudo env PYTHONPATH=tests ... ipython3 -i tools/sandbox.py -- --binary-dir ../repro/v1.4.1 方式启动)。

测试体系的设计目标与选型理由

为什么选 pytest

README 明确列出了选型理由,这些理由可以从配套 Python 代码中得到印证:

  • Python 便于在云环境中工作;
  • Python 内置沙箱(virtualenv)支持;
  • pytest 具备出色的测试发现机制,支持简单、函数式的测试;
  • pytest 拥有强大的 fixture 支持(tests/conftest.py 正是这套能力的集中体现)。

测试产物的内核矩阵

测试产物选择逻辑在 tests/framework/artifacts.py:guest 内核版本模式为 vmlinux-5.10.xvmlinux-6.1.xvmlinux-6.18.x,外加一个 vmlinux-5.10.x-no-acpi(MPTable 引导路径已弃用但仍需覆盖)。所有匹配内核都会进入 ALL_GUEST_KERNELS,被默认的 guest_kernel fixture 自动参数化。

术语对照

  • Testrun:所有(或选定)集成测试的一次沙箱化运行。
  • Test Session:一次 pytest 会话。每个 testrun 一个;sandbox 创建后会启动一次 Test Session。
  • Test:一棵以 test_ 命名的函数,确保 Firecracker 的某个特性、功能参数或质量指标,失败时断言或抛异常。
  • Fixture:返回一个便于添加 Test 的对象的函数(例如一台已启动的 Firecracker microVM),以 @pytest.fixture 标记,定义于 conftest.py 或测试所在文件。
  • Test Case:某个 Test 与其全部参数(含 fixtures)可能状态组成的笛卡尔积中的一个元素。

常见问题(FAQ)要点

  • 已有 shell 测试脚本不想重写:尽量改写成 Python 测试函数;必要时用 shim Python 函数包装调用脚本,也可把它作为镜像资源放入 S3 bucket(会在 microvm.slot.path 下可用),或在测试中拷入 guest 文件系统。
  • 想加一些不打算提交进仓库的测试:testrun 前把你的测试目录放到 tests/ 下即可,pytest 会自动发现整棵测试树。
  • 想用自己的 fixture 且不提交:在测试目录下放一个 conftest.py 定义 fixture,pytest 会让它们对所有测试可见。
  • 想用更多/其他 microVM 测试镜像但不想进公共 S3 bucket:把自定义镜像放到源码树 build/artifacts 子目录,该目录会被 bind-mount 进容器并作为本地镜像缓存。
  • 如何获取实时 logger 输出:修改 tests/pytest.ini 中的 logger 设置。
  • 如何加速集成测试执行:按 "Running" 一节的两种方式收窄选择范围——用 -k substring 只跑名字含某子串的子集,或只跑某文件/目录内的测试。

已知的测试系统 TODO

README 列出的 TODO 与对应源码文件中的注释一致:

功能方面:用 Firecracker Open API spec 生成 Microvm API 资源 URL;用事件驱动方式监听 microvm socket 文件创建以消除 while-spin;自测(测试测试系统本身的测试)。

实现方面:调研 pytest-ordering 以保证测试顺序;建立横跨测试运行器与 pytest 的分层 say 日志系统;按测试函数按需安装依赖;为 tests/* Python 模块补齐类型标注(当前类型提示使用稀疏)。

这些条目意味着测试基础设施本身仍在演进,阅读源码时若遇到相应注释,即可对照理解。

结语

Firecracker 的集成测试体系是"契约守护"思想的落地:Python 层通过 HTTP API 做黑盒验证,Rust 层直接驱动 VMM 程序化接口,Sanitizer 与 A/B 测试则分别覆盖内存安全与"无基准"的性能/安全回归。掌握 tools/devtool 的运行范式、FC_TEST_* 环境变量、conftest.py 中两层次的 fixture 体系以及 tools/ab_test.py 的 A/B 编排模型,就能在本地高效复现 CI 问题、为仓库贡献高质量的测试用例,并在不触碰源码的情况下完成跨 commit、跨环境的行为对比。对测试系统本身的深入理解,反过来也能帮助你更准确地解读 Firecracker 在安全性与性能上的真实承诺。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390