首页
/ Firecracker Metrics 监控体系完全指南:从 PUT /metrics 配置到 JSON 指标字段深度解析

Firecracker Metrics 监控体系完全指南:从 PUT /metrics 配置到 JSON 指标字段深度解析

2026-09-08 14:06:25作者:齐添朝

Firecracker 的 Metrics 系统为每台 microVM 提供统一、低开销的运行时监控能力:它以 JSON 格式按固定周期(默认每 60 秒)或按需向命名管道或普通文件冲刷指标,覆盖 API Server、virtio 设备、vCPU、MMDS、seccomp、信号处理等全部核心子系统。本文以 docs/metrics.md 为主线,结合 vmm 与 firecracker 源码,系统讲解 Metrics 系统的两种配置入口(CLI 与 PUT /metrics)、可选字段(emit_idproperties)、刷新机制,以及如何从源码层解读每一条 JSON 指标行的含义与单位,帮助读者搭建可落地的 Firecracker 可观测性方案。

Metrics 系统整体架构:统一入口与"启动即固定"的生命周期

Firecracker 的全过程监控依赖一套单一的 Metrics 系统METRICS 全局实例),它与其他资源(如 vCPU、内存、设备)的配置相互独立。根据 docs/metrics.md 的说明,该系统可以通过两种等价的方式初始化:

  1. 向 API Server 发送 PUT 请求到 /metrics 路径(经 src/firecracker/src/api_server/request/metrics.rsparse_put_metrics 解析为 VmmAction::ConfigureMetrics,最终调用 vmm_config::metrics::init_metrics);
  2. 在启动命令行中通过 --metrics-path CLI 选项指定(对应 src/firecracker/src/main.rs 中定义的 Argument::new("metrics-path"),help 文案为 "Path to a fifo or a file used for configuring the metrics on startup.")。

需要注意两条重要的生命周期约束:

  • Metrics 配置不属于 Guest 配置的一部分,不会在快照(snapshot)恢复时被还原。也就是说,快照恢复后的新进程需要重新配置其 Metrics 输出目标。
  • 所有 Metrics 配置(包括可选的 emit_idproperties)都是在 microVM boot 前设置一次,并在整台 microVM 的生命周期内保持固定,不提供运行时热更新。

从源码看,这套系统的设计目标(src/vmm/src/logger/metrics.rs 的模块文档)可以总结为四点:尽量采用无需锁的原子读写操作;借助内部可变性与原子类型的 Sync 特性,使所有方法(包括语义上可变的写操作)都能作用于一个 static 全局量;序列化交给 serde 完成;全部指标从 0 开始,统一派生 Default。而对外暴露的全局实例定义在 src/vmm/src/logger/metrics.rs 中:

pub static METRICS: Metrics<FirecrackerMetrics, FcLineWriter> =
    Metrics::<FirecrackerMetrics, FcLineWriter>::new(FirecrackerMetrics::new());

其内部通过 OnceLock<Mutex<M>> 保存输出 writer,保证 init 只被成功执行一次,重复初始化会返回 MetricsError::AlreadyInitialized

前置准备:创建指标输出目标

Metrics 以每行一个完整 JSON 对象的形式写入输出目标。输出目标可以是命名管道(FIFO)普通文件。配置 Metrics 之前需要先创建对应资源(docs/metrics.md):

# 创建所需的命名管道:
mkfifo metrics.fifo

# Metrics 系统同样支持普通文件:
touch metrics.file

选择命名管道的典型场景是:由宿主侧的独立消费者进程(如日志采集器)实时读取每一条指标;选择普通文件的场景则是希望指标落盘、事后用 cat/采集器批量处理。源码中 writer 使用 utils::open_file_nonblock 打开路径并以 FcLineWriter 包装(src/vmm/src/vmm_config/metrics.rs),因此目标文件在每次写指标时都是以追加一行的方式输出。

通过 CLI 配置 Metrics

启动 Firecracker 时直接传入 --metrics-path 即可把 Metrics 输出指向指定文件或 FIFO(docs/metrics.md):

./firecracker --metrics-path metrics.fifo

注意 --metrics-path 只负责设置输出路径,不会处理 emit_id/properties 等可选字段;若要配置这些字段,需要使用下述 API 方式或 --config-filemetrics 块。

通过 API(PUT /metrics)配置 Metrics

API 方式是功能最完整的配置入口,示例(docs/metrics.md):

curl --unix-socket /tmp/firecracker.socket -i \
    -X PUT "http://localhost/metrics" \
    -H "accept: application/json" \
    -H "Content-Type: application/json" \
    -d "{
             \"metrics_path\": \"metrics.fifo\"
    }"

请求体被反序列化为强类型的 MetricsConfig 结构(定义在 src/vmm/src/vmm_config/metrics.rs),其字段如下:

字段 类型 必填 说明
metrics_path string 命名管道或文件的路径,JSON 格式的指标将冲刷到此
emit_id boolean 否(默认 false 是否在每条指标行的顶层输出 id 字段
properties object(string→string) 由运维自定义的键值对,原样输出到顶层 properties 字段

这与 src/firecracker/swagger/firecracker.yamlMetrics 定义(Swagger 中 Metrics 组件的 definitions)完全对应:metrics_path 是唯一必填项,emit_id 默认 falseproperties 为字符串到字符串的 map。字段的默认值行为也有对应的单元测试佐证(test_emit_id_defaults_to_false),即缺省时 emit_idfalsepropertiesNone。配置完成后,Metrics 会以 JSON 格式持续写入 metrics_path

可选顶层字段:emit_id 与 properties

默认情况下,每条指标行只携带时间戳加各组件指标。为了在多实例汇聚场景中区分指标来源,Firecracker 提供了两个各自独立开启、默认关闭的可选顶层字段(docs/metrics.md):

id:标记 microVM 实例

emit_id 设为 true 后,每条指标行的顶层会携带 id 字段,其值即启动时传给 --id 的实例 id(缺省为 anonymous-instance)。当多台 microVM 的指标被汇聚到同一目的地时,通过 id 即可把每行指标归属到具体实例。

从源码实现看,这个字段由 InstanceIdField 负责(src/vmm/src/logger/metrics.rs):内部是一个 AtomicBool enabled 与全局 INSTANCE_ID(取不到时回退 DEFAULT_INSTANCE_ID),仅在开启时通过 serde 的 flatten 序列化输出一个 "id" 键;MetricsConfigemit_id == true 时会调用 METRICS.id.enable()。对应的序列化单元测试 test_instance_id_serialize_disabled / test_instance_id_serialize_enabled 也验证了"未开启时输出 {}、开启后包含 "id""的两种形态。

properties:运维自定义标签

设置 properties 后,该 map 会被原样输出到顶层 properties 字段(注意 map 经 BTreeMap 存储,输出按键排序)。源码中对应 MetricsProperties 结构(src/vmm/src/logger/metrics.rs),其内部用 OnceLock<BTreeMap<String, String>> 保存、同样只能 set 一次,重复设置返回 AlreadyInitialized。测试 test_metrics_properties_serialize_set 精确验证了输出形如 {"properties":{"bundle_id":"fn-abc","customer_id":"1234"}}

配置示例与输出对比

通过 API 请求体同时配置两个字段(docs/metrics.md):

curl --unix-socket /tmp/firecracker.socket -i \
    -X PUT "http://localhost/metrics" \
    -H "accept: application/json" \
    -H "Content-Type: application/json" \
    -d "{
             \"metrics_path\": \"metrics.fifo\",
             \"emit_id\": true,
             \"properties\": {
                 \"customer_id\": \"1234\",
                 \"bundle_id\": \"fn-abc\"
             }
    }"

同样的字段可以放进 --config-file 启动配置文件的 metrics 块(docs/metrics.md):

{
    "metrics": {
        "metrics_path": "metrics.fifo",
        "emit_id": true,
        "properties": {
            "customer_id": "1234",
            "bundle_id": "fn-abc"
        }
    }
}

两个字段都不配置时,一行指标只有默认键,大致形如:

{"utc_timestamp_ms": 1739000000000, "api_server": {"...": 0}}

两个字段都配置后,同一行会额外携带实例 id 与自定义属性(docs/metrics.md):

{"utc_timestamp_ms": 1739000000000, "id": "my-instance", "properties": {"bundle_id": "fn-abc", "customer_id": "1234"}, "api_server": {"...": 0}}

指标刷新机制:60 秒定时冲刷与按需 FlushMetrics

指标通过两种方式被冲刷到输出目标(docs/metrics.md):

  • 无需人工干预:Firecracker 内部每 60 秒自动冲刷一次;
  • 按用户需求:通过 PUT /actions 发送 FlushMetrics 请求触发即时冲刷。

源码证据位于 src/firecracker/src/metrics.rs:常量 WRITE_METRICS_PERIOD_MS: u64 = 60000,主流程用一个 TimerFdwrite_metrics_event_fd)注册到事件管理器,定时到期后调用 METRICS.write() 写出一行完整 JSON;FlushMetrics 则直接对应 docs/api_requests/actions.md 中描述的 action:

curl --unix-socket /tmp/firecracker.socket -i \
    -X PUT "http://localhost/actions" \
    -d '{ "action_type": "FlushMetrics" }'

METRICS.write()src/vmm/src/logger/metrics.rs)的具体动作是:通过 serde_json::to_writer 将整个 FirecrackerMetrics 序列化为 JSON,随后再写一个换行符 \n,因此输出目标的每条记录都是独立的单行 JSON,天然适合按行解析的采集管线。

消费指标输出的两种方式

如果配置的是命名管道,可以用下面的脚本持续读取(docs/metrics.md):

metrics=metrics.fifo

while true
do
    if read line <$metrics; then
        echo $line
    fi
done

echo "Reader exiting"

如果输出目标是普通文件,直接查看即可(docs/metrics.md):

cat metrics.file

单行 JSON 指标中可能出现的顶层键

Firecracker 每条指标 JSON 对象可能包含以下键(docs/metrics.md):

"api_server"
"balloon"
"block"
"deprecated_api"
"entropy"
"get_api_requests"
"i8042"
"latencies_us"
"logger"
"mmds"
"net"
"patch_api_requests"
"put_api_requests"
"rtc"
"seccomp"
"signals"
"uart"
"vcpu"
"vhost_user_block"
"vmm"
"vsock"

从源码视角看,这些键几乎一一对应 src/vmm/src/logger/metrics.rsFirecrackerMetrics 结构的字段。其中 utc_timestamp_ms 是每一行的第一个键,由 SerializeToUtcTimestampMs 在序列化时用实时时钟(ClockType::Real)即时生成(毫秒精度)。设备类指标(balloon/block/net/vsock 等)则通过 serde 的 #[serde(flatten)] 代理结构(如 create_serialize_proxy! 宏生成的 BlockMetricsSerializeProxy)在序列化过程中调用各设备 metrics 模块的 flush_metrics,从而把多设备指标扁平地合并进同一 JSON 对象。

各指标的源码定义位置

下表给出了各指标键对应的设备与定义文件(docs/metrics.md,链接已转换为仓库根目录相对路径):

Metrics key Device Additional comments
balloon BalloonDeviceMetrics 表示 Balloon 设备的指标。
block BlockDeviceMetrics 表示 Virtio Block 设备的聚合指标。
block_{block_drive_id} BlockDeviceMetrics 表示 /drives/{drive_id} 端点对应 Virtio Block 设备的指标,例如 "block_rootfs": 表示端点 /drives/rootfs 的指标。
i8042 I8042DeviceMetrics 表示 i8042 设备特有的指标。
net NetDeviceMetrics 表示 Virtio Net 设备的聚合指标。
net_{iface_id} NetDeviceMetrics 表示 /network-interfaces/{iface_id} 端点对应 Virtio Net 设备的指标,例如 net_eth0 表示端点 /network-interfaces/eth0 的指标。
rtc RTCDeviceMetrics 表示 RTC 设备特有的指标。注意:仅在 aarch64 上输出。
uart SerialDeviceMetrics 表示串口设备特有的指标。
vhost_user_{dev}_{dev_id} VhostUserDeviceMetrics 表示设备 dev 与设备 id dev_id 的 Vhost-user 设备指标,例如 "vhost_user_block_rootfs": 表示端点 /drives/rootfs 的 vhost-user block 设备指标。
vsock VsockDeviceMetrics 表示 vsock 设备特有的指标。
entropy EntropyDeviceMetrics 表示 entropy(virtio-rng)设备特有的指标。
"api_server"
"deprecated_api"
"get_api_requests"
"latencies_us"
"logger"
"mmds"
"patch_api_requests"
"put_api_requests"
"seccomp"
"signals"
"vcpu"
"vmm"
metrics.rs 其余指标均定义在同一文件 metrics.rs 中。

关键行为:所有组件键都会被输出

Firecracker 会无条件输出全部上述指标键,无论对应组件是否真的挂载docs/metrics.md)。例如即使 microVM 没有挂载 vsock 设备,输出中仍会出现键 vsock,其值为 VsockDeviceMetrics 中定义的所有指标全部为 0。这一"全量骨架、零值填充"的设计让下游解析器可以按固定 schema 处理,无需感知每台实例的差异。同理,rtc 仅在 aarch64 平台输出(源码层面表现为 LegacyDevMetricsSerializeProxy 在 aarch64/x86_64 上注册的 legacy 设备集合不同,src/vmm/src/devices/legacy 中 serial.rs 同时承载了 RTC 与串口两种设备的指标结构)。

指标计数语义:SharedIncMetric 与 SharedStoreMetric

想要正确解读数值,需要理解 Firecracker 底层两类指标原语(src/vmm/src/logger/metrics.rs):

  • SharedIncMetric(增量计数指标):内部保存"当前值 + 上一次冲刷值"两个 AtomicU64。序列化(即冲刷)时输出二者之差 current - previous,成功后把 previous 更新为 current。因此这类指标(如各类 API 请求次数、设备事件计数)在每次冲刷后自动归零,两次输出之间的差值即这段时间的新增量。
  • SharedStoreMetric(持久存储指标):只保存单一值,冲刷时原样输出、不清零。适合"状态/瞬时值"语义,例如 api_server.process_startup_time_us、seccomp 的 num_faults、vmm 的 panic_count,以及 vmm 级的 latencies_us 下各类快照操作耗时。

此外还有聚合型延迟指标 LatencyAggregateMetrics,以 min_us / max_us / sum_us 三元组记录某一操作的耗时极值与累计,sum_us 是增量计数的(冲刷清零),min_us/max_us 是持久存储的。使用方只需 metrics.record_latency_metrics() 生成一个 RAII 风格的 recorder,drop 时自动把时间差写回(这正是单元测试之外最常用的埋点方式)。一个典型示例是 vCPU 指标中的 exit_io_in_agg:用 exit_io_in_agg.sum_us 除以 exit_io_in 即可算出单次 KVM I/O 退出处理的平均耗时。

由于 vCPU 指标可能被多个 vCPU 线程同时递增,SharedIncMetric/SharedStoreMetric 均基于 AtomicU64 实现(fetch_add/storeOrdering::Relaxed),无需额外加锁即可并发安全。需要注意一个附带效应:任何一次序列化都会同时执行增量计数器的"复位",因此对指标对象的任何打印/序列化都应视为一次冲刷。

从指标名提取单位:内置的命名约定

Firecracker 指标的单位直接内嵌在字段名后缀中,无需额外元数据表。官方给出的提取伪代码(docs/metrics.md)如下:

    if substring "_bytes" or "_bytes_count" is present in any subkey of full_key
        Unit is "Bytes"
    else substring "_ms" is present in any subkey of full_key
        Unit is "Milliseconds"
    else substring "_us" is present in any subkey of full_key
        Unit is "Microseconds"
    else
        Unit is "Count"

判断顺序是从"字节/毫秒/微秒"逐步收窄到最后默认的"次数"。例如:

  • 顶层 utc_timestamp_ms —— 含 _ms,单位为毫秒(Unix 毫秒时间戳);
  • 全文键 vcpu.exit_io_in_agg.min_us —— 含 _us,单位为微秒
  • _bytes_bytes_count 的字段(如 MMDS 的 tx_bytes)—— 单位为字节
  • 其余不含任何单位后缀的字段 —— 单位为次数(Count)

以真实的 vcpu.exit_io_in_agg.min_us 为例,规则会匹配到 _us 而判定为微秒,这与代码中 LatencyAggregateMetrics 字段名 min_us/max_us/sum_us 以及 PerformanceMetrics(即顶层 latencies_us)中 *_snapshot 等以 _us 结尾的字段完全一致。

归纳:一条指标行的完整解读

综合以上内容,一条典型的 Firecracker 指标行可以按"时间戳 + 可选标签 + 各组件数值"来解读:

{
  "utc_timestamp_ms": 1739000000000,
  "id": "my-instance",
  "properties": { "bundle_id": "fn-abc", "customer_id": "1234" },
  "api_server": { "process_startup_time_us": 0, "process_startup_time_cpu_us": 0 },
  "put_api_requests": { "metrics_count": 1, "metrics_fails": 0 },
  "vcpu": { "exit_io_in": 0, "exit_io_in_agg": { "min_us": 0, "max_us": 0, "sum_us": 0 } },
  "block_rootfs": { "...": 0 },
  "vsock": { "...": 0 }
}
  • 顶层的 utc_timestamp_ms 标明冲刷时刻;idpropertiesemit_id/properties 可选开启,用于多实例汇聚溯源;
  • api_server*_api_requestsloggerseccompsignalsmmdsvcpulatencies_us 等键定义于 src/vmm/src/logger/metrics.rs
  • 设备相关键(blockblock_rootfsnetnet_eth0balloonvsockentropy、vhost-user 系列)由对应设备 metrics 模块在序列化时合并进来,未挂载的设备一律输出零值;
  • 每次输出后,SharedIncMetric 型计数自动复位,适合按 60 秒窗口做增量聚合;SharedStoreMetric 型持久值则适合读取瞬时状态与累计量。

配置与排障的落地建议:优先使用 --config-file 中的 metrics 块或 PUT /metrics 一次性配置好 metrics_path(必要时开启 emit_id/properties),在 microVM boot 之前完成;启动后通过"读取 FIFO/文件 + 60 秒周期或 FlushMetrics"持续获得观测数据;解析时依据上文单位规则与计数语义,即可准确换算为字节/毫秒/微秒/次数,用于告警、容量评估与性能分析。

如需进一步了解字段的 API 定义细节,可直接查阅 Swagger 定义 src/firecracker/swagger/firecracker.yamlMetrics 组件的 schema;关于 FlushMetrics action 的完整用法可参考 docs/api_requests/actions.md

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

项目优选

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