[i18n] print_printable_section [i18n] print_click_to_print.

[i18n] print_show_regular.

Latest

简介

HUATUO(华佗) 是由滴滴开源并依托 CCF (中国计算机学会) 孵化的操作系统深度可观测项目,专注为复杂云原生通用计算,AI 计算,裸金属基础服务等提供操作系统内核级深度观测能力。该项目核心成员为一群开源技术爱好者,基础技术研究者。

内核版本

理论支持 4.18 之后的所有版本,发行版测试矩阵覆盖以下 guest 环境:

No. OS Kernel Cgroup Clang libbpf glibc gcc make Python Java Rust
1 Ubuntu 26.04 LTS 7.0.0-30-generic v2 21.1.8 1.6.3 2.43 15.2.0 4.4.1 3.14.4 17.0.20.1+1 1.93.1
2 Ubuntu 24.04.4 LTS 6.8.0-138-generic v2 15.0.7 1.3.0 2.39 13.3.0 4.3 3.12.3 17.0.20.1+1 1.75.0
3 Ubuntu 22.04.5 LTS 5.15.0-190-generic v2 15.0.7 0.5.0 2.35 11.4.0 4.3 3.10.12 17.0.20.1+1 1.75.0
4 Ubuntu 20.04.6 LTS 5.4.0-216-generic hybrid 12.0.0 0.5.0 2.31 9.4.0 4.2.1 3.8.10 17.0.20.1+1 1.75.0
5 Debian 13 (trixie) 6.12.107+deb13-cloud-amd64 v2 19.1.7 1.5.0 2.41 14.2.0 4.4.1 3.13.5 17.0.20.1+1 1.85.1
6 Debian 12 (bookworm) 6.1.0-53-cloud-amd64 v2 15.0.6 1.1.2 2.36 12.2.0 4.3 3.11.2 17.0.20.1+1 1.63.0
7 Rocky Linux 9.8 5.14.0-687.10.1.el9_8.0.1.x86_64 v2 21.1.8 1.5.0 2.34 11.5.0 4.3 3.9.25 17.0.20.1+1 1.92.0
8 Rocky Linux 8.10 4.18.0-553.el8_10.x86_64 v1 21.1.8 0.5.0 2.28 8.5.0 4.2.1 3.6.8 17.0.20.1+1 1.92.0
9 Fedora Linux 44 6.19.10-300.fc44.x86_64 v2 22.1.8 1.6.3 2.43 16.2.1 4.4.1 3.14.3 17.0.20.1+1 1.98.1
10 openEuler 24.03 LTS-SP4 6.6.0-159.4.3.154.oe2403sp4.x86_64 v1 17.0.6 1.2.2 2.38 12.3.1 4.4.1 3.11.6 17.0.20.1+1 1.96.0
11 openEuler 22.03 LTS-SP4 5.10.0-216.0.0.115.oe2203sp4.x86_64 v1 12.0.1 0.8.1 2.34 10.3.1 4.3 3.9.9 17.0.20.1+1 1.82.0
12 Anolis OS 8.10 5.10.134-18.an8.x86_64 v1 18.1.8 0.5.0 2.28 8.5.0 4.2.1 3.6.8 17.0.20.1 1.79.0
13 OpenCloudOS 9.6 6.6.119-51.5.oc9.x86_64 v2 17.0.6 1.2.2 2.38 12.3.1 4.4.1 3.11.6 17.0.20.1 1.96.0

联系我们

  • 微信群(备注姓名+单位)和公众号:

1 - 快速开始

为帮助大家快速体验、部署 HUATUO, 该文档分别从 极速体验,容器启动,编译部署 三部分说明。

1. 极速体验

你可以直接登陆示例网站访问前端监控大盘示例,如内核指标、异常事件、火焰图等(账户:huatuo 密码:huatuo1024)。

2. 容器启动

HUATUO 组件数据流示意图

2.1 Docker 启动

通过 docker 启动已经编译好的容器镜像(注意:该方式默认关闭了获取容器信息功能,和 ES 存储功能)。

  1. 启动容器:
$ docker run --privileged --pid=host --cgroupns=host --network=host -v /sys:/sys -v /proc:/proc -v /run:/run huatuo/huatuo-bamai:latest
  1. 获取指标:打开另外一个终端,通过 curl 获取。
$ curl -s localhost:19704/metrics
  1. 查看异常事件 (Events, AutoTracing):HUATUO 会将采集到的内核异常事件信息在 ES (已关闭),和本地目录 huatuo-local 分别存储。注意:通常该路径下没有任何文件(正常状态的系统不会触发事件采集),你可以通过构造异常场景或者修改配置文件阈值产生事件。

2.2 Docker Compose 启动

通过 docker compose,可以快速地在本地搭建部署一套完整的环境。该命令拉取最新镜像,启动 elasticsearch, prometheus, grafana,huatuo-bamai 等组件。命令执行成功后,打开浏览器访问 http://localhost:3000 即可浏览监控大盘(grafana 默认管理员账户:admin 密码:admin; 系统正常状态不会触发 Events, AutoTracing)。

$ docker compose --project-directory ./build/docker up

HUATUO 组件之 huatuo-bamai 运行示意图

3. 编译部署

3.1 编译

为隔离开发者本地环境和简化编译流程,我们提供容器化编译,你可以直接通过 docker build,构建完成的镜像(包含底层采集器 huatuo-bamai、bpf obj、工具等)。在项目根目录运行:

$ docker build --network host -t huatuo/huatuo-bamai:latest .

3.2 运行

运行容器:

$ docker run --privileged --pid=host --cgroupns=host --network=host -v /sys:/sys -v /proc:/proc -v /run:/run huatuo/huatuo-bamai:latest

或从容器 /home/huatuo-bamai 路径下拷贝出所有文件后本地手动运行:

$ ./huatuo-bamai --region example --config huatuo-bamai.conf

注意:可使用 systemd/supervisord/k8s-DaemonSet 等方式托管运行。

3.3 配置

  1. 配置容器信息 HUATUO 通过调用 kubelet 接口获取POD/容器信息。你可以根据实际环境配置访问接口和证书,KubeletAuthorizedPort = 0, KubeletReadOnlyPort = 0 表示禁用该功能。

      [Pod]
        KubeletClientCertPath = "/etc/kubernetes/pki/apiserver-kubelet-client.crt,/etc/kubernetes/pki/apiserver-kubelet-client.key"
    
  2. 配置存储

    • 指标存储 (Metric): 所有的指标都存储在 prometheus,你可以通过访问 :19704/metrics 接口获取指标。

    • 异常事件存储 (Events, AutoTracing): 所有的内核事件,和 Autotracing 事件都存储在 ES。注意:如果配置为空表示不启动 ES 存储,只在本地目录 huatuo-local 存储事件。

      ES 存储配置如下:

      [Storage.Elasticsearch]
          Address = "http://127.0.0.1:9200"
          Username = "elastic"
          Password = "huatuo-bamai"
          Index = "huatuo_bamai"
      

      本地存储配置如下:

      # tracer's record data
      # Path: all but the last element of path for per tracer
      # RotationSizeMiB:单个记录文件轮转前的最大容量
      # MaxRotatedFiles:保留的轮转文件数量上限
      [Storage.LocalFile]
          Path = "huatuo-local"
          RotationSizeMiB = 100
          MaxRotatedFiles = 10
      
  3. 事件阈值 所有的内核事件采集 Events 和 AutoTracing 都可以配置触发阈值。默认的阈值都是在实际生产环境反复验证后的经验数据,你可以根据自身需求,在 huatuo-bamai.conf 中修改阈值。

  4. 资源限制 Kubernetes 和 systemd 部署默认由 kubelet 或 systemd 管理 Huatuo 的 cgroup,Huatuo 不会迁移自身 PID。只有直接运行且没有外部管理器时,才使用 --enable-cgroup 并配置:

    [Runtime]
        StartupCPULimitCores = 0.5
        CPULimitCores = 2.0
        MemoryLimitMiB = 2048
    

2 - 应用部署

HUATUO (华佗) 社区提供多种部署方式,具体如下:

2.1 - 容器部署

镜像下载

镜像仓库地址:https://hub.docker.com/r/huatuo/huatuo-bamai/tags

使用 Docker 启动容器

docker run --detach \
  --name huatuo-bamai \
  --restart unless-stopped \
  --privileged \
  --pid=host \
  --cgroupns=host \
  --network=host \
  --cpus=2 \
  --memory=2g \
  --volume /sys:/sys \
  --volume /proc:/proc \
  --volume /run:/run \
  huatuo/huatuo-bamai:latest

注意:容器内置的默认配置不会连接 kubelet 和 Elasticsearch。

生产环境应限制 CPU 和内存,避免异常采集任务影响宿主机业务。Docker 的容器 cgroup 负责资源限制;Huatuo 默认不创建自身 cgroup,也不要在 Docker 部署中传入 --enable-cgroup。

通过以下命令确认限制已经生效,并观察实际使用量:

docker inspect huatuo-bamai \
  --format 'NanoCPUs={{.HostConfig.NanoCpus}} Memory={{.HostConfig.Memory}}'
docker stats huatuo-bamai

示例值为初始基线,应根据节点规格、采集任务和资源峰值调整。

使用 Docker Compose 启动容器

通过 Docker Compose 可在本地快速搭建一套完整环境,自行管理采集器、Elasticsearch、Prometheus、Grafana 等组件。

$ docker compose --project-directory ./build/docker up

Docker Compose 安装方法请参阅 https://docs.docker.com/compose/install/linux/。

2.2 - 集群部署

本文介绍如何通过 Kubernetes DaemonSet 将华佗采集器部署到集群。

1. Kubernetes 清单部署

1.1 获取配置文件

curl -L -o huatuo-bamai.conf https://github.com/ccfos/huatuo/raw/main/huatuo-bamai.conf

1.2 修改配置文件

根据实际部署环境修改配置文件,例如调整存储后端、Pod 信息获取方式等配置项,详见配置指南。

1.3 创建 ConfigMap

kubectl create configmap huatuo-bamai-config \
  --namespace default \
  --from-file=./huatuo-bamai.conf \
  --dry-run=client -o yaml |
kubectl apply -f -

1.4 部署采集器

下载 DaemonSet 清单:

curl -L -o huatuo-daemonset.yaml \
  https://raw.githubusercontent.com/ccfos/huatuo/main/build/huatuo-daemonset.minimal.yaml

部署到生产环境前,将 huatuo 容器的 resources 调整为以下初始基线:

resources:
  limits:
    cpu: "2"
    memory: 2Gi
  requests:
    cpu: "2"
    memory: 2Gi

应用修改后的清单:

kubectl apply -f ./huatuo-daemonset.yaml

requests 提供调度保障,limits 由 kubelet 写入 Pod cgroup,负责限制 Huatuo 的 CPU 和内存。Huatuo 默认不创建自身 cgroup,因此不会把进程移出 kubepods 层级,containerd 重启和 Pod 删除时可以正常回收进程。

示例值为初始基线,requests 与 limits 相同,因此 Pod 为 Guaranteed QoS。 只有显式传入 --enable-cgroup 时 [Runtime] 才生效;Kubernetes 部署不要传入 该参数。

1.5 验证部署

kubectl rollout status daemonset/huatuo \
  --namespace default \
  --timeout=10m

kubectl get pods \
  --namespace default \
  --selector app=huatuo \
  --output wide

kubectl get daemonset huatuo \
  --namespace default \
  --output jsonpath='{.spec.template.spec.containers[?(@.name=="huatuo")].resources}'

更新 huatuo-bamai.conf 后,重新执行 1.3 节更新 ConfigMap,并手工触发滚动更新:

kubectl rollout restart daemonset/huatuo --namespace default

2. Helm 部署

Helm Chart 位于 build/charts/。

2.1 检查部署环境

管理机需要安装 Helm 和 kubectl,并能够访问目标 Kubernetes 集群。

(command -v helm || curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash) && helm version

TARGET_CONTEXT="$(kubectl config get-contexts -o name | sed -n '1p')"
kubectl --context "${TARGET_CONTEXT}" get nodes

确认目标 Node 状态为 Ready。

2.2 准备配置文件

按照 1.1 和 1.2 节获取并修改 huatuo-bamai.conf。

2.3 配置部署参数

创建 values-production.yaml:

image:
  repository: <所有Node均可访问的镜像仓库>/huatuo-bamai
  tag: "<固定发行版本>"
  pullPolicy: IfNotPresent

resources:
  limits:
    cpu: "2"
    memory: 2Gi
  requests:
    cpu: "2"
    memory: 2Gi

nodeSelector:
  kubernetes.io/os: linux

tolerations:
  - operator: Exists

hostPaths:
  proc: /proc
  sys: /sys
  run: /run
  var: /var
  etc: /etc
  data: /var/log/huatuo/huatuo-local

2.4 检查 Helm Chart

helm lint ./build/charts \
  -f ./values-production.yaml \
  --set-file config.content=./huatuo-bamai.conf

helm template huatuo ./build/charts \
  --namespace huatuo \
  -f ./values-production.yaml \
  --set-file config.content=./huatuo-bamai.conf \
  >/dev/null

2.5 部署采集器

helm upgrade --install huatuo ./build/charts \
  --kube-context "${TARGET_CONTEXT}" \
  --namespace huatuo \
  --create-namespace \
  -f ./values-production.yaml \
  --set-file config.content=./huatuo-bamai.conf \
  --atomic \
  --timeout 10m

2.6 验证部署

helm status huatuo \
  --kube-context "${TARGET_CONTEXT}" \
  --namespace huatuo

kubectl --context "${TARGET_CONTEXT}" \
  --namespace huatuo \
  get daemonset,configmap,pod --output wide

kubectl --context "${TARGET_CONTEXT}" \
  --namespace huatuo \
  rollout status daemonset/huatuo \
  --timeout=10m

查看采集器日志:

kubectl --context "${TARGET_CONTEXT}" \
  --namespace huatuo \
  logs \
  --selector app.kubernetes.io/name=huatuo \
  --prefix \
  --tail=100

2.7 更新和回滚

修改镜像版本或 huatuo-bamai.conf 后,重新执行 2.5 节的命令。

查看发布历史并回滚到指定版本:

helm history huatuo \
  --kube-context "${TARGET_CONTEXT}" \
  --namespace huatuo

helm rollback huatuo <Revision> \
  --kube-context "${TARGET_CONTEXT}" \
  --namespace huatuo \
  --wait \
  --timeout 10m

2.3 - 物理机部署

生产环境资源限制

huatuo-bamai.service 由 systemd 负责资源限制和进程生命周期。服务单元默认不启用 Huatuo 自身 cgroup,使用以下原生配置:

[Service]
CPUAccounting=yes
CPUQuota=200%
MemoryAccounting=yes
MemoryMax=2G
TasksAccounting=yes
TasksMax=32768
KillMode=control-group

CPUQuota=200% 表示最多使用 2 个 CPU 核,MemoryMax=2G 限制服务内存。根据主机规格、采集任务和资源峰值调整这些值。systemd 部署不要传入 --enable-cgroup,否则进程会脱离 service cgroup。

服务启动后,检查状态和 cgroup:

systemctl status huatuo-bamai --no-pager
systemd-cgls --unit huatuo-bamai.service

二进制

HUATUO Release 提供 amd64 和 arm64 的 Linux 静态 tar 包。tar 包包含 huatuo-bamai 和 huatuo-apiserver 二进制文件、配置文件和 BPF 对象。

本节命令适用于发布资产名为 huatuo-bamai-<version>-static-linux-<arch>.tar.gz 的版本。当前该命名从 v2.2.0 开始提供;v2.0.0 和 v2.1.0 的 tar 包名不同,不能直接套用以下命令。

以下命令使用 HUATUO_VERSION 表示目标版本,可按实际发行版本调整,例如 v2.2.0:

HUATUO_VERSION="<release-version>"

1. 下载 tar 包

x86_64 主机下载 amd64 包:

wget "https://github.com/ccfos/huatuo/releases/download/${HUATUO_VERSION}/huatuo-bamai-${HUATUO_VERSION}-static-linux-amd64.tar.gz"

aarch64 主机下载 arm64 包:

wget "https://github.com/ccfos/huatuo/releases/download/${HUATUO_VERSION}/huatuo-bamai-${HUATUO_VERSION}-static-linux-arm64.tar.gz"

2. 安装 tar 包

创建安装、日志和数据目录:

sudo install -d -m 0755 /opt/huatuo-bamai /var/log/huatuo-bamai /var/lib/huatuo-bamai

amd64:

sudo tar -xzf "huatuo-bamai-${HUATUO_VERSION}-static-linux-amd64.tar.gz" --strip-components=1 --no-same-owner -C /opt/huatuo-bamai

arm64:

sudo tar -xzf "huatuo-bamai-${HUATUO_VERSION}-static-linux-arm64.tar.gz" --strip-components=1 --no-same-owner -C /opt/huatuo-bamai

3. 安装服务单元文件

从对应版本源码下载服务单元文件:

sudo wget -O /etc/systemd/system/huatuo-bamai.service "https://raw.githubusercontent.com/ccfos/huatuo/${HUATUO_VERSION}/build/rpm/huatuo-bamai.service"
sudo wget -O /etc/systemd/system/huatuo-apiserver.service "https://raw.githubusercontent.com/ccfos/huatuo/${HUATUO_VERSION}/build/rpm/huatuo-apiserver.service"

4. 修改配置

根据实际部署环境编辑 /opt/huatuo-bamai/conf/huatuo-bamai.conf 和 /opt/huatuo-bamai/conf/huatuo-apiserver.conf。详细配置项说明请参见 huatuo-bamai 配置 和 huatuo-apiserver 配置。

根据 service unit 中的 CPUQuota、MemoryMax 和 TasksMax 设置资源上限。只有直接运行且传入 --enable-cgroup 时才配置 [Runtime]。

5. 注册 HUATUO 服务

重新加载 systemd 配置:

sudo systemctl daemon-reload

6. 启动 HUATUO 服务

启动服务并设置开机启动:

sudo systemctl enable --now huatuo-bamai huatuo-apiserver

RPM 包

OpenCloudOS 镜像仓库提供 HUATUO v2.1.0 的 x86_64 和 aarch64 RPM 包。RPM 包会安装 HUATUO 文件和 systemd 服务单元文件。

1. 下载 RPM 包

根据主机架构下载对应的 RPM 包:

x86_64:

wget https://mirrors.opencloudos.tech/epol/9/Everything/x86_64/os/Packages/huatuo-bamai-2.1.0-2.oc9.x86_64.rpm

aarch64:

wget https://mirrors.opencloudos.tech/epol/9/Everything/aarch64/os/Packages/huatuo-bamai-2.1.0-2.oc9.aarch64.rpm

2. 安装 RPM 包

x86_64:

sudo dnf install ./huatuo-bamai-2.1.0-2.oc9.x86_64.rpm

aarch64:

sudo dnf install ./huatuo-bamai-2.1.0-2.oc9.aarch64.rpm

3. 修改配置

根据实际部署环境编辑 /etc/huatuo-bamai/huatuo-bamai.conf。详细配置项说明请参见 huatuo-bamai 配置。

根据 service unit 中的 CPUQuota、MemoryMax 和 TasksMax 设置资源上限。只有直接运行且传入 --enable-cgroup 时才配置 [Runtime]。

4. 启动 HUATUO 服务

RPM 包会安装 huatuo-bamai.service 服务单元文件。启动服务并设置开机启动:

sudo systemctl enable --now huatuo-bamai

完整的 RPM 安装指引请参阅 https://mp.weixin.qq.com/s/Gmst4_FsbXUIhuJw1BXNnQ。

3 - 源码编译

1. 容器编译

可以执行如下命令,完成编译,静态代码检查。

$ sh build/build-run-testing-image.sh

或者单独执行:

1. 准备编译环境

$ docker build --network host -t huatuo/huatuo-bamai-dev:latest -f ./Dockerfile.devel .

2. 启动编译容器

$ docker run -it --pid=host --privileged --cgroupns=host --network=host -v $(pwd):/go/huatuo-bamai huatuo/huatuo-bamai-dev:latest sh

3. 进入容器编译

$ make

2. 镜像发布

通过 docker build 方式能够快速的发布,最新二进制容器镜像。

docker build --network host -t huatuo/huatuo-bamai:latest .

3. 物理机编译

3.1 安装依赖

Ubuntu 24.04:

apt install make git clang libbpf-dev linux-tools-common curl capnproto

Fedora 40:

dnf install make git clang libbpf-devel bpftool curl capnproto capnproto-devel glibc-static
go install mvdan.cc/gofumpt@v0.8.0
go install mvdan.cc/sh/v3/cmd/shfmt@v3.11.0
go install golang.org/x/tools/cmd/goimports@v0.36.0
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
go install github.com/vektra/mockery/v2@v2.53.6
go install capnproto.org/go/capnp/v3/capnpc-go@v3.1.0-alpha.2

3.2 编译

$ make

4. BPF 调试编译

通过 BPF_DEBUG=1 将 -DDEBUG_BPF 传给 clang,把调试代码编译进 BPF 对象:

$ make BPF_DEBUG=1     # 或只重新生成构建产物:make BPF_DEBUG=1 gen-build

埋点、运行时开关和日志说明参见调试。

4 - 配置指南

huatuo-bamai 与 huatuo-apiserver 的配置指南。

4.1 - huatuo-bamai 配置

1. 文档概述

huatuo-bamai 作为 HUATUO 的核心采集器(bpf-based metrics and anomaly inspector),其配置文件用于定义数据采集范围、探针启用策略、指标输出格式、异常检测规则、以及日志行为等。

配置文件包含全局黑名单、日志、运行时资源限制、存储配置以及自动追踪(AutoTracing)等多个 section。每个配置项均附带详细注释,明确说明用途、默认值及注意事项。本文档针对配置文件中的每一个配置项提供中文的详细解释,帮助用户准确理解和安全定制配置。

注意:配置文件中多数参数以 # 注释形式提供默认值,实际启用时需移除 # 并根据环境调整。修改后需重启 huatuo-bamai 进程生效。生产环境建议遵循最小化原则,避免过度开启高开销特性。

2. 全局黑名单

# Global tracing and metrics configuration.
#
# - BlackList
# Global blacklist for tracing and metrics.
#
BlackList = ["netdev_hw", "netdev_qdisc", "metax_gpu", "ascend_npu", "diskio", "tcp_retransmit", "mthreads_gpu"]
  • BlackList:全局追踪与指标黑名单。

    用于排除特定模块的追踪和指标采集,避免无关噪声或高开销探针。默认值为 ["netdev_hw", "netdev_qdisc", "metax_gpu", "ascend_npu", "diskio", "tcp_retransmit", "mthreads_gpu"],即全局禁用网络设备硬件层(netdev_hw)、队列调度统计(netdev_qdisc)、Metax GPU、Ascend NPU、基于 procfs 的磁盘 I/O 指标、TCP 重传追踪和摩尔线程 GPU 监控。需要启用磁盘 I/O 指标时从黑名单中移除 diskio;需要启用 TCP 重传追踪时移除 tcp_retransmit;需要启用摩尔线程 GPU 指标采集时移除 mthreads_gpu(要求已安装 MTML 库)。local 关联不依赖 standalone dropwatch tracer。

    说明:添加黑名单项可有效降低资源消耗,尤其在特定硬件环境中;支持数组格式,可根据实际业务扩展。

3. 日志配置

# Log Configuration
[Log]
    # - Level
    # The log level for huatuo-bamai: Debug, Info, Warn, Error, Panic.
    # Default: Info
    #
    # - File
    # Store logs to where the logging file is. If it is empty, don't write log
    # to any file.
    # Default: empty
    #
    # Level = "Info"
    # File = ""
  • Level:日志级别。

    可选值包括 Debug、Info、Warn、Error、Panic。默认值为 Info。

    说明:控制 huatuo-bamai 的日志输出详细程度。生产环境推荐使用 Info 或 Warn 以减少日志量;Debug 级别仅用于故障排查,会产生大量输出。

  • File:日志文件路径。

    指定日志写入的文件路径。若为空字符串,则不写入文件(仅输出到标准输出或系统日志)。默认值为空。

    说明:在容器化部署中,建议配置具体路径进行持久化。

4. 运行时资源限制

默认不创建 Huatuo 自身 cgroup。只有显式传入 --enable-cgroup 时,本节配置才会生效;Kubernetes 和 systemd 部署应使用各自的资源管理配置。

# Runtime limits for the huatuo-bamai process.
[Runtime]
    # - StartupCPULimitCores
    # CPU limit during startup, in cores.
    # Default: 0.5
    #
    # - CPULimitCores
    # CPU limit after startup, in cores.
    # Default: 2.0
    #
    # - MemoryLimitMiB
    # Memory limit in MiB.
    # Default: 2048
    #
    # StartupCPULimitCores = 0.5
    # CPULimitCores = 2.0
    # MemoryLimitMiB = 2048
  • StartupCPULimitCores:初始化阶段 CPU 上限,默认 0.5 核。
  • CPULimitCores:启动完成后的 CPU 上限,默认 2.0 核。
  • MemoryLimitMiB:进程内存上限,默认 2048 MiB。

配置始终以文档标明的单位保存,仅在应用 cgroup 限制时将内存转换为字节。

5. HTTP 服务与按需 Operation

# HTTP server configuration.
[HTTPServer]
    # - ListenAddress
    # Listen address in "host:port" form.
    # Default: ":19704"
    #
    # - MaxEventStreamClients
    # Maximum number of concurrent clients allowed to hold an open
    # /v1/events/watch SSE connection. Once the limit is reached, new requests
    # are rejected with HTTP 429 until an existing client disconnects.
    # Default: 100
    #
    # - EventStreamKeepAliveIntervalSeconds
    # Interval in seconds at which the server sends an SSE comment ping to each
    # connected client. The ping keeps the connection alive through load
    # balancers and proxies that would otherwise time out idle connections. If
    # writing the ping fails three consecutive times, the server closes the
    # connection.
    # Default: 30
    #
    # ListenAddress = ":19704"
    # MaxEventStreamClients = 100
    # EventStreamKeepAliveIntervalSeconds = 30

[HTTPServer.Auth]
    # huatuo-apiserver 调用 Node API 时使用的必填服务凭证。
    BearerToken = "REPLACE_WITH_RANDOM_HEX"

# Profiling 和 Tracing 共用的生命周期策略。
[Operations]
    # MaxConcurrent = 10
    # LaunchTimeoutSeconds = 10
    # StopGracePeriodSeconds = 5
    # FinalizationTimeoutSeconds = 30
    # TerminalRetentionPeriodSeconds = 600

# Node 本地 Profiling 执行配置。
[Profiling]
    # AggregationIntervalSeconds = 10
    # MaxConcurrentProcesses = 10
    # CommandOutputLimitBytes = 65536
    # ToolDir = "/opt/huatuo/tools"
  • ListenAddress 使用 host:port 格式,主机为空时监听所有接口。
  • HTTPServer.Auth.BearerToken 必填,并且必须与 huatuo-apiserver 独立配置的 Node 凭证一致;部署前必须替换示例值。
  • Operations.MaxConcurrent 是 Profiling、Tracing 共用的进程级上限;容量用尽时 直接拒绝新 Operation,不在 Node 排队。
  • 四个 Operation 时间参数分别限制进程启动、优雅停止、结果收尾和终态保留,不能 合并为一个通用 timeout。
  • Profiling.ToolDir 是外部采样工具的统一根目录,原样传给 profiler 的 --tool-path。Java 使用该目录下的 bin/asprof 和 lib/libasyncProfiler.so,Python 使用 py-spy。 只检查请求语言所需的工具;原生采集不需要此配置。Node 环境不满足要求时 拒绝请求且不创建 Operation。

生成的 Node API 通过 GET /openapi.json 提供协议文档。Profiling、Tracing 的 Start、Get、Stop 路由、POST /v1/events/watch 及 PUT /v1/config 必须携带 服务 Bearer Token;/readyz、指标、版本和 OpenAPI 文档保持公开。

5.1 通过 Node API 更新配置

PUT /v1/config 接收一个非空的 config 对象。键名使用与 TOML 结构一致的 点分路径,值保留 JSON 类型:

curl -i -X PUT 'http://127.0.0.1:19704/v1/config' \
  -H 'Authorization: Bearer REPLACE_WITH_RANDOM_HEX' \
  -H 'Content-Type: application/json' \
  --data '{
    "config": {
      "BlackList": ["dropwatch", "netdev_hw"],
      "Runtime.CPULimitCores": 1.5,
      "Runtime.MemoryLimitMiB": 1024
    }
  }'

更新成功返回 204 No Content。Node Agent 先校验完整的候选配置,再原子替换 配置文件,最后发布新的内存快照。校验或持久化失败时,当前快照保持不变。 未知键、无效值类型和空更新对象返回 400 Bad Request。

动态读取配置的组件无需重启即可观察到新快照。HTTP 监听与鉴权、存储初始化、 cgroup 设置等仅在启动阶段读取的配置会被持久化,但需重启 huatuo-bamai 后 才能生效。

事件流配置控制 POST /v1/events/watch。达到 MaxEventStreamClients 后,新连接返回 HTTP 429。 EventStreamKeepAliveIntervalSeconds 控制 SSE 心跳注释间隔,用于避免 代理或负载均衡器关闭空闲连接。连续三次写入失败后服务端关闭连接。 该值应小于上游 idle timeout,生产环境通常设置为 15–60 秒。

6. 存储配置

6.1 ElasticSearch/OpenSearch 存储

# Storage configuration
[Storage]
    # Elasticsearch and OpenSearch Storage
    #
    # Disable ES/OS storage if one of Address, Username, Password is empty.
    # Store the tracing and events data of linux kernel to ES/OS.
    #
    # - Address
    # Port 9200 is commonly used for Elasticsearch/OpenSearch HTTP APIs.
    # e.g.
    # http://127.0.0.1:9200
    # https://127.0.0.1:9200
    #
    # - Index
    # Elasticsearch or OpenSearch index, a logical namespace that holds a collection of
    # documents for huatuo-bamai.
    # Default: huatuo_bamai
    #
    # - Username
    # - Password
    # Address, Username, and Password must be either all empty (disabled) or
    # all configured (enabled). Partial connection settings are invalid.
    #
    [Storage.Elasticsearch]
        # Address = "http://127.0.0.1:9200"
        # Index = "huatuo_bamai"
        # Username = "elastic"
        # Password = "REPLACE_WITH_PASSWORD"
  • Address:ElasticSearch/OpenSearch 存储服务地址。

    无默认值。

    说明:用于存储内核追踪和事件数据。Address、Username、Password 三项全部为空时禁用 ES/OS 存储;启用时必须三项全部配置,部分配置会 导致进程启动失败。支持 HTTP/HTTPS 协议。

  • Index:索引名称。

    默认值为 huatuo_bamai。

    说明:索引是 ElasticSearch/OpenSearch 文档的逻辑命名空间,用于组织 huatuo-bamai 产生的追踪与事件数据。

  • Username:用户名。

    无默认值。

    说明:用于 Basic Auth 认证。

  • Password:认证密码。

    无默认值。

    说明:配合用户名进行安全认证。生产环境强烈建议使用强密码并结合 TLS 加密传输。

整体说明:ES/OS 存储用于持久化内核追踪和事件数据,便于后续检索与分析。如果用户不关心 Linux 内核事件、Autotracing 数据则可以关闭该配置。

6.2 本地文件存储

# LocalFile Storage
#
# Store data to local directory for troubleshooting on the host machine.
#
# - Path
# The directory for storing data. If the Path is empty, LocalFile will be disabled.
# Default: "huatuo-local"
#
# - RotationSizeMiB
# The maximum size in Megabytes of a record file before it gets rotated
# for per linux kernel tracer.
# Default: 100MB
#
# - MaxRotatedFiles
# The maximum number of old log files to retain for per tracer.
# Default: 10
#
[Storage.LocalFile]
	# Path = "huatuo-local"
    # RotationSizeMiB = 100
    # MaxRotatedFiles = 10
  • Path:本地数据存储目录。

    默认值为 huatuo-local。若路径为空,则禁用本地文件存储。

    说明:用于在宿主机本地保存数据,主要用于现场故障排查。推荐配置为绝对路径。

  • RotationSizeMiB:单文件轮转大小。

    每个追踪器记录文件在达到该大小时进行轮转。默认值为 100 MB。

    说明:单位为 MB,防止单个文件过大导致磁盘占用失控。

  • MaxRotatedFiles:最大保留轮转文件数。

    每个追踪器最多保留的历史文件数量。默认值为 10。

    说明:超过数量后自动删除最早文件,控制磁盘空间使用。

7. 自动追踪配置

自动追踪模块是 HUATUO 的智能特性之一,可根据阈值自动触发特定性能追踪,减少人工干预。

7.1 CPUIdle 自动追踪 — 容器突发高 CPU 使用场景

# Autotracing configuration 
[AutoTracing]
    # cpuidle
    #
    # For a high cpu usage all of a sudden in containers.
    #
    # - UserThreshold
    # User CPU usage threshold, when cpu usage reaches this threshold, cpu
    # performance tracing will be triggered.
    # Default: 75%
    #
    # - SysThreshold
    # System CPU usage threshold, when reaching this threshold, cpu performance
    # tracing will be triggered.
    # Default: 45%
    #
    # - UsageThreshold
    # The total cpu usage (system + user cpu usage) threshold, when reaching
    # this threshold, cpu performance tracing will be triggered.
    # Default: 45%
    #
    # - DeltaUserThreshold
    # The range of this user cpu changes within a short period of time.
    # Default: 45%
    #
    # - DeltaSysThreshold
    # The range of this system cpu changes within a short period of time.
    # Default: 20%
    #
    # - DeltaUsageThreshold
    # The range of this cpu usage changes within a short period of time.
    # Default: 55%
    #
    # - Interval
    # The sample interval of the cpu usage for all containers.
    # Default: 10s
    #
    # - IntervalTracing
    # Time since last run. Avoid frequently executing this tracing to prevent
    # damage to the system.
    # Default: 1800s
    #
    # - RunTracingToolTimeout
    # The executing time of this tracing program.
    # Default: 10s
    # 
    # NOTE:
    # Running this performance tool, when:
    # 1. UserThreshold and DeltaUserThreshold are true, or
    # 2. SysThreshold and DeltaSysThreshold are true, or
    # 3. UsageThreshold and DeltaUsageThreshold
    #
    [AutoTracing.CPUIdle]
        # UserThreshold = 75
        # SysThreshold = 45
        # UsageThreshold = 90
        # DeltaUserThreshold = 45
        # DeltaSysThreshold = 20
        # DeltaUsageThreshold = 55
        # Interval = 10
        # IntervalTracing = 1800
        # RunTracingToolTimeout = 10
  • UserThreshold:用户态 CPU 使用率阈值(%)。

    默认 75%。 当容器用户态 CPU 使用率达到该值时,可能触发 CPU 性能追踪。

  • SysThreshold:系统态 CPU 使用率阈值(%)。

    默认 45%。 当系统态 CPU 使用率达到该值时,可能触发追踪。

  • UsageThreshold:总 CPU 使用率阈值(用户态 + 系统态,%)。

    默认 90%(注释中示例)。 总 CPU 使用率达到该阈值时触发追踪。

  • DeltaUserThreshold:用户态 CPU 短期变化幅度阈值(%)。

    默认 45%。 短时间内用户态 CPU 使用率变化超过该值时触发。

  • DeltaSysThreshold:系统态 CPU 短期变化幅度阈值(%)。

    默认 20%。 短时间内系统态 CPU 使用率变化超过该值时触发。

  • DeltaUsageThreshold:总 CPU 使用率短期变化幅度阈值(%)。

    默认 55%。 短时间内总 CPU 使用率变化超过该值时触发。

  • Interval:CPU 使用率采样间隔(秒)。

    默认 10s。 对所有容器进行 CPU 使用率采样的周期。

  • IntervalTracing:连续运行间隔(秒)。

    默认 1800s(30 分钟)。 两次自动追踪之间的最小间隔,防止频繁执行对系统造成压力。

  • RunTracingToolTimeout:单次性能追踪执行超时时间(秒)。默认 10s。 控制追踪程序的最长运行时间,避免长时间占用资源。

触发逻辑说明:当满足以下任一条件时触发追踪:

  1. UserThreshold 与 DeltaUserThreshold 同时满足;或
  2. SysThreshold 与 DeltaSysThreshold 同时满足;或
  3. UsageThreshold 与 DeltaUsageThreshold 同时满足。

Filter 容器过滤:通过 Included/Excluded 规则数组控制监控范围。

    # 每条规则包含 Field(过滤字段)和 Pattern(正则)
    # Field: container_host_namespace | container_hostname | container_qos
    #
    # [[AutoTracing.CPUIdle.Filter.Excluded]]
    #     Field = "container_qos"
    #     Pattern = "besteffort"
    # [[AutoTracing.CPUIdle.Filter.Included]]
    #     Field = "container_host_namespace"
    #     Pattern = "^application-"
  • Filter:容器过滤规则。使用 [[double-bracket]] 语法定义多条规则,每条含 Field(过滤字段)和 Pattern(正则)。过滤逻辑:

    • 无规则:监控所有容器
    • 仅 Excluded:黑名单,排除匹配的容器
    • 仅 Included:白名单,仅监控匹配的容器
    • 两者并存:匹配 Included 且不匹配 Excluded

    默认无规则,监控所有容器。

7.2 CPUSys 自动追踪 — 宿主机突发高系统 CPU 使用场景

# cpusys
#
# For a high system cpu usage all of a sudden on host machine.
#
# - SysThreshold
# System CPU usage threshold, when reaching this threshold, cpu performance
# tracing will be triggered.
# Default: 45%
#
# - DeltaSysThreshold
# The range of system cpu changes within a short period of time.
# Default: 20%
#
# - Interval
# The sample interval of the cpu usage for host machine.
# Default: 10s
#
# - IntervalTracing
# Minimum time between profiling runs.
# Default: 1800s
#
# - RunTracingToolTimeout
# The executing time of this tracing program.
# Default: 10s
#
# NOTE:
# Running this performance tool, when:
# SysThreshold and DeltaSysThreshold are true.
#
[AutoTracing.CPUSys]
	# SysThreshold = 45
	# DeltaSysThreshold = 20
	# Interval = 10
	# IntervalTracing = 1800
	# RunTracingToolTimeout = 10
  • SysThreshold:系统态 CPU 使用率阈值(%)。

    默认 45%。

  • DeltaSysThreshold:系统态 CPU 短期变化幅度阈值(%)。

    默认 20%。

  • Interval:宿主机 CPU 使用率采样间隔(秒)。

    默认 10s。

  • IntervalTracing:两次性能剖析之间的最短间隔,默认 1800s。

  • RunTracingToolTimeout:单次追踪执行超时时间(秒)。默认 10s。

触发逻辑:当 SysThreshold 与 DeltaSysThreshold 同时满足时触发。

7.3 Dload 自动追踪 — 容器 D 状态任务剖析

# dload
#
# linux tasks D state profiling for containers.
#
# - ThresholdLoad
# The loadavg threshold value, when reaching this threshold, dload profiling
# is triggered.
# Default: 5
#
# - Interval
# The sample interval of the load for all containers.
# Default: 10s
#
# - IntervalTracing
# Time since last run. Avoid frequently executing this tracing to prevent
# damage to the system.
# Default: 1800s
#
[AutoTracing.Dload]
	# ThresholdLoad = 5
	# Interval = 10
	# IntervalTracing = 1800
  • ThresholdLoad:容器的系统负载平均值(loadavg)阈值。

    默认 5。 当 loadavg 达到该值时,触发 D 状态(不可中断睡眠)任务剖析。

    说明:用于诊断容器中大量进程进入 D 状态的场景。

  • Interval:监控间隔(秒)。

    默认 10。 Dload 监控的周期。

  • IntervalTracing:连续运行间隔(秒)。

    默认 1800s(30 分钟)。 两次自动追踪之间的最小间隔,防止频繁执行对系统造成压力。

7.4 IOTracing 自动追踪 — 容器 IO 性能剖析

# iotracing
#
# io profiling for containers.
#
# - WbpsThreshold
# Max write bytes per second, when reaching this threshold, iotracing is triggered.
# Please note that if it is an NVMe device, it must also meet the UtilThreshold.
# Default: 1500 MB/s
#
# - RbpsThreshold
# Max read bytes per second, when reaching this threshold, iotracing is triggered.
# Please note that if it is an NVMe device, it must also meet the UtilThreshold.
# Default: 2000 MB/s
#
# - UtilThreshold
# Disk utilization, Percentage of time the disk is busy. If this is consistently
# above 80-90%, the disk may be a bottleneck.
# Default: 90%
#
# - AwaitThreshold
# Await (Average IO wait time in ms): High values indicate slow disk response times.
# Default: 100ms
#
# - RunTracingToolTimeout
# The executing time of this tracing tool.
# Default: 10s
#
# - MaxProcDump
# The number of processes displayed by iotracing tool.
# Default: 10
#
# - MaxFilesPerProcDump
# The number of files per process displayed by iotracing tool.
# Default: 5
#
[AutoTracing.IOTracing]
	# WbpsThreshold = 1500
	# RbpsThreshold = 2000
	# UtilThreshold = 90
	# AwaitThreshold = 100
	# RunTracingToolTimeout = 10
	# MaxProcDump = 10
	# MaxFilesPerProcDump = 5
  • WbpsThreshold:每秒最大写字节数阈值(MB/s)。

    默认 1500 MB/s。 达到该值时可能触发 IO 追踪(NVMe 设备需同时满足 UtilThreshold)。

  • RbpsThreshold:每秒最大读字节数阈值(MB/s)。

    默认 2000 MB/s。 类似写字节,达到阈值时触发。

  • UtilThreshold:磁盘利用率阈值(%)。

    默认 90%。 磁盘忙碌时间百分比,持续高于 80-90% 可能成为瓶颈。

  • AwaitThreshold:平均 IO 等待时间阈值(ms)。

    默认 100ms。 高值表示磁盘响应缓慢。

  • RunIOTracingTimeout:IO 追踪工具执行超时时间(秒)。

    默认 10s。

  • MaxProcDump:IO 追踪显示的最大进程数。

    默认 10。 控制输出中展示的进程数量。

  • MaxFilesPerProcDump:每个进程显示的最大文件数。

    默认 5。 控制每个进程关联文件的展示数量。

说明:IOTracing 用于容器 IO 热点诊断,特别关注高负载磁盘场景。

7.5 内存突发自动追踪

该模块用于检测宿主机内存使用量突发增长场景,并在触发时自动捕获内核上下文,便于诊断内存压力事件。

# memory burst
#
# If there is a memory used burst on the host, capture this kernel context.
#
# - Interval
# The sample interval of the memory used.
# Default: 10s
#
# - DeltaMemoryBurst
# A certain percentage of memory burst used. 100% that means, e.g.,
# memory used increased from 200MB to 400MB.
# Default: 100%
#
# - DeltaAnonThreshold
# A certain percentage of anon memory burst used. 100% that means, e.g.,
# anon memory used increased from 200MB to 400MB.
# Default: 70%
#
# - IntervalTracing
# Time since last run. Avoid frequently executing this tracing
# to prevent damage to the system.
# Default: 1800s
#
# - DumpProcessMaxNum
# How many processes to dump when this event is triggered.
# Default: 10
#
[AutoTracing.MemoryBurst]
	# DeltaMemoryBurst = 100
	# DeltaAnonThreshold = 70
	# Interval = 10
	# IntervalTracing = 1800
	# SlidingWindowLength = 60
	# DumpProcessMaxNum = 10
  • DeltaMemoryBurst:内存使用量突发增长百分比阈值。

    默认 100%。 表示内存使用量在采样窗口内增长的比例(例如从 200MB 增长到 400MB 即 100%)。达到该阈值时可能触发内存突发追踪。

    说明:用于捕获整体内存使用量的急剧上升场景。

  • DeltaAnonThreshold:匿名页内存突发增长百分比阈值。

    默认 70%。 匿名内存(anonymous memory)增长比例阈值,匿名页是内存压力诊断的重要指标。

    说明:重点监控易导致 OOM 或 swap 的匿名内存突发。

  • Interval:内存使用量采样间隔(秒)。

    默认 10s。 对宿主机内存使用情况进行周期性采样的时间间隔。

    说明:采样频率影响检测灵敏度与开销。

  • IntervalTracing:连续运行最小间隔(秒)。

    默认 1800s(30 分钟)。 两次内存突发追踪之间的冷却时间,避免频繁执行对系统造成额外压力。

    说明:防止追踪工具被过度触发。

  • DumpProcessMaxNum:触发事件时转储的最大进程数。

    默认 10。 当内存突发事件触发时,最多转储多少个相关进程的详细信息(包括内存占用、调用栈等)。

    说明:控制输出数据量,避免单次事件产生过多诊断信息。

7.6 内存阈值运行时快照

memory_threshold_snapshot 默认开启;将该自动追踪加入全局 BlackList 并重启 huatuo-bamai,即可关闭。 该功能在容器内存压力通知后尝试采集 Go、HotSpot 或 CPython 运行时快照,不保证在 OOM 前完成。候选进程按近似内核 OOM 分数选择。

持久化字段 victim_pid、victim_process_name 和 victim_oom_score_adj 描述本次快照选中的采集进程。

[AutoTracing.MemoryThresholdSnapshot]
    # ThresholdPercent = 90
    # IntervalTracing = 300
    # RunTracingToolTimeout = 2
    # MaxMemoryObjectEntries = 10

注释中的数值为默认值。

参数 含义
ThresholdPercent 采集要求的内存使用量与限额比例,范围 1–100
IntervalTracing 成功或失败采集尝试完成后的节点级最小间隔,单位秒,默认 300,必须为正数
RunTracingToolTimeout Go、Java、Python 统一使用的协作式采集超时时间,单位秒,默认 2,必须为正数
MaxMemoryObjectEntries 单次快照最多保留的内存对象排序条目数,范围 1–100,默认 10;最终 JSON 上限为 512 KiB,超限会裁剪

运行时识别和保存时间不计入采集预算,运行时识别没有独立超时限制。 超时不能中断正在执行的同步读取,因此不是整个操作的耗时上限。

触发条件:

  • cgroup v1:通过 cgroup.event_control 注册 ThresholdPercent 对应的内存阈值。
  • cgroup v2:监听 memory.events.local 的 high 或 max 计数增长 (文件不存在时使用 memory.events),再检查 memory.current / memory.max 是否达到配置比例。 本功能不设置 memory.high;为 max 时仍可由硬限制的 max 计数触发检查。 该通知发生较晚,不能保证恰好在配置百分比处检测,也不能保证在 OOM 前完成抓取。

两种版本均在注册完成后和硬限制变化时检查水位。一个 watcher 管理所有目标, 不进行周期采样。同一目标的重复通知会合并,不逐次统计跨越,也不提供水位恢复通知。 抓取前会重新检查当前水位和目标身份。

该功能依赖已启用的 pod 管理器。pod 根据共享 CSS 生命周期线索主动更新容器视图, 向快照模块提供存量容器及包含实例代次、InitPID、memory cgroup 路径的增删事件。 快照模块不再扫描 cgroup 树;订阅溢出后通过 pod 的完整视图恢复。 上游同步失败时暂停采集,不能把失败视为容器全部删除。

监控范围包括运行中的普通容器和可重启 init sidecar,不包括临时调试容器及普通 init 容器。 普通注册失败或达到监听数量上限时记录日志并跳过该目录实例;监听失效后注销,不自动重试或补位。 重复事件、全量更新和容器代次变化不会恢复失败的监听。目录实例替换、原容器退出后 建立新的跟踪状态,或重启 huatuo-bamai 后可重新尝试。宿主机资源耗尽等致命错误仍会停止当前监听流程。 每个被监控容器必须拥有独立的 memory cgroup 目录;监听和采集事件直接绑定一个容器实例。 注册时若发现返回的监听已属于其他容器,则停止该功能、取消采集并释放监听; 修正容器 cgroup 隔离后需重启 huatuo-bamai,不自动重试该冲突。 目录实例替换通过 watcher 的移除通知使旧注册失效。 采集及保存前核验容器代次和实际绑定、目录身份,以及目标进程身份和 cgroup 归属。记录中的内存使用量和限额属于该容器的 cgroup。

部署限制和结果查询见第 14 节。

7.7 IRQTracing 自动追踪

该模块检测单个 CPU 的 irq+softirq 利用率异常,并调用 irqtracing 采集 softirq source 和 victim 调用栈。

[AutoTracing.IRQTracing]
    Interval = 2
    RunTracingToolTimeout = 3
    IntervalTracing = 300
    MaxEventsPerSecond = 1000
    MinCPUs = 3
    DeltaUsageThreshold = 20
    RelativeIncreaseThreshold = 30
    SustainedIntervals = 10
    UsageThreshold = 80
  • Interval:/proc/stat 中每 CPU irq+softirq 利用率的采样间隔,默认 2s。
  • RunTracingToolTimeout:单次 irqtracing 采集时长,默认 3s。
  • IntervalTracing:两次触发之间的最小间隔,默认 300s。
  • MaxEventsPerSecond:在被跟踪 CPU 上每秒采集的 source 和 victim 栈 样本总上限,默认 1000。守护进程将额度尽量均分给 softirq_raise 和 softirq_entry;默认每条流 500/s。该值必须在 2 到 8589934590 之间。
  • MinCPUs、DeltaUsageThreshold 和 RelativeIncreaseThreshold: 控制多 CPU irq+softirq 利用率突增规则;两个阈值分别表示利用率增加的 百分点和相对上一采样值的增长百分比。
  • SustainedIntervals 和 UsageThreshold:控制单 CPU irq+softirq 持续高利用率规则的连续采样次数和利用率阈值。

7.8 已知问题过滤(IssuesList)

# Autotracing configuration.
#
# - IssuesList
# Known issue filters for autotracing.
#
[AutoTracing]
    IssuesList = []
  • IssuesList:已知问题过滤器。格式 [["问题名称", "正则"], ...]。采集到的堆栈匹配正则时标记为对应问题名称,默认 []。当前用于 dload 追踪。

    示例:IssuesList = [["known_issue1", "softlockup"], ["known_issue2", "alloc_pages.*failed"]]

注意:当前仅支持 dload 追踪的已知问题过滤,其他事件暂不支持。

8. 事件追踪配置

该 section 负责内核关键事件的捕获与延迟监控,包括调度 tick 间隔、内存回收、网络接收延迟、网卡事件及丢包监控等,是 HUATUO 内核级异常上下文采集的核心模块。

8.1 调度 tick 间隔追踪

# linux kernel events capturing configuration
[EventTracing]
	# scheduler tick
	#
	# Trace long scheduler tick intervals.
	#
	# - IntervalThreshold
	# When the scheduler tick interval reaches the threshold, huatuo-bamai
	# will collect kernel context.
	# Default: 10000000 in nanoseconds, 10ms
	#
	[EventTracing.SchedTick]
		# IntervalThreshold = 10000000
  • IntervalThreshold:调度 tick 间隔阈值(纳秒)。默认 10000000 ns(10ms)。达到该阈值时采集事件。该事件通过过长的 tick 间隔推断 CPU 异常停顿,不能单独证明软中断被禁用。

8.2 内存回收阻塞追踪

# memreclaim
#
# The memory reclaim may block the process, if one process is blocked
# for a long time, reporting the events to userspace.
#
# - BlockedThreshold
# The blocked time when memory reclaiming.
# Default: 900000000ns, 900ms
#
[EventTracing.MemoryReclaim]
	# BlockedThreshold = 900000000
  • BlockedThreshold:内存回收阻塞时间阈值(纳秒)。默认 900000000 ns(900ms)。 当单个进程因内存回收(reclaim)被阻塞超过该时间时,向用户态上报事件并捕获上下文。 说明:内存回收阻塞是导致进程卡顿的常见原因,尤其在内存紧张的云原生环境中。

8.3 网络接收延迟追踪

# networking rx latency
#
# linux net stack rx latency for every tcp skbs.
#
# - Driver2NetRx
# The latency from driver to net rx, e.g., netif_receive_skb.
# Default: 5ms
#
# - Driver2TCP
# The latency from driver to tcp rx, e.g., tcp_v4_rcv.
# Default: 10ms
#
# - Driver2Userspace
# The latency from driver to userspace copy data, e.g., skb_copy_datagram_iovec.
# Default: 115ms
#
# - ExcludedContainerQos
# Blacklist: skip containers whose qos level matches.
# Values: "guaranteed", "burstable", "besteffort" (case-insensitive).
# Default: [].
#
# - ExcludedHostNetnamespace
# Don't care the skbs, packets in the host net namespace.
# Default: true
#
[EventTracing.NetRxLatency]
	# Driver2NetRx = 5
	# Driver2TCP = 10
	# Driver2Userspace = 115
	# ExcludedContainerQos = []
	ExcludedContainerQos = ["besteffort"]
	# ExcludedHostNetnamespace = true
  • Driver2NetRx:从驱动到网络层接收的延迟阈值(毫秒)。

    默认 5ms。 例如 netif_receive_skb 等函数的延迟监控阈值。

  • Driver2TCP:从驱动到 TCP 协议栈接收的延迟阈值(毫秒)。

    默认 10ms。 例如 tcp_v4_rcv 等函数的延迟监控。

  • Driver2Userspace:从驱动到用户态数据拷贝的延迟阈值(毫秒)。

    默认 115ms。 例如 skb_copy_datagram_iovec 等函数的延迟监控。

  • ExcludedContainerQos:排除的容器 QoS 级别,黑名单模式。

    默认 [""]。 不监控指定 QoS 级别的容器网络接收延迟(对应 Kubernetes Pod QoS:Guaranteed、Burstable、BestEffort,大小写不敏感)。

    说明:通常排除 BestEffort 容器以减少噪声。

  • ExcludedHostNetnamespace:是否排除宿主机网络命名空间。

    默认 true。 不监控宿主机 net namespace 中的 skb 数据包延迟。

    说明:聚焦容器网络流量,减少无关宿主机数据干扰。

8.4 网卡事件监控

# netdev events
#
# monitor the net device events.
#
# - DeviceList
# The net devices we take care of.
# Default: [] is empty, meaning no devices.
#
[EventTracing.Netdev]
	DeviceList = ["eth0", "eth1", "bond4", "lo"]
  • DeviceList:需要监控的网卡设备完整匹配正则列表。"eth0" 等字面量名称保持精确匹配,"bond[0-9]+" 等模式可匹配多块网卡。

    默认示例包含 “eth0”, “eth1”, “bond4”, “lo”。 为空列表时表示不监控任何设备。 监控网络设备的物理链路状态事件等。

    说明:精确指定感兴趣的网络接口,支持 bond、lo 等。

8.5 丢包监控([EventTracing.Dropwatch])

[EventTracing.Dropwatch]
    # standalone dropwatch 使用的 filter。
    # 默认值:"tcp"
    Filter = "tcp"

    # 转发给 dropwatch --max-events-per-second。
    # 默认值:100;0 表示不限速。
    MaxEventsPerSecond = 100

    # 预留配置字段。当前 dropwatch 事件链路未消费该字段,
    # 因此不会产生容器过滤效果。
    # 默认值:[]
    ExcludeContainers = []
  • Filter:只传给 standalone dropwatch 的 tcpdump 风格过滤表达式。TCP 重传关联的两个输入统一使用 TCPRetransmit.Filter。

    默认值:"tcp"。

  • MaxEventsPerSecond:BPF 侧每秒最多输出的 dropwatch 事件数。

    默认值:100,设置为 0 表示不限速。

  • ExcludeContainers:预留的容器排除列表。

    默认值:[]。该字段存在于配置结构中,但当前 dropwatch 事件链路既不读取也不转发它,因此配置后不会生效。运维侧如需按调用栈抑制 dropwatch 噪声,应使用 EventTracing.IssuesList。

8.6 TCP 重传追踪([EventTracing.TCPRetransmit])

[EventTracing.TCPRetransmit]
    # 重传过滤条件;local 关联会把它应用到两个输入。
    # 默认值:空(关闭关联时不传参数,开启关联时使用 "tcp")。
    Filter = ""

    # Forwarded as tcpshark --enable-tlp. Default: false.
    EnableTLP = false

    # Run tcpshark with an embedded dropwatch source. Default: false.
    EnableDropwatch = false

    # Forwarded as tcpshark --max-events-per-second.
    # Default: 100; 0 disables rate limiting.
    MaxEventsPerSecond = 100
  • EnableTLP:是否采集 tcp_send_loss_probe 事件。

    默认 false。

  • Filter:两种模式都使用的 TCP 重传过滤条件。开启 local 关联后,两个 tcpshark 输入统一使用规范化后的表达式,空值回退为 tcp;关闭关联时,空值不传 --filter。Dropwatch.Filter 保持独立,只控制 standalone dropwatch。

  • EnableDropwatch:是否让 tcpshark 加载私有 dropwatch source 并在本地完成重传结果定型,默认 false。必须从 BlackList 移除 tcp_retransmit;standalone dropwatch 可以继续位于黑名单中。重传最多等待 100ms,候选 drop 的内核单调时间必须早于重传且相差不超过 1s。embedded source 自动检测并启用 devlink DROP trap。同 netns 的严格匹配按来源输出 software 或 hardware,附带与 dropwatch 一致的 drop_source、drop_reason 和硬件 drop_reason_group;每条已定型事件输出唯一的 correlation_reason(matched、unsupported、warmup、wait_timeout、queue_full 或 interrupted);warmup 仅用于等待到期且重传发生时间早于 source ready 的事件,等于或晚于 ready 的重传在等待到期时使用 wait_timeout,其他结束路径保留各自原因;未匹配事件输出 drop_location=unknown,并保留 namespace 诊断标记、dropwatch 计数及 map 计数可用性。

  • MaxEventsPerSecond:BPF 侧每秒最多输出的 TCP 重传事件数。关联模式还会给 embedded dropwatch 配置一个数值相同但独立的 limiter,因此 100 表示两条输入各自最多 100 条/秒。

    默认 100,设置为 0 表示不限速。超限时 tcpshark 会输出 rate limit hit 日志。

8.7 硬件错误事件追踪(EventTracing.Ras)

# ras
#
# Hardware error event tracing (RAS: Reliability, Availability, Serviceability).
# Captures MCE, EDAC, ACPI/GHES, PCIe AER, and MCE threshold (THR) events via eBPF.
#
# - MceThrBackoff
# Minimum interval in seconds between consecutive MCE threshold (THR) event saves.
# THR events are fired by the local-APIC threshold interrupt and can storm at high
# frequency; this cooldown prevents flooding storage with redundant records.
# Default: 1800s (30 minutes)
#
[EventTracing.Ras]
    # MceThrBackoff = 1800
  • MceThrBackoff:MCE 阈值中断(THR)事件存储的最小间隔时间(秒)。

    默认 1800s(30 分钟)。

    说明:THR 事件由 CPU 本地 APIC 阈值中断触发,在硬件出现纠正性错误时可能以极高频率产生。该冷却时间用于防止存储系统被大量重复记录淹没,同时保证关键事件仍能被捕获。调低该值可获得更实时的事件记录,但需注意存储压力;在错误频发的环境中建议适当调高。

8.8 摩尔线程 GPU 事件追踪(EventTracing.MthreadsGPU)

# mthreads_gpu
#
# Moore Threads GPU XID error event tracing.
[EventTracing.MthreadsGPU]
    # MthreadsXidLevel = ""
  • MthreadsXidLevel:XID 错误报告的最低严重级别。

    可选值:""(禁用)、"notify"、"warning"、"fatal"。

    默认值:""(禁用)。

    说明:控制哪些 XID 错误事件被报告。设置此选项以启用摩尔线程 GPU 的 XID 错误追踪。低于指定严重级别的 XID 错误将被过滤掉。要启用此功能,请确保 mthreads_xid 从全局 BlackList(如果存在)中移除,并将此字段设置为有效的严重级别之一("notify"、"warning"、"fatal")。此外,主机上必须安装 MUSA 驱动。该功能每秒轮询 /proc/driver/musa/gpu*/event_report 文件以捕获 XID 错误事件。每个 XID 事件包含详细信息,包括 UUID、XID ID、严重级别、作用域、PCI BDF、进程 ID 和附加上下文信息。

8.9 已知问题过滤(IssuesList)

# Linux kernel event tracing configuration.
#
# - IssuesList
# Known issue filters for event tracing.
#
[EventTracing]
    IssuesList = []
  • IssuesList:已知问题抑制规则,格式为 [["名称", "正则"], ...],默认值为 []。

    对 net_rx_latency,正则匹配生成的事件标题;对 dropwatch,正则匹配以换行符连接的内核调用栈。匹配后事件会被丢弃;配置的名称只用于标识规则,不会写入已保存事件。

    示例:IssuesList = [["ignored_process", "comm=ignored_process"], ["neighbor_cleanup", "neigh_invalidate/"]]

9. 指标采集器配置

该 section 定义各类系统与网络指标的采集规则。所有 Included/Excluded 字段底层共用同一套过滤逻辑(正则表达式):

  • 无规则:全部采集
  • 仅 Excluded:黑名单,匹配即跳过
  • 仅 Included:白名单,仅采集匹配项
  • 两者并存:必须匹配 Included 且不匹配 Excluded

9.1 网卡统计

# Metric Collector
[MetricCollector]
	# Netdev statistic
	#
	# - EnableNetlink
	# Use netlink instead of procfs net/dev to get netdev statistic.
	# Only support the host environment to use `netlink` now.
	# Default is "false".
	#
	# - DeviceIncluded
	# Accept special devices in netdev statistic.
	# Default: "" (empty), meaning include all.
	#
	# - DeviceExcluded
	# Exclude special devices in netdev statistic.
	# Default: "" (empty), meaning exclude nothing.
	#
	# Filter logic see MetricCollector section header.
	#
	[MetricCollector.NetdevStats]
		# EnableNetlink = false
		# DeviceIncluded = ""
		DeviceExcluded = "^(lo)|(docker\\w*)|(veth\\w*)$"
  • EnableNetlink:是否使用 netlink 而非 procfs 获取网卡统计。

    默认 false。 仅宿主机环境支持 netlink。

    说明:netlink 方式通常更高效,但需内核支持。

  • DeviceIncluded:需要纳入统计的网卡设备正则。默认空(全部采集)。

  • DeviceExcluded:需排除的网卡设备正则。如:排除 lo、docker、veth 等虚拟接口。

9.2 网卡 DCB(Data Center Bridging)采集

# netdev dcb, DCB (Data Center Bridging)
#
# Collecting the DCB PFC (Priority-based Flow Control).
#
# - DeviceList
# The net devices we take care of.
# Default: [] is empty, meaning no devices.
#
[MetricCollector.NetdevDCB]
	DeviceList = ["eth0", "eth1"]
  • DeviceList:需要采集 DCB(优先流控 PFC)信息的网卡完整匹配正则列表。

    默认空。

    说明:主要用于数据中心网络环境下的优先级流控监控。

9.3 网卡硬件统计

# netdev hardware statistic
#
# Collecting the hardware statistic of net devices, e.g, rx_dropped.
#
# - DeviceList
# The net devices we take care of.
# Default: [] is empty, meaning no devices.
#
[MetricCollector.NetdevHW]
	DeviceList = ["eth0", "eth1"]
  • DeviceList:需要采集硬件层统计(如 rx_dropped)的网卡完整匹配正则列表。

    默认空。

    说明:聚焦硬件丢包、错误等底层指标。

9.4 Qdisc(队列规则)采集

# Qdisc
#
# - DeviceIncluded / DeviceExcluded
# Same as above.
#
[MetricCollector.Qdisc]
	# DeviceIncluded = ""
	DeviceExcluded = "^(lo)|(docker\\w*)|(veth\\w*)$"
  • DeviceIncluded / DeviceExcluded:同 MetricCollector 描述的过滤逻辑。

    说明:用于诊断流量整形、调度延迟等问题。

9.5 vmstat 指标采集

# vmstat
#
# This metric supports host vmstat and cgroup vmstat.
# - IncludedOnHost / ExcludedOnHost: same filter logic, for host /proc/vmstat.
# - IncludedOnContainer / ExcludedOnContainer: same, for cgroup containers memory.stat.
#
[MetricCollector.Vmstat]
	IncludedOnHost = "allocstall|nr_active_anon|nr_active_file|nr_boost_pages|nr_dirty|nr_free_pages|nr_inactive_anon|nr_inactive_file|nr_kswapd_boost|nr_mlock|nr_shmem|nr_slab_reclaimable|nr_slab_unreclaimable|nr_unevictable|nr_writeback|numa_pages_migrated|pgdeactivate|pgrefill|pgscan_direct|pgscan_kswapd|pgsteal_direct|pgsteal_kswapd"
	ExcludedOnHost = "total"
	IncludedOnContainer = "active_anon|active_file|dirty|inactive_anon|inactive_file|pgdeactivate|pgrefill|pgscan_direct|pgscan_kswapd|pgsteal_direct|pgsteal_kswapd|shmem|unevictable|writeback|pgscan_globaldirect|pgscan_globalkswapd|pgscan_cswapd|pgsteal_cswapd|pgsteal_globaldirect|pgsteal_globalkswapd"
	ExcludedOnContainer = "total"
  • IncludedOnHost / ExcludedOnHost:宿主机 /proc/vmstat 的过滤字段正则。

  • IncludedOnContainer / ExcludedOnContainer:容器 cgroup memory.stat 的过滤字段正则。

    说明:精细控制 vmstat 指标采集,支持主机与容器差异化配置,避免采集无关字段。

9.6 其他指标采集

# MemoryEvents/Netstat/MountPointStat
#
# - Included / Excluded: same as above.
# - MountPointsIncluded: whitelist only (no Excluded), same logic.
#
[MetricCollector.MemoryEvents]
	Included = "watermark_inc|watermark_dec"
	# Excluded = ""
[MetricCollector.Netstat]
	# Excluded = ""
	# Included = ""

# MountPointStat
[MetricCollector.MountPointStat]
	MountPointsIncluded = "(^/home$)|(^/$)|(^/boot$)"
  • Included / Excluded(MemoryEvents、Netstat):同上过滤逻辑。

  • MountPointsIncluded:采集挂载点统计的路径正则。默认示例含 /、/home、/boot。

    说明:用于监控关键文件系统使用情况。

9.7 摩尔线程 GPU 指标

# MetricCollector.Mthreads
#
# 通过 MTML(摩尔线程管理库)共享库采集摩尔线程 GPU 指标。
# 库文件在启动时通过系统动态链接器按 SONAME 顺序自动发现
# (libmtml.so.2,然后 libmtml.so),无需硬编码路径。
#
# 从 BlackList 中移除 "mthreads_gpu" 以启用此采集器。
#
# - EnableHealth
# 启用健康指标:温度、功耗、利用率、时钟、风扇、pstate、VPU。
# 默认值:true
#
# - EnablePCIe
# 启用 PCIe 链路指标:当前速率/宽度和重放计数器。
# 默认值:false
#
# - EnableMTLink
# 启用 MtLink 互连指标:每链路状态和带宽。
# 默认值:false
#
[MetricCollector.Mthreads]
    # EnableHealth = true
    # EnablePCIe = false
    # EnableMTLink = false
  • EnableHealth:控制健康相关指标的采集。

    默认值:true。启用后采集:GPU/内存温度(gpu_temperature_celsius、memory_temperature_celsius)、功耗及限制(device_power_watts、gpu_power_limit_watts、gpu_power_default_limit_watts)、GPU/内存利用率(gpu_utilization_percent、memory_utilization_percent)、时钟频率(gpu_clock_mhz、gpu_max_clock_mhz、memory_clock_mhz、memory_max_clock_mhz)、电压(gpu_voltage_volts)、内存容量(memory_total_bytes、memory_used_bytes)、风扇转速(fan_rpm、fan_speed_percent)、性能状态(gpu_pstate)以及 VPU 指标(vpu_utilization_percent、vpu_encoder_utilization_percent、vpu_decoder_utilization_percent、vpu_clock_mhz)。

  • EnablePCIe:控制 PCIe 链路指标的采集。

    默认值:false。启用后采集:当前 PCIe 链路速率和宽度(pcie_link_speed_gt_per_sec、pcie_link_width_lanes)、最大能力值(pcie_link_max_speed_gt_per_sec、pcie_link_max_width_lanes)以及重放计数器(pcie_replay_total)。

  • EnableMTLink:控制 MtLink 互连指标的采集。

    默认值:false。启用后采集:设备级静态规格(每链路带宽 mtlink_link_bandwidth_gb_s 和链路数 mtlink_link_count)以及每链路状态(mtlink_state)。

库发现机制:启动时,采集器通过系统动态链接器(遵循 LD_LIBRARY_PATH 和 /etc/ld.so.cache)依次搜索 libmtml.so.2 和 libmtml.so。如果未找到库文件,采集器记录警告并在进程生命周期内保持禁用状态。库发现仅在启动时执行;更改 LD_LIBRARY_PATH 或安装新的 MTML 版本需要重启。

热更新语义:EnableHealth、EnablePCIe、EnableMTLink 在每次 scrape 时从最新配置快照中读取,因此切换这些开关后下一个 Prometheus scrape 即可生效,无需重启 huatuo-bamai。false → true → false 的转换会在每次变更后的下一个 scrape 上按预期发布或停止发布对应的指标组。

注意:在进程已经启动且因 libmtml.so 缺失导致采集器被禁用的情况下,要启用该采集器(即把 mthreads_gpu 从 BlackList 中移除)需要重启进程。采集器工厂只在初始化时运行,运行时即使库被加载成功也不会注册新的采集器。

10. Pod 配置

该 section 用于从 kubelet 获取 Pod 信息,实现容器与 Pod 级别的标签关联和指标隔离。

# Pod Configuration
#
# Configure these parameters for fetching pods from kubelet.
#
# - KubeletReadOnlyPort
# The KubeletReadOnlyPort is kubelet read-only port for the Kubelet to serve on with
# no authentication/authorization. The port number must be between 1 and 65535, inclusive.
# Setting this field to 0 disables fetching pods from kubelet read-only service.
# Default: 10255
#
# - KubeletAuthorizedPort
# The port is the HTTPs port of the kubelet. The port number must be between 1 and 65535,
# inclusive. Setting this field to 0 disables fetching pods from kubelet HTTPS port.
# Default: 10250
#
# - KubeletClientCertPath
# https://kubernetes.io/docs/setup/best-practices/certificates/
#
# Client certificate and private key file name. One file or two files:
# "/path/to/xxx-kubelet-client.crt,/path/to/xxx-kubelet-client.key",
# "/path/to/kubelet-client-current.pem"
#
# You can disable this kubelet fetching pods, for bare metal service, by
# KubeletReadOnlyPort = 0, and KubeletAuthorizedPort = 0.
#
[Pod]
	KubeletClientCertPath = "/etc/kubernetes/pki/apiserver-kubelet-client.crt,/etc/kubernetes/pki/apiserver-kubelet-client.key"
  • KubeletReadOnlyPort:kubelet 只读端口。

    默认 10255。 用于无认证方式从 kubelet 获取 Pod 列表。设置为 0 时禁用该方式。

    说明:端口范围 1-65535,适合测试或非安全环境。

  • KubeletAuthorizedPort:kubelet HTTPS 授权端口。

    默认 10250。 用于安全方式(证书认证)从 kubelet 获取 Pod 信息。设置为 0 时禁用。

    说明:生产环境推荐使用该端口结合证书认证。

  • KubeletClientCertPath:kubelet 客户端证书及私钥路径。

    支持格式:"/path/to/xxx-kubelet-client.crt,/path/to/xxx-kubelet-client.key" 或单文件 PEM 格式。

    说明:参考 Kubernetes 证书最佳实践,用于 HTTPS 端口的 mTLS 认证。在裸金属或非 Kubernetes 环境中可通过将两个端口设为 0 来禁用 Pod 获取功能。

11. 命令行参数

huatuo-bamai 支持以下命令行参数:

huatuo-bamai --region <region> [选项]
参数 说明 默认值
--config 配置文件名 huatuo-bamai.conf
--config-dir 配置文件目录 conf
--bpf-dir BPF 对象文件目录 bpf
--tools-bin-dir 追踪工具二进制目录 bin
--region 部署区域(必填) -
--disable-kubelet 禁用 kubelet Pod 获取 false
--disable-storage 禁用存储后端 false
--enable-cgroup 启用自身 cgroup 资源限制(默认关闭) false
--disable-tracing 禁用指定追踪模块(可多次指定) -
--log-debug 强制设置日志级别为 Debug false
--dry-run 仅加载测试,启动后优雅退出 false
--procfs-prefix procfs 挂载点前缀 -

12. 配置覆盖原则

当同一配置项同时存在于命令行参数和配置文件时,遵循以下优先级:

命令行参数 > 配置文件 > 内置默认值

具体规则:

  1. 日志级别:--log-debug > 配置文件 [Log] Level > 内置默认值 Info

    • --log-debug 具有最高优先级,无论配置文件中 Level 为何值均强制设为 Debug
    • 配置文件中显式设置 Level 时覆盖内置默认值
    • 均未设置时使用默认值 Info
  2. 追踪黑名单:--disable-tracing 与配置文件 BlackList 合并(两者互补,非覆盖)

  3. 其他布尔开关(--disable-kubelet、--disable-storage):命令行显式设置时覆盖配置文件

13. 配置最佳实践与注意事项

  • 资源控制:Kubernetes 使用 Pod resources,systemd 使用 service 的资源限制。只有直接运行且没有外部管理器时,才使用 --enable-cgroup 和 [Runtime]。
  • 存储选择:小规模部署可优先使用 LocalFile 进行本地排查;大规模集群推荐配置 Elasticsearch 实现集中存储与查询。
  • 自动追踪调优:根据业务负载特征调整阈值,过低阈值会导致频繁触发,过高则可能遗漏问题。建议在测试环境逐步验证。
  • 安全性:ES 配置中请使用强密码,并考虑启用 HTTPS;避免在配置文件中硬编码敏感信息。
  • 兼容性:配置参数受内核版本、硬件环境影响,建议结合 HUATUO 官方文档验证。

通过合理配置 huatuo-bamai.conf,可充分发挥 HUATUO 在内核级异常检测与智能追踪方面的优势,有效提升云原生系统的可观测性和故障诊断效率。如需针对特定场景的深度定制,欢迎提供更多环境细节进一步讨论。

14. 内存阈值快照部署与排障

14.1 部署条件与限制

  • 要求 Linux、memory cgroup v1/v2、宿主机 PID/procfs/cgroup 视图、 kubelet 元数据、内核 BTF 及加载和挂载 BPF 的权限。
  • 需读取目标进程内存(通常为 CAP_SYS_PTRACE)、访问 procfs/cgroup; v1 还需写入 cgroup.event_control。安全策略可能阻止访问。
  • 仅选择该 cgroup 的直接成员,排除 oom_score_adj = -1000 的进程。 超过 4096 个 PID、64 KiB PID 数据或 1 秒预算时跳过选择。
  • 最多监听 4096 个容器,目标由 pod 事件提供,不扫描 cgroup 树。 普通注册失败或容量不足时记录日志并跳过,监听失效后注销,不自动重试或补位。 同一实例不会因资源恢复或全量更新而重新注册,可能持续缺少压力监控。
  • 身份校验失败或容器元数据缺失时,不采集或不保存。
  • 进程选择与采集统一使用 Huatuo 所在 PID 命名空间的 /proc; --procfs-prefix 不会重定向内存快照读取。

下表为实验性实现范围,不代表所有版本均已验证:

运行时 实验性范围 主要限制
Go Go 1.18–1.26,64 位 ELF 去符号二进制的指令恢复仅支持 x86-64
Java Java 8+,64 位小端 ELF HotSpot,G1 GC 依赖可识别的 VMStruct/VMType 元数据
Python CPython 3.8–3.14,64 位小端 ELF 需能定位 _PyRuntime 并识别版本和布局

已记录的人工验证:x86-64 Linux、cgroup v1(legacy/hybrid)、Go 1.24.0。 请在实际环境验证压力触发与非空快照;跳过测试或返回 unavailable 不代表兼容。

14.2 输出与排障

Info 日志记录监听状态和采集尝试。 进程选择和保存细节记录在 Debug 日志中。 按容器/cgroup 找到采集记录,通过进程选择日志确定 PID; 运行时诊断查看已保存快照的状态和原因字段。 若采集只有开始而没有结束,使用 bamai 的 /debug/pprof/goroutine?debug=2 (需相应权限)确认阻塞栈;没有日志不代表监听已停止。

tracer_data.process_memory 在运行时探测无错误返回后读取一次 /proc/<pid>/status, 也适用于 C/C++ 等未识别的运行时。provider 失败会生成 failed 快照并保留已取得的摘要。 探测或输出处理出错时不保存结果,错误原因查看采集日志; 身份变化或任务取消时丢弃结果。不提供 PSS、映射排名或分配调用栈。

字段(字节) 来源 / 含义
virtual_bytes VmSize,虚拟地址空间,不是实际物理内存占用
rss_bytes VmRSS,常驻内存
rss_anon_bytes RssAnon,匿名常驻内存
rss_file_bytes RssFile,文件映射常驻内存
rss_shmem_bytes RssShmem,共享内存常驻量
swap_bytes VmSwap,私有匿名内存换出量,不含 shmem 换出
page_table_bytes VmPTE,页表内存

缺失或无效字段省略,不填 0;状态为 partial,完全无法读取时为 unavailable,附带 status_reason。这些近似值不是 OOM 瞬间快照,也不能直接证明泄漏; 候选进程不保证是最终 OOM victim。

结果沿用现有 [Storage] 配置,见第 6 节,无需另配存储。 LocalFile 文件名为 memory_threshold_snapshot;在 tracing_documents 中可按 tracer_name = memory_threshold_snapshot、tracer_type = autotracing 查询。

started_timestamp 记录采集尝试的开始时间,位于目标进程选择之前。 observed_timestamp 记录快照采集流程的开始时间,位于目标进程选定之后、 运行时识别之前。

tracer_data.snapshot.entries 的 kind 和统计口径如下:

运行时 kind objects bytes
Go inuse_space_objects 根据已发布统计,按完整分配栈聚合、经采样校正的未释放对象数量估计 对应的未释放字节数估计
Java object_class 按类汇总的实例数量估计 对象自身占用的堆内存估计(shallow heap)
Python gc_tracked_object_type 按类型汇总的 GC 跟踪对象数量 这些对象的浅层内存大小估计(shallow size)

Go 的 bytes 和 objects 分别对应 pprof 的 inuse_space 和 inuse_objects 统计口径。Java 未计算 retained heap;Python 只覆盖 GC 跟踪对象, 不能代表整个 Python 堆。历史记录中,Go 和 Java 的 kind 可能分别为 allocation_site 和 object_type;读取历史记录的消费方应兼容这些旧值。

Go 条目的 name 保留首个非 runtime 栈帧的纯函数名;全部为 runtime 栈帧时, 取第一帧。stack 仍为字符串数组,按分配位置到上层调用者的顺序保存栈帧。 文件和行号可用时,每帧使用 函数名, 源文件:行号 格式,逗号后留一个空格。例如:

{
  "kind": "inuse_space_objects",
  "name": "example/cache.allocate",
  "bytes": 131072,
  "objects": 32,
  "average_bytes": 4096,
  "stack": [
    "example/cache.allocate, example/cache/cache.go:123",
    "example/service.load, example/service/load.go:58",
    "main.main, example/cmd/server/main.go:42"
  ]
}

位置来自二进制的 Go 行号表,无需目标机器保存源码。路径保留编译元数据中的值, 可能受 -trimpath 影响,不保证对应目标机器上的实际文件。缺少文件或有效行号时, 栈帧只显示函数名;函数也无法解析时,保留十六进制 PC。历史记录的栈帧可能只有 函数名或地址,消费方应兼容;函数名和路径本身也可能包含逗号,不能按逗号任意拆分。 源码位置用于分配归因,不表示对象持有位置,也不保证完整展开内联调用链。

Go 仅使用 runtime 已发布的 active 计数计算分配量与释放量之差,再进行采样校正, 不合并尚未发布的 future[0..2]。runtime 延迟发布以等待相应的 GC 清扫释放统计, 因此近期分配峰值可能尚未反映在结果中;结果是已发布堆画像的外部采样, 不能代表触发瞬间的堆占用。采集不获取 runtime 锁、不主动发布计数, 也不触发目标进程 GC,字段或 bucket 仍可能处于不同发布阶段,不能保证与 pprof 原子一致。

完整扫描未观察到任何非零 active 计数时,返回 unavailable, status_reason 为 Go heap profile has no published statistics,不回退到 future。 这可能发生在尚未 GC 的进程中,并不表示没有堆对象。 已有发布计数但样本全部释放时,完整扫描仍可返回 complete 和空条目。 扫描因其他问题中断时保留 partial 及其原因,不将未扫描到数据解释为尚未发布。

Go 使用完整栈作为聚合键:Go 1.18–1.22 最多 32 帧,Go 1.23–1.26 最多 1024 帧;统一输出限制可将展示栈缩短到 64 帧,并设置 output_truncated。bucket 类型无效、栈深度超过读取上限或 bucket 链表成环时,扫描以 partial 结束,重复 bucket 不会再次累计。

扫描遇到导致 partial 的问题后停止遍历后续 bucket,保留此前及当前批次中已读取且聚合预算允许的有效样本,再按内存字节数降序排列,最多保留 MaxMemoryObjectEntries 条结果。status_reason 只记录首次原因,收尾时不追加其他原因。

任何 bucket 头、记录或栈读取失败(包括短读)都会使本次 Go 采集失败,丢弃所有运行时条目,包括此前批次的数据。采集器输出 failed 和读取错误,不逐区间重试。

栈深度为 0 的样本不生成调用栈条目,也不会因空栈被标记为 partial。

Go 快照要求采样率已知、采样已启用且 bucket 链表非空。采样率未知、采样已禁用或 bucket 链表为空时返回 unavailable,附带 status_reason,不返回条目。

Go 采集在运行时读取、扫描、排序和条目生成阶段共用一个请求超时预算。 超时后丢弃运行时条目,由采集器输出 failed 和超时原因,仍尝试读取进程内存摘要。 取消采用协作方式,正在执行的系统调用或不支持取消的解析步骤可能在截止时间之后才结束。

查看 tracer_data.snapshot.status(complete、partial、 unavailable、failed),结合 status_reason、runtime_version、 duration_ms 和 output_truncated 判断结果。

status_reason 解释采集处于 partial、unavailable 或 failed 状态的原因, 为空时省略。snapshot 和 process_memory 均使用该字段。 历史记录可能使用 reason;同时读取新旧记录的消费方应兼容两种字段名。

duration_ms 统计 provider 阶段耗时,向上取整为毫秒;成功、失败及超时快照 使用相同口径,不包含运行时探测、进程内存摘要读取、输出处理和保存时间。

问题 检查项
没有输出 是否启用并重启、是否被 BlackList 禁用;v2 触发条件见 7.6
有事件但没有候选进程 进程是否直接属于该 cgroup、是否允许 OOM kill、是否超过枚举限制
unavailable / failed 运行时与布局限制、访问权限、容器元数据,以及目标是否已退出;具体见 status_reason
资源耗尽后事件停止 检查 RLIMIT_NOFILE、fs.inotify.max_user_watches、fs.inotify.max_user_instances,调整后重启;其他事件不受此停止影响

4.2 - huatuo-apiserver 配置说明

1. 概述

huatuo-apiserver 使用 TOML 配置文件并启用严格解析。未知或废弃的配置项 会阻止服务启动。以下被注释的配置使用所示默认值。

2. 日志与运行时资源限制

默认不创建 huatuo-apiserver 自身 cgroup。只有显式传入 --enable-cgroup 时, 本节 [Runtime] 配置才会生效;Kubernetes 和 systemd 部署应使用各自的资源管理配置。

# Log Configuration
[Log]
    # - Level
    # The log level for huatuo-apiserver: Debug, Info, Warn, Error, Panic.
    # Default: Info
    #
    # Level = "Info"

# Runtime limits for the huatuo-apiserver process.
[Runtime]
    # - CPULimitCores
    # CPU limit in cores.
    # Default: 20
    #
    # - MemoryLimitMiB
    # Memory limit in MiB.
    # Default: 4096
    #
    # CPULimitCores = 20
    # MemoryLimitMiB = 4096
  • Log.Level 支持 Debug、Info、Warn、Error 和 Panic。
  • CPULimitCores 以 CPU 核数限制 API 服务进程。
  • MemoryLimitMiB 以 MiB 限制 API 服务进程。

资源限制必须大于零。命令行参数 --log-debug 的优先级高于 Log.Level。

3. HTTP 服务

# HTTP server configuration.
[APIServer]
    # - ListenAddress
    # Listen address in "host:port" form.
    # Default: ":12740"
    #
    # ListenAddress = ":12740"

    # Request rate limiting.
    [APIServer.RateLimit]
        # - RequestsPerSecond
        # Maximum process-wide request rate per second.
        # Default: 200
        #
        # - Burst
        # Maximum process-wide request burst.
        # Default: 200
        #
        # RequestsPerSecond = 200
        # Burst = 200

ListenAddress 使用 host:port 格式,主机为空表示监听所有网络接口。 RateLimit 是进程级令牌桶,两个值都必须大于零。HTTP 超时和请求大小限制 属于服务固定防护参数,不对用户开放配置。

4. 任务与 Agent 通信

# Job persistence.
[Jobs]
    # - StoreDSN
    # SQLite DSN. Relative paths are resolved from this file's directory.
    # Default: "jobs.db"
    #
    # StoreDSN = "jobs.db"

    # Job 记录使用内部修订号进行并发更新。仅支持当前服务版本写入的记录;
    # 启动本版本前,需要停止旧服务并删除或替换不兼容的数据库。

    # Profiling and tracing retain independent quotas because their resource
    # costs and expected concurrency differ.
    [Jobs.Profiling]
        # - MaxConcurrentPerHost
        # Maximum concurrent profiling jobs on one host.
        # Default: 3
        #
        # - MaxConcurrent
        # Maximum concurrent profiling jobs across all hosts.
        # Default: 500
        #
        # MaxConcurrentPerHost = 3
        # MaxConcurrent = 500

    [Jobs.Tracing]
        # - MaxConcurrentPerHost
        # Maximum concurrent tracing jobs on one host.
        # Default: 5
        #
        # - MaxConcurrent
        # Maximum concurrent tracing jobs across all hosts.
        # Default: 1000
        #
        # MaxConcurrentPerHost = 5
        # MaxConcurrent = 1000

    # Apiserver 独立维护的 Job 生命周期策略。
    [Jobs.Controller]
        # StatusPollIntervalSeconds = 5
        # PendingTimeoutSeconds = 30
        # CompletionGracePeriodSeconds = 60
        # NodeUnavailableGracePeriodSeconds = 30
        # JobRetentionPeriodHours = 720

# huatuo-bamai Agent HTTP client configuration.
[Agent]
    # - HTTPPort
    # Agent HTTP server port.
    # Default: 19704
    #
    # HTTPPort = 19704

    [Agent.Auth]
        # 必须与各 Node 的 HTTPServer.Auth.BearerToken 一致。
        BearerToken = "REPLACE_WITH_RANDOM_HEX"

StoreDSN 是持久化任务状态的 SQLite 数据源。相对路径基于配置文件目录 解析。

Profiling 和 tracing 复用相同的配额结构,但保留独立配置值。两类任务的 资源开销和预期并发不同,统一限额会导致一类任务挤占另一类任务。

Agent 请求传输保护使用客户端内部默认值。Job 轮询、各阶段 deadline、Node 不可用宽限期和保留期由 Jobs.Controller 独立维护,不与 Node 运行时协商。

服务退出时不停止 Node 上的活动 Operation。新的 API 服务实例从持久化状态 恢复活动 Job 并继续监督,不重放 Start。

5. Elasticsearch/OpenSearch

# Optional Elasticsearch/OpenSearch backend for querying profiling data.
[Elasticsearch]
    # Address, Username, and Password must be configured together to enable
    # this backend.
    #
    # - Address
    # Elasticsearch or OpenSearch HTTP address.
    #
    # - Username
    # Elasticsearch or OpenSearch username.
    #
    # - Password
    # Elasticsearch or OpenSearch password.
    #
    # - Index
    # Index containing huatuo-bamai profiling data.
    # Default: "huatuo_bamai"
    #
    # Address = "https://elasticsearch.example.com:9200"
    # Username = "huatuo-apiserver"
    # Password = "REPLACE_WITH_STRONG_PASSWORD"
    # Index = "huatuo_bamai"

存储是可选能力。Address、Username 和 Password 必须同时为空或同时 配置。Index 默认为 huatuo_bamai,并且应与采集端索引一致。禁用存储 时,不注册原始 profile 和火焰图查询路由。

6. 认证与授权

# Authentication configuration.
[Auth]
    # - ID
    # Stable principal identifier stored with jobs.
    #
    # - BearerToken
    # Secret used only to authenticate requests. IDs and tokens must be unique.
    #
    # - Admin
    # Whether the principal has unrestricted API access.
    #
    # - Permissions
    # API method and path patterns granted to a restricted principal.
    #
    # Administrator example:
    # [[Auth.Users]]
    #     ID = "administrator"
    #     BearerToken = "REPLACE_WITH_RANDOM_HEX"
    #     Admin = true
    #
    # Restricted example:
    # [[Auth.Users]]
    #     ID = "huatuo-front"
    #     BearerToken = "REPLACE_WITH_ANOTHER_RANDOM_HEX"
    #     Permissions = [
    #         "GET /v1/tracing",
    #         "GET /v1/tracing/**",
    #         "GET /v1/profiling",
    #         "GET /v1/profiling/**",
    #     ]
  • ID 是必填的稳定主体标识,会随任务持久化。
  • BearerToken 是必填密钥,仅用于请求认证。
  • Admin 授予全部路由权限,并忽略 Permissions。
  • 非管理员必须配置 Permissions。权限可以仅包含路径,也可以包含 HTTP 方法前缀;* 匹配单个路径段,** 匹配后续路径。

用户 ID 和 BearerToken 都必须唯一。轮换 BearerToken 不会改变任务归属, 因为 Token 不再作为用户 ID 使用或写入任务存储。

/readyz、/metrics 和 /version 为公开路由。 /debug/pprof/** 和 /v1/profiling/flamegraph/** 仅管理员可访问。

7. 性能剖析

# Profiling 结果链接配置。
[Profiling]
    # - DashboardBaseURL
    # Optional dashboard base URL. Result URLs are omitted when empty.
    # Default: empty
    #
    # DashboardBaseURL = "https://grafana.example.com/d"
  • DashboardBaseURL 可选;配置时必须使用 HTTP 或 HTTPS。为空时,已完成 或结果未知的任务不生成 Dashboard URL。

Profiler 执行和聚合参数只在 Node 本地配置中维护。

5 - 核心特性

5.1 - 内核全景观测

当前版本支持的指标:

CPU 系统

调度延迟

如下指标可以观测进程调度延迟状态,即一个进程从变得可运行的时刻(即被放进运行队列),到它真正开始在 CPU 上执行的这段时间。

# HELP huatuo_bamai_runqlat_container_latency cpu run queue latency for the containers
# TYPE huatuo_bamai_runqlat_container_latency gauge
huatuo_bamai_runqlat_container_latency{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev",zone="0"} 226
huatuo_bamai_runqlat_container_latency{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev",zone="1"} 0
huatuo_bamai_runqlat_container_latency{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev",zone="2"} 0
huatuo_bamai_runqlat_container_latency{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev",zone="3"} 0

# HELP huatuo_bamai_runqlat_latency cpu run queue latency for the host
# TYPE huatuo_bamai_runqlat_latency gauge
huatuo_bamai_runqlat_latency{host="hostname",region="dev",zone="0"} 35100
huatuo_bamai_runqlat_latency{host="hostname",region="dev",zone="1"} 0
huatuo_bamai_runqlat_latency{host="hostname",region="dev",zone="2"} 0
huatuo_bamai_runqlat_latency{host="hostname",region="dev",zone="3"} 0
指标 意义 单位 对象 取值 标签
runqlat_container_latency 进程调度延迟计数:
zone0, 0~10ms
zone1, 10-20ms
zone2, 20-50ms
zone3, 50+ms
计数 容器 eBPF container_host, container_hostnamespace, container_level, container_name, container_type, host, region, zone
runqlat_latency 进程调度延迟计数:
zone0, 0~10ms
zone1, 10-20ms
zone2, 20-50ms
zone3, 50+ms
计数 物理机 eBPF host, region, zone

中断延迟

系统中各类软中断在不同CPU上的响应延迟指标(当前只采集了 NET_RX/NET_TX)。

# HELP huatuo_bamai_softirq_latency softirq latency
# TYPE huatuo_bamai_softirq_latency gauge
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_RX",zone="0"} 125
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_RX",zone="1"} 2
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_RX",zone="2"} 0
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_RX",zone="3"} 0
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_TX",zone="0"} 0
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_TX",zone="1"} 0
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_TX",zone="2"} 0
huatuo_bamai_softirq_latency{cpuid="0",host="hostname",region="dev",type="NET_TX",zone="3"} 0
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_RX",zone="0"} 110
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_RX",zone="1"} 0
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_RX",zone="2"} 1
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_RX",zone="3"} 0
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_TX",zone="0"} 0
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_TX",zone="1"} 0
huatuo_bamai_softirq_latency{cpuid="1",host="hostname",region="dev",type="NET_TX",zone="2"} 0
指标 意义 单位 对象 取值 标签
softirq_latency 软中断响应延迟在不同 zone 的计数:
zone0, 0-10us
zone1, 10-100us
zone2, 100-1000us
zone3, 1+ms
计数 物理机 eBPF cpuid, host, region, type, zone

资源利用率

通过如下指标可以观测,物理机,容器的 CPU 资源使用情况,prometheus 指标格式:

# HELP huatuo_bamai_cpu_util_sys cpu sys for the host
# TYPE huatuo_bamai_cpu_util_sys gauge
huatuo_bamai_cpu_util_sys{host="hostname",region="dev"} 6.268857848549965e-06
# HELP huatuo_bamai_cpu_util_total cpu total for the host
# TYPE huatuo_bamai_cpu_util_total gauge
huatuo_bamai_cpu_util_total{host="hostname",region="dev"} 1.7736934944144352e-05
# HELP huatuo_bamai_cpu_util_usr cpu usr for the host
# TYPE huatuo_bamai_cpu_util_usr gauge
huatuo_bamai_cpu_util_usr{host="hostname",region="dev"} 1.1468077095594387e-05

# HELP huatuo_bamai_cpu_util_container_sys cpu sys for the containers
# TYPE huatuo_bamai_cpu_util_container_sys gauge
huatuo_bamai_cpu_util_container_sys{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1.6708593420881415e-07
# HELP huatuo_bamai_cpu_util_container_total cpu total for the containers
# TYPE huatuo_bamai_cpu_util_container_total gauge
huatuo_bamai_cpu_util_container_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 3.379584661890774e-07
# HELP huatuo_bamai_cpu_util_container_usr cpu usr for the containers
# TYPE huatuo_bamai_cpu_util_container_usr gauge
huatuo_bamai_cpu_util_container_usr{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1.7087253017325962e-07
指标 意义 单位 对象 标签
cpu_util_sys CPU 内核态利用率 % 物理机 host, region
cpu_util_usr CPU 用户态利用率 % 物理机 host, region
cpu_util_total CPU 总利用率 % 物理机 host, region
cpu_util_container_sys CPU 内核态利用率 % 容器 container_host,container_hostnamespace,container_level,container_name,container_type,host,region
cpu_util_container_usr CPU 用户态利用率 % 容器 container_host,container_hostnamespace,container_level,container_name,container_type,host,region
cpu_util_container_total CPU 总利用率 % 容器 container_host,container_hostnamespace,container_level,container_name,container_type,host,region

资源配置

通过如下指标可以了解容器 CPU 资源配置情况,prometheus 指标格式:

# HELP huatuo_bamai_cpu_util_container_cores cpu core number for the containers
# TYPE huatuo_bamai_cpu_util_container_cores gauge
huatuo_bamai_cpu_util_container_cores{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="Burstable",container_name="coredns",container_type="Normal",host="hostname",region="dev"} 6
指标 意义 单位 对象 标签
cpu_util_container_cores CPU 核心数 个 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

资源争抢

这些指标体现了容器争抢,被限制等状态,prometheus 指标格式:

# HELP huatuo_bamai_cpu_stat_container_nr_throttled throttle nr for the containers
# TYPE huatuo_bamai_cpu_stat_container_nr_throttled gauge
huatuo_bamai_cpu_stat_container_nr_throttled{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_cpu_stat_container_throttled_time throttle time for the containers
# TYPE huatuo_bamai_cpu_stat_container_throttled_time gauge
huatuo_bamai_cpu_stat_container_throttled_time{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
cpu_stat_container_nr_throttled 当前 cgroup 被 throttled 限制的次数 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
cpu_stat_container_throttled_time 当前 cgroup 被 throttled 限制的总时间 纳秒 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

Ref:

内核导出 wait_sum 时提供以下争抢指标:

# HELP huatuo_bamai_cpu_stat_container_wait_sum_percent percentage of CFS cgroup schedulable time spent waiting on the parent runqueue (requires kernel.sched_schedstats=1)
# TYPE huatuo_bamai_cpu_stat_container_wait_sum_percent gauge
huatuo_bamai_cpu_stat_container_wait_sum_percent{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0

资源突发

如下指标体现了容器出现资源突发使用状态:

# HELP huatuo_bamai_cpu_stat_container_nr_bursts burst nr for the containers
# TYPE huatuo_bamai_cpu_stat_container_nr_bursts gauge
huatuo_bamai_cpu_stat_container_nr_bursts{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_cpu_stat_container_burst_time burst time for the containers
# TYPE huatuo_bamai_cpu_stat_container_burst_time gauge
huatuo_bamai_cpu_stat_container_burst_time{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
cpu_stat_container_burst_time 所有在各个周期中超过 quota 部分所累计使用的真实墙钟时间 纳秒 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
cpu_stat_container_nr_bursts 发生超额使用的周期数量 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

资源负载

这些指标体现物理机、容器负载状态。

# HELP huatuo_bamai_loadavg_load1 system load average, 1 minute
# TYPE huatuo_bamai_loadavg_load1 gauge
huatuo_bamai_loadavg_load1{host="hostname",region="dev"} 0.3
# HELP huatuo_bamai_loadavg_load15 system load average, 15 minutes
# TYPE huatuo_bamai_loadavg_load15 gauge
huatuo_bamai_loadavg_load15{host="hostname",region="dev"} 0.22
# HELP huatuo_bamai_loadavg_load5 system load average, 5 minutes
# TYPE huatuo_bamai_loadavg_load5 gauge
huatuo_bamai_loadavg_load5{host="hostname",region="dev"} 0.2
# HELP huatuo_bamai_loadavg_container_nr_running nr_running of container
# TYPE huatuo_bamai_loadavg_container_nr_running gauge
huatuo_bamai_loadavg_container_nr_running{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_loadavg_container_nr_uninterruptible nr_uninterruptible of container
# TYPE huatuo_bamai_loadavg_container_nr_uninterruptible gauge
huatuo_bamai_loadavg_container_nr_uninterruptible{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签 备注
loadavg_load1 系统过去 1 分钟的平均负载 计数 物理机 host, region
loadavg_load5 系统过去 5 分钟的平均负载 计数 物理机 host, region
loadavg_load15 系统过去 15 分钟的平均负载 计数 物理机 host, region
loadavg_container_container_nr_running 容器中运行的任务数量 计数 容器 host, region 只支持 cgroup v1
loadavg_container_container_nr_uninterruptible 容器中不可中断任务的数量 计数 容器 host, region 只支持 cgroup v1

内存系统

资源回收

系统内存回收行为可能导致进程被阻塞。通过这些指标可以了解系统内存状态。

# HELP huatuo_bamai_memory_free_allocpages_stall time stalled in alloc pages
# TYPE huatuo_bamai_memory_free_allocpages_stall gauge
huatuo_bamai_memory_free_allocpages_stall{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_free_compaction_stall time stalled in memory compaction
# TYPE huatuo_bamai_memory_free_compaction_stall gauge
huatuo_bamai_memory_free_compaction_stall{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_reclaim_container_directstall counter of cgroup reclaim when try_charge
# TYPE huatuo_bamai_memory_reclaim_container_directstall gauge
huatuo_bamai_memory_reclaim_container_directstall{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 取值 标签
memory_free_allocpages_stall 系统在分配内存页过程中的耗时计数 纳秒 物理机 eBPF host, region
memory_free_compaction_stall 系统在规整内存页过程中的耗时计数 纳秒 物理机 eBPF host, region
memory_reclaim_container_directstall 容器直接内存事件次数 计数 容器 eBPF container_host, container_hostnamespace, container_level, container_name, container_type, host, region

注意:memory_others_container_directstall_time、memory_others_container_asyncreclaim_time、memory_others_container_local_direct_reclaim_time 指标读取的是滴滴云定制内核提供的 memory cgroup 扩展接口(memory.directstall_stat、memory.asynreclaim_stat、memory.local_direct_reclaim_time)。主线内核及常见发行版内核不提供这些接口,因此这些指标不会输出,属预期行为,无需额外加载内核模块。在标准内核上观测容器直接回收(direct reclaim)行为,请使用上表基于 eBPF 实现的 memory_reclaim_container_directstall。

资源状态

通过如下指标可以了解整体系统、容器的内存状态。

# HELP huatuo_bamai_memory_vmstat_container_active_anon cgroup memory.stat active_anon
# TYPE huatuo_bamai_memory_vmstat_container_active_anon gauge
huatuo_bamai_memory_vmstat_container_active_anon{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1.47456e+07
# HELP huatuo_bamai_memory_vmstat_container_active_file cgroup memory.stat active_file
# TYPE huatuo_bamai_memory_vmstat_container_active_file gauge
huatuo_bamai_memory_vmstat_container_active_file{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 2.3617536e+07
# HELP huatuo_bamai_memory_vmstat_container_file_dirty cgroup memory.stat file_dirty
# TYPE huatuo_bamai_memory_vmstat_container_file_dirty gauge
huatuo_bamai_memory_vmstat_container_file_dirty{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_file_writeback cgroup memory.stat file_writeback
# TYPE huatuo_bamai_memory_vmstat_container_file_writeback gauge
huatuo_bamai_memory_vmstat_container_file_writeback{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_inactive_anon cgroup memory.stat inactive_anon
# TYPE huatuo_bamai_memory_vmstat_container_inactive_anon gauge
huatuo_bamai_memory_vmstat_container_inactive_anon{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_inactive_file cgroup memory.stat inactive_file
# TYPE huatuo_bamai_memory_vmstat_container_inactive_file gauge
huatuo_bamai_memory_vmstat_container_inactive_file{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 65536
# HELP huatuo_bamai_memory_vmstat_container_pgdeactivate cgroup memory.stat pgdeactivate
# TYPE huatuo_bamai_memory_vmstat_container_pgdeactivate gauge
huatuo_bamai_memory_vmstat_container_pgdeactivate{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_pgrefill cgroup memory.stat pgrefill
# TYPE huatuo_bamai_memory_vmstat_container_pgrefill gauge
huatuo_bamai_memory_vmstat_container_pgrefill{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_pgscan_direct cgroup memory.stat pgscan_direct
# TYPE huatuo_bamai_memory_vmstat_container_pgscan_direct gauge
huatuo_bamai_memory_vmstat_container_pgscan_direct{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_pgscan_kswapd cgroup memory.stat pgscan_kswapd
# TYPE huatuo_bamai_memory_vmstat_container_pgscan_kswapd gauge
huatuo_bamai_memory_vmstat_container_pgscan_kswapd{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_pgsteal_direct cgroup memory.stat pgsteal_direct
# TYPE huatuo_bamai_memory_vmstat_container_pgsteal_direct gauge
huatuo_bamai_memory_vmstat_container_pgsteal_direct{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_pgsteal_kswapd cgroup memory.stat pgsteal_kswapd
# TYPE huatuo_bamai_memory_vmstat_container_pgsteal_kswapd gauge
huatuo_bamai_memory_vmstat_container_pgsteal_kswapd{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_shmem cgroup memory.stat shmem
# TYPE huatuo_bamai_memory_vmstat_container_shmem gauge
huatuo_bamai_memory_vmstat_container_shmem{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_shmem_thp cgroup memory.stat shmem_thp
# TYPE huatuo_bamai_memory_vmstat_container_shmem_thp gauge
huatuo_bamai_memory_vmstat_container_shmem_thp{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_container_unevictable cgroup memory.stat unevictable
# TYPE huatuo_bamai_memory_vmstat_container_unevictable gauge
huatuo_bamai_memory_vmstat_container_unevictable{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
memory_vmstat_container_active_file 活跃的文件内存数 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_active_anon 活跃的匿名内存数 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_inactive_file 非活跃的文件内存数 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_inactive_anon 非活跃的匿名内存数 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_file_dirty 已修改且还未写入磁盘的文件内存大小 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_file_writeback 已修改且正等待写入磁盘的文件内存大小 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_dirty 已修改且还未写入磁盘的内存大小 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_writeback 已修改且正等待写入磁盘的文件,匿名内存大小 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_pgdeactivate 将页面从 active LRU 移动到 inactive LRU 的数量 页数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_pgrefill 在 active LRU 链表上被扫描的页面总数 页数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_pgscan_direct 直接回收时,在 inactive LRU 上扫描过的页面总数 页数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_pgscan_kswapd kswapd 在 inactive LRU 链表上扫描过的页面总数 页数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_pgsteal_direct 直接回收时,成功从 inactive LRU 回收的页面总数 页数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_pgsteal_kswapd kswapd 成功从 inactive LRU 回收的页面总数 页数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_vmstat_container_unevictable 不可回收的页面字节数 字节, Bytes 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

物理机内存资源指标:

# HELP huatuo_bamai_memory_vmstat_allocstall_device /proc/vmstat allocstall_device
# TYPE huatuo_bamai_memory_vmstat_allocstall_device gauge
huatuo_bamai_memory_vmstat_allocstall_device{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_allocstall_dma /proc/vmstat allocstall_dma
# TYPE huatuo_bamai_memory_vmstat_allocstall_dma gauge
huatuo_bamai_memory_vmstat_allocstall_dma{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_allocstall_dma32 /proc/vmstat allocstall_dma32
# TYPE huatuo_bamai_memory_vmstat_allocstall_dma32 gauge
huatuo_bamai_memory_vmstat_allocstall_dma32{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_allocstall_movable /proc/vmstat allocstall_movable
# TYPE huatuo_bamai_memory_vmstat_allocstall_movable gauge
huatuo_bamai_memory_vmstat_allocstall_movable{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_allocstall_normal /proc/vmstat allocstall_normal
# TYPE huatuo_bamai_memory_vmstat_allocstall_normal gauge
huatuo_bamai_memory_vmstat_allocstall_normal{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_nr_active_anon /proc/vmstat nr_active_anon
# TYPE huatuo_bamai_memory_vmstat_nr_active_anon gauge
huatuo_bamai_memory_vmstat_nr_active_anon{host="hostname",region="dev"} 155449
# HELP huatuo_bamai_memory_vmstat_nr_active_file /proc/vmstat nr_active_file
# TYPE huatuo_bamai_memory_vmstat_nr_active_file gauge
huatuo_bamai_memory_vmstat_nr_active_file{host="hostname",region="dev"} 212425
# HELP huatuo_bamai_memory_vmstat_nr_dirty /proc/vmstat nr_dirty
# TYPE huatuo_bamai_memory_vmstat_nr_dirty gauge
huatuo_bamai_memory_vmstat_nr_dirty{host="hostname",region="dev"} 19047
# HELP huatuo_bamai_memory_vmstat_nr_dirty_background_threshold /proc/vmstat nr_dirty_background_threshold
# TYPE huatuo_bamai_memory_vmstat_nr_dirty_background_threshold gauge
huatuo_bamai_memory_vmstat_nr_dirty_background_threshold{host="hostname",region="dev"} 379858
# HELP huatuo_bamai_memory_vmstat_nr_dirty_threshold /proc/vmstat nr_dirty_threshold
# TYPE huatuo_bamai_memory_vmstat_nr_dirty_threshold gauge
huatuo_bamai_memory_vmstat_nr_dirty_threshold{host="hostname",region="dev"} 760646
# HELP huatuo_bamai_memory_vmstat_nr_free_pages /proc/vmstat nr_free_pages
# TYPE huatuo_bamai_memory_vmstat_nr_free_pages gauge
huatuo_bamai_memory_vmstat_nr_free_pages{host="hostname",region="dev"} 3.20535e+06
# HELP huatuo_bamai_memory_vmstat_nr_inactive_anon /proc/vmstat nr_inactive_anon
# TYPE huatuo_bamai_memory_vmstat_nr_inactive_anon gauge
huatuo_bamai_memory_vmstat_nr_inactive_anon{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_nr_inactive_file /proc/vmstat nr_inactive_file
# TYPE huatuo_bamai_memory_vmstat_nr_inactive_file gauge
huatuo_bamai_memory_vmstat_nr_inactive_file{host="hostname",region="dev"} 428518
# HELP huatuo_bamai_memory_vmstat_nr_mlock /proc/vmstat nr_mlock
# TYPE huatuo_bamai_memory_vmstat_nr_mlock gauge
huatuo_bamai_memory_vmstat_nr_mlock{host="hostname",region="dev"} 6821
# HELP huatuo_bamai_memory_vmstat_nr_shmem /proc/vmstat nr_shmem
# TYPE huatuo_bamai_memory_vmstat_nr_shmem gauge
huatuo_bamai_memory_vmstat_nr_shmem{host="hostname",region="dev"} 541
# HELP huatuo_bamai_memory_vmstat_nr_shmem_hugepages /proc/vmstat nr_shmem_hugepages
# TYPE huatuo_bamai_memory_vmstat_nr_shmem_hugepages gauge
huatuo_bamai_memory_vmstat_nr_shmem_hugepages{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_nr_shmem_pmdmapped /proc/vmstat nr_shmem_pmdmapped
# TYPE huatuo_bamai_memory_vmstat_nr_shmem_pmdmapped gauge
huatuo_bamai_memory_vmstat_nr_shmem_pmdmapped{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_nr_slab_reclaimable /proc/vmstat nr_slab_reclaimable
# TYPE huatuo_bamai_memory_vmstat_nr_slab_reclaimable gauge
huatuo_bamai_memory_vmstat_nr_slab_reclaimable{host="hostname",region="dev"} 22322
# HELP huatuo_bamai_memory_vmstat_nr_slab_unreclaimable /proc/vmstat nr_slab_unreclaimable
# TYPE huatuo_bamai_memory_vmstat_nr_slab_unreclaimable gauge
huatuo_bamai_memory_vmstat_nr_slab_unreclaimable{host="hostname",region="dev"} 24168
# HELP huatuo_bamai_memory_vmstat_nr_unevictable /proc/vmstat nr_unevictable
# TYPE huatuo_bamai_memory_vmstat_nr_unevictable gauge
huatuo_bamai_memory_vmstat_nr_unevictable{host="hostname",region="dev"} 6839
# HELP huatuo_bamai_memory_vmstat_nr_writeback /proc/vmstat nr_writeback
# TYPE huatuo_bamai_memory_vmstat_nr_writeback gauge
huatuo_bamai_memory_vmstat_nr_writeback{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_nr_writeback_temp /proc/vmstat nr_writeback_temp
# TYPE huatuo_bamai_memory_vmstat_nr_writeback_temp gauge
huatuo_bamai_memory_vmstat_nr_writeback_temp{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_numa_pages_migrated /proc/vmstat numa_pages_migrated
# TYPE huatuo_bamai_memory_vmstat_numa_pages_migrated gauge
huatuo_bamai_memory_vmstat_numa_pages_migrated{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgdeactivate /proc/vmstat pgdeactivate
# TYPE huatuo_bamai_memory_vmstat_pgdeactivate gauge
huatuo_bamai_memory_vmstat_pgdeactivate{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgrefill /proc/vmstat pgrefill
# TYPE huatuo_bamai_memory_vmstat_pgrefill gauge
huatuo_bamai_memory_vmstat_pgrefill{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgscan_direct /proc/vmstat pgscan_direct
# TYPE huatuo_bamai_memory_vmstat_pgscan_direct gauge
huatuo_bamai_memory_vmstat_pgscan_direct{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgscan_direct_throttle /proc/vmstat pgscan_direct_throttle
# TYPE huatuo_bamai_memory_vmstat_pgscan_direct_throttle gauge
huatuo_bamai_memory_vmstat_pgscan_direct_throttle{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgscan_kswapd /proc/vmstat pgscan_kswapd
# TYPE huatuo_bamai_memory_vmstat_pgscan_kswapd gauge
huatuo_bamai_memory_vmstat_pgscan_kswapd{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgsteal_direct /proc/vmstat pgsteal_direct
# TYPE huatuo_bamai_memory_vmstat_pgsteal_direct gauge
huatuo_bamai_memory_vmstat_pgsteal_direct{host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_vmstat_pgsteal_kswapd /proc/vmstat pgsteal_kswapd
# TYPE huatuo_bamai_memory_vmstat_pgsteal_kswapd gauge
huatuo_bamai_memory_vmstat_pgsteal_kswapd{host="hostname",region="dev"} 0
  • 页面状态与 LRU 分布, Page state & LRU
指标 意义 单位 对象 标签
nr_free_pages 空闲页面总数(伙伴系统可直接分配)。 页面 物理机 host, region
nr_inactive_anon 非活跃匿名页面数 页面 物理机 host, region
nr_inactive_file 活跃文件页面数 页面 物理机 host, region
nr_active_anon 活跃匿名页面数 页面 物理机 host, region
nr_active_file 活跃文件页面数 页面 物理机 host, region
nr_unevictable 不可回收页面数(mlocked、hugetlbfs 等) 页面 物理机 host, region
nr_mlock 被 mlock() 锁定的页面数 页面 物理机 host, region
nr_shmem tmpfs / shmem 使用的页面数 页面 物理机 host, region
nr_slab_reclaimable 可回收的 slab 缓存对象 页面 物理机 host, region
nr_slab_unreclaimable 不可回收的 slab 缓存对象 页面 物理机 host, region
  • 脏页与写回控制, Dirty & writeback thresholds
指标 意义 单位 对象 标签
nr_dirty 当前脏页数 页面 物理机 host, region
nr_writeback 正在写回的页面数 页面 物理机 host, region
nr_dirty_threshold 脏页达到此阈值时开始强制写回(dirty_background_ratio / dirty_ratio 决定) 页面 物理机 host, region
nr_dirty_background_threshold 后台写回开始的阈值 页面 物理机 host, region
nr_dirty_background_threshold 后台写回开始的阈值 页面 物理机 host, region
  • 页面错误与换页, Page fault & swapping
指标 意义 单位 对象 标签
pgfault 总缺页异常次数 计数 物理机 host, region
pgmajfault 主缺页异常次数 计数 物理机 host, region
pgpgin 从块设备读入的页面数 页面 物理机 host, region
pgpgout 写出到块设备的页面数 页面 物理机 host, region
pswpin/pswpout 换入/换出的页面数(swap) 页面 物理机 host, region
  • 回收与扫描, Reclaim & scanning
指标 意义 单位 对象 标签
pgscan_kswapd/direct/khugepaged kswapd/直接回收/khugepaged 扫描的页面数 页面数 物理机 host, region
pgsteal_kswapd/direct/khugepaged 回收成功的页面数 页面数 物理机 host, region
  • 透明大页, THP
指标 意义 单位 对象 标签
thp_fault_alloc 缺页时成功分配 THP 的次数 计数 物理机 host, region
thp_fault_fallback 缺页时分配 THP 失败而回落普通页的次数 计数 物理机 host, region
thp_collapse_alloc khugepaged 折叠成 THP 的成功次数 计数 物理机 host, region
thp_collapse_alloc_failed khugepaged 折叠 THP 的失败次数 计数 物理机 host, region
  • NUMA 相关统计, NUMA balancing & allocation
指标 意义 单位 对象 标签
numa_hit 进程希望从某个节点分配内存,并且成功在该节点上分配到的页面总数。 计数 物理机 host, region
numa_miss 进程原本希望从其他节点分配,但由于目标节点内存不足等原因,最终在本节点分配成功的页面数。 计数 物理机 host, region
numa_foreign 进程原本希望从本节点分配内存,但最终在其他节点分配成功的页面数。 计数 物理机 host, region
numa_local 进程在本地节点上成功分配到的页面总数。 计数 物理机 host, region
numa_other 进程在远程节点上分配到的页面总数。 计数 物理机 host, region
numa_pages_migrated 由于自动 NUMA 平衡而成功迁移的页面总数 计数 物理机 host, region

Ref:

资源事件

容器级别的内存事件指标。

# HELP huatuo_bamai_memory_events_container_high memory events high
# TYPE huatuo_bamai_memory_events_container_high gauge
huatuo_bamai_memory_events_container_high{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_events_container_low memory events low
# TYPE huatuo_bamai_memory_events_container_low gauge
huatuo_bamai_memory_events_container_low{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_events_container_max memory events max
# TYPE huatuo_bamai_memory_events_container_max gauge
huatuo_bamai_memory_events_container_max{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_events_container_oom memory events oom
# TYPE huatuo_bamai_memory_events_container_oom gauge
huatuo_bamai_memory_events_container_oom{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_events_container_oom_group_kill memory events oom_group_kill
# TYPE huatuo_bamai_memory_events_container_oom_group_kill gauge
huatuo_bamai_memory_events_container_oom_group_kill{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_memory_events_container_oom_kill memory events oom_kill
# TYPE huatuo_bamai_memory_events_container_oom_kill gauge
huatuo_bamai_memory_events_container_oom_kill{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
memory_events_container_low 使用量低于 memory.low,但由于系统内存压力大,仍被主动回收的次数。说明 memory.low 被过度承诺。 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_events_container_high 内存使用量超过 memory.high(软限制),导致进程被节流并强制走直接回收的次数。 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_events_container_max 内存使用量达到或即将超过 memory.max(硬限制),触发内存分配失败检查的次数。 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_events_container_oom 内存使用量达到 memory.max 限制,导致内存分配失败,进入 OOM 路径的次数。 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_events_container_oom_kill cgroup 内因达到内存限制而被 OOM killer 杀死的进程数。 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
memory_events_container_oom_group_kill 整个 cgroup 被 OOM killer 杀死的次数。 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

Buddyinfo

展示 Buddy 分配器(内核页分配器核心算法)在每个 NUMA 节点(Node)和每个内存区域(Zone)中的空闲内存块分布情况。

# HELP huatuo_bamai_memory_buddyinfo_blocks buddy info
# TYPE huatuo_bamai_memory_buddyinfo_blocks gauge
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="0",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="0",region="dev",zone="DMA32"} 3
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="0",region="dev",zone="Normal"} 7
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="1",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="1",region="dev",zone="DMA32"} 1
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="1",region="dev",zone="Normal"} 36
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="10",region="dev",zone="DMA"} 2
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="10",region="dev",zone="DMA32"} 743
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="10",region="dev",zone="Normal"} 2265
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="2",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="2",region="dev",zone="DMA32"} 3
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="2",region="dev",zone="Normal"} 10
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="3",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="3",region="dev",zone="DMA32"} 2
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="3",region="dev",zone="Normal"} 224
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="4",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="4",region="dev",zone="DMA32"} 1
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="4",region="dev",zone="Normal"} 376
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="5",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="5",region="dev",zone="DMA32"} 1
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="5",region="dev",zone="Normal"} 165
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="6",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="6",region="dev",zone="DMA32"} 3
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="6",region="dev",zone="Normal"} 118
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="7",region="dev",zone="DMA"} 0
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="7",region="dev",zone="DMA32"} 4
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="7",region="dev",zone="Normal"} 172
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="8",region="dev",zone="DMA"} 1
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="8",region="dev",zone="DMA32"} 4
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="8",region="dev",zone="Normal"} 35
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="9",region="dev",zone="DMA"} 2
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="9",region="dev",zone="DMA32"} 4
huatuo_bamai_memory_buddyinfo_blocks{host="hostname",node="0",order="9",region="dev",zone="Normal"} 25
指标 意义 单位 对象 取值 标签
memory_buddyinfo_blocks buddy 内存页空闲情况。 内存页 物理机 procfs host, node, order, region, zone

网络系统

TCP 内存

如下指标描述 TCP 协议栈占用系统内存状态。

# HELP huatuo_bamai_tcp_memory_limit_pages tcp memory pages limit
# TYPE huatuo_bamai_tcp_memory_limit_pages gauge
huatuo_bamai_tcp_memory_limit_pages{host="hostname",region="dev"} 380526
# HELP huatuo_bamai_tcp_memory_usage_bytes tcp memory bytes usage
# TYPE huatuo_bamai_tcp_memory_usage_bytes gauge
huatuo_bamai_tcp_memory_usage_bytes{host="hostname",region="dev"} 0
# HELP huatuo_bamai_tcp_memory_usage_pages tcp memory pages usage
# TYPE huatuo_bamai_tcp_memory_usage_pages gauge
huatuo_bamai_tcp_memory_usage_pages{host="hostname",region="dev"} 0
# HELP huatuo_bamai_tcp_memory_usage_percent tcp memory usage percent
# TYPE huatuo_bamai_tcp_memory_usage_percent gauge
huatuo_bamai_tcp_memory_usage_percent{host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
tcp_memory_limit_pages 系统可使用的 TCP 总内存大小 内存页 物理机 host, region
tcp_memory_usage_bytes 系统已使用的 TCP 内存大小 字节 物理机 host, region
tcp_memory_usage_pages 系统已使用的 TCP 内存大小 内存页 物理机 host, region
tcp_memory_usage_percent 系统已使用的 TCP 内存百分比(相对 TCP 内存总限制) % 物理机 host, region

邻居项

如下指标描述邻居项使用状态。

# HELP huatuo_bamai_arp_container_entries arp entries in container netns
# TYPE huatuo_bamai_arp_container_entries gauge
huatuo_bamai_arp_container_entries{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_arp_entries host init namespace
# TYPE huatuo_bamai_arp_entries gauge
huatuo_bamai_arp_entries{host="hostname",region="dev"} 5
# HELP huatuo_bamai_arp_total all entries in arp_cache for containers and host netns
# TYPE huatuo_bamai_arp_total gauge
huatuo_bamai_arp_total{host="hostname",region="dev"} 12
指标 意义 单位 对象 标签
arp_entries 宿主机网络命名空间 arp 条目数量 计数 宿主命名空间 host, region
arp_total 物理机所有网络命名空间 arp 条目数量总和 计数 物理机 host, region
arp_container_entries 容器网络命名空间 arp 条目数量 计数 容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

Qdisc

Qdisc 是内核网络子系统重要模块。通过观测该模块,可以清楚的看到网络报文处理,延迟情况。

# HELP huatuo_bamai_netdev_qdisc_backlog Number of bytes currently in queue to be sent.
# TYPE huatuo_bamai_netdev_qdisc_backlog gauge
huatuo_bamai_netdev_qdisc_backlog{device="ens2",host="hostname",kind="fq_codel",region="dev"} 0
# HELP huatuo_bamai_netdev_qdisc_bytes_total Number of bytes sent.
# TYPE huatuo_bamai_netdev_qdisc_bytes_total counter
huatuo_bamai_netdev_qdisc_bytes_total{device="ens2",host="hostname",kind="fq_codel",region="dev"} 2.578235443e+09
# HELP huatuo_bamai_netdev_qdisc_current_queue_length Number of packets currently in queue to be sent.
# TYPE huatuo_bamai_netdev_qdisc_current_queue_length gauge
huatuo_bamai_netdev_qdisc_current_queue_length{device="ens2",host="hostname",kind="fq_codel",region="dev"} 0
# HELP huatuo_bamai_netdev_qdisc_drops_total Number of packet drops.
# TYPE huatuo_bamai_netdev_qdisc_drops_total counter
huatuo_bamai_netdev_qdisc_drops_total{device="ens2",host="hostname",kind="fq_codel",region="dev"} 0
# HELP huatuo_bamai_netdev_qdisc_overlimits_total Number of packet overlimits.
# TYPE huatuo_bamai_netdev_qdisc_overlimits_total counter
huatuo_bamai_netdev_qdisc_overlimits_total{device="ens2",host="hostname",kind="fq_codel",region="dev"} 0
# HELP huatuo_bamai_netdev_qdisc_packets_total Number of packets sent.
# TYPE huatuo_bamai_netdev_qdisc_packets_total counter
huatuo_bamai_netdev_qdisc_packets_total{device="ens2",host="hostname",kind="fq_codel",region="dev"} 6.867714e+06
# HELP huatuo_bamai_netdev_qdisc_requeues_total Number of packets dequeued, not transmitted, and requeued.
# TYPE huatuo_bamai_netdev_qdisc_requeues_total counter
huatuo_bamai_netdev_qdisc_requeues_total{device="ens2",host="hostname",kind="fq_codel",region="dev"} 0
指标 意义 单位 对象 标签
qdisc_backlog 后备排队待发送的字节数 字节 物理机 device, host, kind, region
qdisc_current_queue_length 当前排队的包量 计数 物理机 device, host, kind, region
qdisc_overlimits_total 超限次数 计数 物理机 device, host, kind, region
qdisc_requeues_total 由于网卡/驱动暂时无法发送而被重新入队的次数 计数 物理机 device, host, kind, region
qdisc_drops_total 主动丢弃的包数(因队列满、限速策略等原因) 计数 物理机 device, host, kind, region
qdisc_bytes_total 已发送的包量 字节 物理机 device, host, kind, region
qdisc_packets_total 已发送的包数 计数 物理机 device, host, kind, region

硬件丢包

网络设备硬件接收方向丢包数。

# HELP huatuo_bamai_netdev_hw_rx_dropped count of packets dropped at hardware level
# TYPE huatuo_bamai_netdev_hw_rx_dropped gauge
huatuo_bamai_netdev_hw_rx_dropped{device="eth0",driver="mlx5_core",host="hostname",region="dev"} 0
指标 意义 单位 对象 取值 标签
netdev_hw_rx_dropped 网卡硬件接收方向丢包 计数 物理机 eBPF device, driver, host, region

网络设备

# HELP huatuo_bamai_netdev_container_receive_bytes_total Network device statistic receive_bytes.
# TYPE huatuo_bamai_netdev_container_receive_bytes_total counter
huatuo_bamai_netdev_container_receive_bytes_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 6.4400018e+07
# HELP huatuo_bamai_netdev_container_receive_compressed_total Network device statistic receive_compressed.
# TYPE huatuo_bamai_netdev_container_receive_compressed_total counter
huatuo_bamai_netdev_container_receive_compressed_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_receive_dropped_total Network device statistic receive_dropped.
# TYPE huatuo_bamai_netdev_container_receive_dropped_total counter
huatuo_bamai_netdev_container_receive_dropped_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_receive_errors_total Network device statistic receive_errors.
# TYPE huatuo_bamai_netdev_container_receive_errors_total counter
huatuo_bamai_netdev_container_receive_errors_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_receive_fifo_total Network device statistic receive_fifo.
# TYPE huatuo_bamai_netdev_container_receive_fifo_total counter
huatuo_bamai_netdev_container_receive_fifo_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_receive_frame_total Network device statistic receive_frame.
# TYPE huatuo_bamai_netdev_container_receive_frame_total counter
huatuo_bamai_netdev_container_receive_frame_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_receive_multicast_total Network device statistic receive_multicast.
# TYPE huatuo_bamai_netdev_container_receive_multicast_total counter
huatuo_bamai_netdev_container_receive_multicast_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_receive_packets_total Network device statistic receive_packets.
# TYPE huatuo_bamai_netdev_container_receive_packets_total counter
huatuo_bamai_netdev_container_receive_packets_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 693155
# HELP huatuo_bamai_netdev_container_transmit_bytes_total Network device statistic transmit_bytes.
# TYPE huatuo_bamai_netdev_container_transmit_bytes_total counter
huatuo_bamai_netdev_container_transmit_bytes_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 6.2347911e+07
# HELP huatuo_bamai_netdev_container_transmit_carrier_total Network device statistic transmit_carrier.
# TYPE huatuo_bamai_netdev_container_transmit_carrier_total counter
huatuo_bamai_netdev_container_transmit_carrier_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_transmit_colls_total Network device statistic transmit_colls.
# TYPE huatuo_bamai_netdev_container_transmit_colls_total counter
huatuo_bamai_netdev_container_transmit_colls_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_transmit_compressed_total Network device statistic transmit_compressed.
# TYPE huatuo_bamai_netdev_container_transmit_compressed_total counter
huatuo_bamai_netdev_container_transmit_compressed_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_transmit_dropped_total Network device statistic transmit_dropped.
# TYPE huatuo_bamai_netdev_container_transmit_dropped_total counter
huatuo_bamai_netdev_container_transmit_dropped_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_transmit_errors_total Network device statistic transmit_errors.
# TYPE huatuo_bamai_netdev_container_transmit_errors_total counter
huatuo_bamai_netdev_container_transmit_errors_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_transmit_fifo_total Network device statistic transmit_fifo.
# TYPE huatuo_bamai_netdev_container_transmit_fifo_total counter
huatuo_bamai_netdev_container_transmit_fifo_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netdev_container_transmit_packets_total Network device statistic transmit_packets.
# TYPE huatuo_bamai_netdev_container_transmit_packets_total counter
huatuo_bamai_netdev_container_transmit_packets_total{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",device="eth0",host="hostname",region="dev"} 660218
指标 意义 单位 对象 标签
netdev_receive_bytes_total 成功接收的总字节数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_receive_packets_total 成功接收的数据包总数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_receive_compressed_total 接收到的已压缩数据包数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_receive_frame_total 接收帧错误数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_receive_errors_total 接收错误总数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_receive_dropped_total 由于各种原因被内核或驱动丢弃的接收包数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_receive_fifo_total 接收FIFO/环形缓冲区溢出错误数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_bytes_total 成功发送的总字节数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_packets_total 成功发送的数据包总数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_errors_total 发送错误总数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_dropped_total 发送过程中被丢弃的包数(队列满、策略丢弃等) 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_fifo_total 发送FIFO/环形缓冲区错误数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_carrier_total 载波错误次数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netdev_transmit_compressed_total 发送的已压缩数据包数 计数 物理机或者容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

TCP

# HELP huatuo_bamai_netstat_container_TcpExt_ArpFilter statistic TcpExtArpFilter.
# TYPE huatuo_bamai_netstat_container_TcpExt_ArpFilter gauge
huatuo_bamai_netstat_container_TcpExt_ArpFilter{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_BusyPollRxPackets statistic TcpExtBusyPollRxPackets.
# TYPE huatuo_bamai_netstat_container_TcpExt_BusyPollRxPackets gauge
huatuo_bamai_netstat_container_TcpExt_BusyPollRxPackets{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_DelayedACKLocked statistic TcpExtDelayedACKLocked.
# TYPE huatuo_bamai_netstat_container_TcpExt_DelayedACKLocked gauge
huatuo_bamai_netstat_container_TcpExt_DelayedACKLocked{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_DelayedACKLost statistic TcpExtDelayedACKLost.
# TYPE huatuo_bamai_netstat_container_TcpExt_DelayedACKLost gauge
huatuo_bamai_netstat_container_TcpExt_DelayedACKLost{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_DelayedACKs statistic TcpExtDelayedACKs.
# TYPE huatuo_bamai_netstat_container_TcpExt_DelayedACKs gauge
huatuo_bamai_netstat_container_TcpExt_DelayedACKs{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 4650
# HELP huatuo_bamai_netstat_container_TcpExt_EmbryonicRsts statistic TcpExtEmbryonicRsts.
# TYPE huatuo_bamai_netstat_container_TcpExt_EmbryonicRsts gauge
huatuo_bamai_netstat_container_TcpExt_EmbryonicRsts{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_IPReversePathFilter statistic TcpExtIPReversePathFilter.
# TYPE huatuo_bamai_netstat_container_TcpExt_IPReversePathFilter gauge
huatuo_bamai_netstat_container_TcpExt_IPReversePathFilter{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_ListenDrops statistic TcpExtListenDrops.
# TYPE huatuo_bamai_netstat_container_TcpExt_ListenDrops gauge
huatuo_bamai_netstat_container_TcpExt_ListenDrops{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_ListenOverflows statistic TcpExtListenOverflows.
# TYPE huatuo_bamai_netstat_container_TcpExt_ListenOverflows gauge
huatuo_bamai_netstat_container_TcpExt_ListenOverflows{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_LockDroppedIcmps statistic TcpExtLockDroppedIcmps.
# TYPE huatuo_bamai_netstat_container_TcpExt_LockDroppedIcmps gauge
huatuo_bamai_netstat_container_TcpExt_LockDroppedIcmps{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_OfoPruned statistic TcpExtOfoPruned.
# TYPE huatuo_bamai_netstat_container_TcpExt_OfoPruned gauge
huatuo_bamai_netstat_container_TcpExt_OfoPruned{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_OutOfWindowIcmps statistic TcpExtOutOfWindowIcmps.
# TYPE huatuo_bamai_netstat_container_TcpExt_OutOfWindowIcmps gauge
huatuo_bamai_netstat_container_TcpExt_OutOfWindowIcmps{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_PAWSActive statistic TcpExtPAWSActive.
# TYPE huatuo_bamai_netstat_container_TcpExt_PAWSActive gauge
huatuo_bamai_netstat_container_TcpExt_PAWSActive{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_PAWSEstab statistic TcpExtPAWSEstab.
# TYPE huatuo_bamai_netstat_container_TcpExt_PAWSEstab gauge
huatuo_bamai_netstat_container_TcpExt_PAWSEstab{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_PFMemallocDrop statistic TcpExtPFMemallocDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_PFMemallocDrop gauge
huatuo_bamai_netstat_container_TcpExt_PFMemallocDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_PruneCalled statistic TcpExtPruneCalled.
# TYPE huatuo_bamai_netstat_container_TcpExt_PruneCalled gauge
huatuo_bamai_netstat_container_TcpExt_PruneCalled{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_RcvPruned statistic TcpExtRcvPruned.
# TYPE huatuo_bamai_netstat_container_TcpExt_RcvPruned gauge
huatuo_bamai_netstat_container_TcpExt_RcvPruned{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_SyncookiesFailed statistic TcpExtSyncookiesFailed.
# TYPE huatuo_bamai_netstat_container_TcpExt_SyncookiesFailed gauge
huatuo_bamai_netstat_container_TcpExt_SyncookiesFailed{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_SyncookiesRecv statistic TcpExtSyncookiesRecv.
# TYPE huatuo_bamai_netstat_container_TcpExt_SyncookiesRecv gauge
huatuo_bamai_netstat_container_TcpExt_SyncookiesRecv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_SyncookiesSent statistic TcpExtSyncookiesSent.
# TYPE huatuo_bamai_netstat_container_TcpExt_SyncookiesSent gauge
huatuo_bamai_netstat_container_TcpExt_SyncookiesSent{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedChallenge statistic TcpExtTCPACKSkippedChallenge.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedChallenge gauge
huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedChallenge{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedFinWait2 statistic TcpExtTCPACKSkippedFinWait2.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedFinWait2 gauge
huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedFinWait2{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedPAWS statistic TcpExtTCPACKSkippedPAWS.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedPAWS gauge
huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedPAWS{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedSeq statistic TcpExtTCPACKSkippedSeq.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedSeq gauge
huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedSeq{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedSynRecv statistic TcpExtTCPACKSkippedSynRecv.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedSynRecv gauge
huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedSynRecv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedTimeWait statistic TcpExtTCPACKSkippedTimeWait.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedTimeWait gauge
huatuo_bamai_netstat_container_TcpExt_TCPACKSkippedTimeWait{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAOBad statistic TcpExtTCPAOBad.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAOBad gauge
huatuo_bamai_netstat_container_TcpExt_TCPAOBad{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAODroppedIcmps statistic TcpExtTCPAODroppedIcmps.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAODroppedIcmps gauge
huatuo_bamai_netstat_container_TcpExt_TCPAODroppedIcmps{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAOGood statistic TcpExtTCPAOGood.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAOGood gauge
huatuo_bamai_netstat_container_TcpExt_TCPAOGood{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAOKeyNotFound statistic TcpExtTCPAOKeyNotFound.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAOKeyNotFound gauge
huatuo_bamai_netstat_container_TcpExt_TCPAOKeyNotFound{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAORequired statistic TcpExtTCPAORequired.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAORequired gauge
huatuo_bamai_netstat_container_TcpExt_TCPAORequired{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAbortFailed statistic TcpExtTCPAbortFailed.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAbortFailed gauge
huatuo_bamai_netstat_container_TcpExt_TCPAbortFailed{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAbortOnClose statistic TcpExtTCPAbortOnClose.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAbortOnClose gauge
huatuo_bamai_netstat_container_TcpExt_TCPAbortOnClose{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAbortOnData statistic TcpExtTCPAbortOnData.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAbortOnData gauge
huatuo_bamai_netstat_container_TcpExt_TCPAbortOnData{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAbortOnLinger statistic TcpExtTCPAbortOnLinger.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAbortOnLinger gauge
huatuo_bamai_netstat_container_TcpExt_TCPAbortOnLinger{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAbortOnMemory statistic TcpExtTCPAbortOnMemory.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAbortOnMemory gauge
huatuo_bamai_netstat_container_TcpExt_TCPAbortOnMemory{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAbortOnTimeout statistic TcpExtTCPAbortOnTimeout.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAbortOnTimeout gauge
huatuo_bamai_netstat_container_TcpExt_TCPAbortOnTimeout{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAckCompressed statistic TcpExtTCPAckCompressed.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAckCompressed gauge
huatuo_bamai_netstat_container_TcpExt_TCPAckCompressed{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPAutoCorking statistic TcpExtTCPAutoCorking.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPAutoCorking gauge
huatuo_bamai_netstat_container_TcpExt_TCPAutoCorking{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPBacklogCoalesce statistic TcpExtTCPBacklogCoalesce.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPBacklogCoalesce gauge
huatuo_bamai_netstat_container_TcpExt_TCPBacklogCoalesce{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 3
# HELP huatuo_bamai_netstat_container_TcpExt_TCPBacklogDrop statistic TcpExtTCPBacklogDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPBacklogDrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPBacklogDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPChallengeACK statistic TcpExtTCPChallengeACK.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPChallengeACK gauge
huatuo_bamai_netstat_container_TcpExt_TCPChallengeACK{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredDubious statistic TcpExtTCPDSACKIgnoredDubious.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredDubious gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredDubious{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredNoUndo statistic TcpExtTCPDSACKIgnoredNoUndo.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredNoUndo gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredNoUndo{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredOld statistic TcpExtTCPDSACKIgnoredOld.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredOld gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKIgnoredOld{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKOfoRecv statistic TcpExtTCPDSACKOfoRecv.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKOfoRecv gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKOfoRecv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKOfoSent statistic TcpExtTCPDSACKOfoSent.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKOfoSent gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKOfoSent{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKOldSent statistic TcpExtTCPDSACKOldSent.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKOldSent gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKOldSent{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKRecv statistic TcpExtTCPDSACKRecv.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKRecv gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKRecv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKRecvSegs statistic TcpExtTCPDSACKRecvSegs.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKRecvSegs gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKRecvSegs{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDSACKUndo statistic TcpExtTCPDSACKUndo.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDSACKUndo gauge
huatuo_bamai_netstat_container_TcpExt_TCPDSACKUndo{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDeferAcceptDrop statistic TcpExtTCPDeferAcceptDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDeferAcceptDrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPDeferAcceptDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDelivered statistic TcpExtTCPDelivered.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDelivered gauge
huatuo_bamai_netstat_container_TcpExt_TCPDelivered{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 3.28098e+06
# HELP huatuo_bamai_netstat_container_TcpExt_TCPDeliveredCE statistic TcpExtTCPDeliveredCE.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPDeliveredCE gauge
huatuo_bamai_netstat_container_TcpExt_TCPDeliveredCE{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenActive statistic TcpExtTCPFastOpenActive.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenActive gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenActive{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenActiveFail statistic TcpExtTCPFastOpenActiveFail.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenActiveFail gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenActiveFail{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenBlackhole statistic TcpExtTCPFastOpenBlackhole.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenBlackhole gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenBlackhole{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenCookieReqd statistic TcpExtTCPFastOpenCookieReqd.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenCookieReqd gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenCookieReqd{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenListenOverflow statistic TcpExtTCPFastOpenListenOverflow.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenListenOverflow gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenListenOverflow{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassive statistic TcpExtTCPFastOpenPassive.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassive gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassive{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassiveAltKey statistic TcpExtTCPFastOpenPassiveAltKey.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassiveAltKey gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassiveAltKey{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassiveFail statistic TcpExtTCPFastOpenPassiveFail.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassiveFail gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastOpenPassiveFail{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFastRetrans statistic TcpExtTCPFastRetrans.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFastRetrans gauge
huatuo_bamai_netstat_container_TcpExt_TCPFastRetrans{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFromZeroWindowAdv statistic TcpExtTCPFromZeroWindowAdv.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFromZeroWindowAdv gauge
huatuo_bamai_netstat_container_TcpExt_TCPFromZeroWindowAdv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPFullUndo statistic TcpExtTCPFullUndo.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPFullUndo gauge
huatuo_bamai_netstat_container_TcpExt_TCPFullUndo{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPHPAcks statistic TcpExtTCPHPAcks.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPHPAcks gauge
huatuo_bamai_netstat_container_TcpExt_TCPHPAcks{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 616667
# HELP huatuo_bamai_netstat_container_TcpExt_TCPHPHits statistic TcpExtTCPHPHits.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPHPHits gauge
huatuo_bamai_netstat_container_TcpExt_TCPHPHits{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 9913
# HELP huatuo_bamai_netstat_container_TcpExt_TCPHystartDelayCwnd statistic TcpExtTCPHystartDelayCwnd.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPHystartDelayCwnd gauge
huatuo_bamai_netstat_container_TcpExt_TCPHystartDelayCwnd{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPHystartDelayDetect statistic TcpExtTCPHystartDelayDetect.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPHystartDelayDetect gauge
huatuo_bamai_netstat_container_TcpExt_TCPHystartDelayDetect{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPHystartTrainCwnd statistic TcpExtTCPHystartTrainCwnd.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPHystartTrainCwnd gauge
huatuo_bamai_netstat_container_TcpExt_TCPHystartTrainCwnd{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPHystartTrainDetect statistic TcpExtTCPHystartTrainDetect.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPHystartTrainDetect gauge
huatuo_bamai_netstat_container_TcpExt_TCPHystartTrainDetect{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPKeepAlive statistic TcpExtTCPKeepAlive.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPKeepAlive gauge
huatuo_bamai_netstat_container_TcpExt_TCPKeepAlive{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 20
# HELP huatuo_bamai_netstat_container_TcpExt_TCPLossFailures statistic TcpExtTCPLossFailures.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPLossFailures gauge
huatuo_bamai_netstat_container_TcpExt_TCPLossFailures{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPLossProbeRecovery statistic TcpExtTCPLossProbeRecovery.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPLossProbeRecovery gauge
huatuo_bamai_netstat_container_TcpExt_TCPLossProbeRecovery{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPLossProbes statistic TcpExtTCPLossProbes.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPLossProbes gauge
huatuo_bamai_netstat_container_TcpExt_TCPLossProbes{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_netstat_container_TcpExt_TCPLossUndo statistic TcpExtTCPLossUndo.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPLossUndo gauge
huatuo_bamai_netstat_container_TcpExt_TCPLossUndo{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPLostRetransmit statistic TcpExtTCPLostRetransmit.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPLostRetransmit gauge
huatuo_bamai_netstat_container_TcpExt_TCPLostRetransmit{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMD5Failure statistic TcpExtTCPMD5Failure.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMD5Failure gauge
huatuo_bamai_netstat_container_TcpExt_TCPMD5Failure{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMD5NotFound statistic TcpExtTCPMD5NotFound.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMD5NotFound gauge
huatuo_bamai_netstat_container_TcpExt_TCPMD5NotFound{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMD5Unexpected statistic TcpExtTCPMD5Unexpected.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMD5Unexpected gauge
huatuo_bamai_netstat_container_TcpExt_TCPMD5Unexpected{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMTUPFail statistic TcpExtTCPMTUPFail.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMTUPFail gauge
huatuo_bamai_netstat_container_TcpExt_TCPMTUPFail{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMTUPSuccess statistic TcpExtTCPMTUPSuccess.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMTUPSuccess gauge
huatuo_bamai_netstat_container_TcpExt_TCPMTUPSuccess{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMemoryPressures statistic TcpExtTCPMemoryPressures.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMemoryPressures gauge
huatuo_bamai_netstat_container_TcpExt_TCPMemoryPressures{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMemoryPressuresChrono statistic TcpExtTCPMemoryPressuresChrono.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMemoryPressuresChrono gauge
huatuo_bamai_netstat_container_TcpExt_TCPMemoryPressuresChrono{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMigrateReqFailure statistic TcpExtTCPMigrateReqFailure.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMigrateReqFailure gauge
huatuo_bamai_netstat_container_TcpExt_TCPMigrateReqFailure{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMigrateReqSuccess statistic TcpExtTCPMigrateReqSuccess.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMigrateReqSuccess gauge
huatuo_bamai_netstat_container_TcpExt_TCPMigrateReqSuccess{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPMinTTLDrop statistic TcpExtTCPMinTTLDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPMinTTLDrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPMinTTLDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPOFODrop statistic TcpExtTCPOFODrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPOFODrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPOFODrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPOFOMerge statistic TcpExtTCPOFOMerge.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPOFOMerge gauge
huatuo_bamai_netstat_container_TcpExt_TCPOFOMerge{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPOFOQueue statistic TcpExtTCPOFOQueue.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPOFOQueue gauge
huatuo_bamai_netstat_container_TcpExt_TCPOFOQueue{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPOrigDataSent statistic TcpExtTCPOrigDataSent.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPOrigDataSent gauge
huatuo_bamai_netstat_container_TcpExt_TCPOrigDataSent{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 2.675557e+06
# HELP huatuo_bamai_netstat_container_TcpExt_TCPPLBRehash statistic TcpExtTCPPLBRehash.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPPLBRehash gauge
huatuo_bamai_netstat_container_TcpExt_TCPPLBRehash{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPPartialUndo statistic TcpExtTCPPartialUndo.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPPartialUndo gauge
huatuo_bamai_netstat_container_TcpExt_TCPPartialUndo{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPPureAcks statistic TcpExtTCPPureAcks.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPPureAcks gauge
huatuo_bamai_netstat_container_TcpExt_TCPPureAcks{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 2.095262e+06
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRcvCoalesce statistic TcpExtTCPRcvCoalesce.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRcvCoalesce gauge
huatuo_bamai_netstat_container_TcpExt_TCPRcvCoalesce{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 3
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRcvCollapsed statistic TcpExtTCPRcvCollapsed.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRcvCollapsed gauge
huatuo_bamai_netstat_container_TcpExt_TCPRcvCollapsed{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRcvQDrop statistic TcpExtTCPRcvQDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRcvQDrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPRcvQDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRenoFailures statistic TcpExtTCPRenoFailures.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRenoFailures gauge
huatuo_bamai_netstat_container_TcpExt_TCPRenoFailures{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRenoRecovery statistic TcpExtTCPRenoRecovery.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRenoRecovery gauge
huatuo_bamai_netstat_container_TcpExt_TCPRenoRecovery{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRenoRecoveryFail statistic TcpExtTCPRenoRecoveryFail.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRenoRecoveryFail gauge
huatuo_bamai_netstat_container_TcpExt_TCPRenoRecoveryFail{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRenoReorder statistic TcpExtTCPRenoReorder.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRenoReorder gauge
huatuo_bamai_netstat_container_TcpExt_TCPRenoReorder{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPReqQFullDoCookies statistic TcpExtTCPReqQFullDoCookies.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPReqQFullDoCookies gauge
huatuo_bamai_netstat_container_TcpExt_TCPReqQFullDoCookies{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPReqQFullDrop statistic TcpExtTCPReqQFullDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPReqQFullDrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPReqQFullDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPRetransFail statistic TcpExtTCPRetransFail.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPRetransFail gauge
huatuo_bamai_netstat_container_TcpExt_TCPRetransFail{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSACKDiscard statistic TcpExtTCPSACKDiscard.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSACKDiscard gauge
huatuo_bamai_netstat_container_TcpExt_TCPSACKDiscard{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSACKReneging statistic TcpExtTCPSACKReneging.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSACKReneging gauge
huatuo_bamai_netstat_container_TcpExt_TCPSACKReneging{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSACKReorder statistic TcpExtTCPSACKReorder.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSACKReorder gauge
huatuo_bamai_netstat_container_TcpExt_TCPSACKReorder{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSYNChallenge statistic TcpExtTCPSYNChallenge.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSYNChallenge gauge
huatuo_bamai_netstat_container_TcpExt_TCPSYNChallenge{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSackFailures statistic TcpExtTCPSackFailures.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSackFailures gauge
huatuo_bamai_netstat_container_TcpExt_TCPSackFailures{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSackMerged statistic TcpExtTCPSackMerged.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSackMerged gauge
huatuo_bamai_netstat_container_TcpExt_TCPSackMerged{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSackRecovery statistic TcpExtTCPSackRecovery.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSackRecovery gauge
huatuo_bamai_netstat_container_TcpExt_TCPSackRecovery{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSackRecoveryFail statistic TcpExtTCPSackRecoveryFail.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSackRecoveryFail gauge
huatuo_bamai_netstat_container_TcpExt_TCPSackRecoveryFail{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSackShiftFallback statistic TcpExtTCPSackShiftFallback.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSackShiftFallback gauge
huatuo_bamai_netstat_container_TcpExt_TCPSackShiftFallback{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSackShifted statistic TcpExtTCPSackShifted.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSackShifted gauge
huatuo_bamai_netstat_container_TcpExt_TCPSackShifted{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSlowStartRetrans statistic TcpExtTCPSlowStartRetrans.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSlowStartRetrans gauge
huatuo_bamai_netstat_container_TcpExt_TCPSlowStartRetrans{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSpuriousRTOs statistic TcpExtTCPSpuriousRTOs.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSpuriousRTOs gauge
huatuo_bamai_netstat_container_TcpExt_TCPSpuriousRTOs{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSpuriousRtxHostQueues statistic TcpExtTCPSpuriousRtxHostQueues.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSpuriousRtxHostQueues gauge
huatuo_bamai_netstat_container_TcpExt_TCPSpuriousRtxHostQueues{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPSynRetrans statistic TcpExtTCPSynRetrans.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPSynRetrans gauge
huatuo_bamai_netstat_container_TcpExt_TCPSynRetrans{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPTSReorder statistic TcpExtTCPTSReorder.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPTSReorder gauge
huatuo_bamai_netstat_container_TcpExt_TCPTSReorder{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPTimeWaitOverflow statistic TcpExtTCPTimeWaitOverflow.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPTimeWaitOverflow gauge
huatuo_bamai_netstat_container_TcpExt_TCPTimeWaitOverflow{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPTimeouts statistic TcpExtTCPTimeouts.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPTimeouts gauge
huatuo_bamai_netstat_container_TcpExt_TCPTimeouts{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPToZeroWindowAdv statistic TcpExtTCPToZeroWindowAdv.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPToZeroWindowAdv gauge
huatuo_bamai_netstat_container_TcpExt_TCPToZeroWindowAdv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPWantZeroWindowAdv statistic TcpExtTCPWantZeroWindowAdv.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPWantZeroWindowAdv gauge
huatuo_bamai_netstat_container_TcpExt_TCPWantZeroWindowAdv{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPWinProbe statistic TcpExtTCPWinProbe.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPWinProbe gauge
huatuo_bamai_netstat_container_TcpExt_TCPWinProbe{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPWqueueTooBig statistic TcpExtTCPWqueueTooBig.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPWqueueTooBig gauge
huatuo_bamai_netstat_container_TcpExt_TCPWqueueTooBig{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TCPZeroWindowDrop statistic TcpExtTCPZeroWindowDrop.
# TYPE huatuo_bamai_netstat_container_TcpExt_TCPZeroWindowDrop gauge
huatuo_bamai_netstat_container_TcpExt_TCPZeroWindowDrop{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TW statistic TcpExtTW.
# TYPE huatuo_bamai_netstat_container_TcpExt_TW gauge
huatuo_bamai_netstat_container_TcpExt_TW{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 720624
# HELP huatuo_bamai_netstat_container_TcpExt_TWKilled statistic TcpExtTWKilled.
# TYPE huatuo_bamai_netstat_container_TcpExt_TWKilled gauge
huatuo_bamai_netstat_container_TcpExt_TWKilled{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TWRecycled statistic TcpExtTWRecycled.
# TYPE huatuo_bamai_netstat_container_TcpExt_TWRecycled gauge
huatuo_bamai_netstat_container_TcpExt_TWRecycled{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 2461
# HELP huatuo_bamai_netstat_container_TcpExt_TcpDuplicateDataRehash statistic TcpExtTcpDuplicateDataRehash.
# TYPE huatuo_bamai_netstat_container_TcpExt_TcpDuplicateDataRehash gauge
huatuo_bamai_netstat_container_TcpExt_TcpDuplicateDataRehash{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_netstat_container_TcpExt_TcpTimeoutRehash statistic TcpExtTcpTimeoutRehash.
# TYPE huatuo_bamai_netstat_container_TcpExt_TcpTimeoutRehash gauge
huatuo_bamai_netstat_container_TcpExt_TcpTimeoutRehash{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
netstat_TcpExt_ArpFilter 因 ARP 过滤规则而被丢弃的数据包数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_BusyPollRxPackets 通过 busy polling 机制接收到的数据包数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_DelayedACKLocked 由于用户态进程锁住了 socket,而无法发送 delayed ACK 的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_DelayedACKLost 延迟 ACK 丢失导致重传的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_DelayedACKs 尝试发送 delayed ACK 的次数,包括未成功发送的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_EmbryonicRsts 在 SYN_RECV 状态收到带 RST/SYN 标记的包个数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_ListenDrops 因全连接队列满丢弃的连接总数(含ListenOverflows) 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_ListenOverflows 表示在 TCP 监听队列中发生的溢出次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_OfoPruned 乱序队列因内存不足被修剪的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_OutOfWindowIcmps 收到的与当前 TCP 窗口无关的 ICMP 错误报文数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_PruneCalled 因内存不足触发缓存清理的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_RcvPruned 接收队列因内存不足被修剪(丢弃数据包)的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_SyncookiesFailed 验证失败的 SYN cookie 数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_SyncookiesRecv 表示接收的 SYN cookie 的数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_SyncookiesSent 表示发送的 SYN cookie 的数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPACKSkippedChallenge 在处理 Challenge ACK 过程中跳过的其他 ACK 数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPACKSkippedFinWait2 在 FIN-WAIT-2 状态下跳过的 ACK 数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPACKSkippedPAWS 因 PAWS 检查失败而跳过的 ACK 数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPACKSkippedSeq 因为序列号检查而跳过的 ACK 数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPACKSkippedTimeWait 在 TIME-WAIT 状态下跳过的 ACK 数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPAbortOnClose 用户态程序在缓冲区内还有数据时关闭连接的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPAbortOnData 收到未知数据导致被关闭的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPAbortOnLinger 在LINGER状态下等待超时后中止连接的数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPAbortOnMemory 因内存问题关闭连接的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPAbortOnTimeout 因各种计时器的重传次数超过上限而关闭连接的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPLossFailures 丢失数据包而进行恢复失败的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPLossProbeRecovery 检测到丢失的数据包恢复的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPLossProbes TCP 检测到丢失的数据包数量,通常用于检测网络拥塞或丢包 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPLossUndo 在恢复过程中检测到丢失而撤销的次数 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region
netstat_TcpExt_TCPLostRetransmit 丢包重传的数量 计数 宿主,容器 container_host, container_hostnamespace, container_level, container_name, container_type, host, region

备注:TcpExt 扩展指标非常多,可按需参考官方文档。

Ref:

Socket

# HELP huatuo_bamai_sockstat_container_FRAG_inuse Number of FRAG sockets in state inuse.
# TYPE huatuo_bamai_sockstat_container_FRAG_inuse gauge
huatuo_bamai_sockstat_container_FRAG_inuse{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_sockstat_container_FRAG_memory Number of FRAG sockets in state memory.
# TYPE huatuo_bamai_sockstat_container_FRAG_memory gauge
huatuo_bamai_sockstat_container_FRAG_memory{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_sockstat_container_RAW_inuse Number of RAW sockets in state inuse.
# TYPE huatuo_bamai_sockstat_container_RAW_inuse gauge
huatuo_bamai_sockstat_container_RAW_inuse{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_sockstat_container_TCP_alloc Number of TCP sockets in state alloc.
# TYPE huatuo_bamai_sockstat_container_TCP_alloc gauge
huatuo_bamai_sockstat_container_TCP_alloc{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 171
# HELP huatuo_bamai_sockstat_container_TCP_inuse Number of TCP sockets in state inuse.
# TYPE huatuo_bamai_sockstat_container_TCP_inuse gauge
huatuo_bamai_sockstat_container_TCP_inuse{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 1
# HELP huatuo_bamai_sockstat_container_TCP_orphan Number of TCP sockets in state orphan.
# TYPE huatuo_bamai_sockstat_container_TCP_orphan gauge
huatuo_bamai_sockstat_container_TCP_orphan{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_sockstat_container_TCP_tw Number of TCP sockets in state tw.
# TYPE huatuo_bamai_sockstat_container_TCP_tw gauge
huatuo_bamai_sockstat_container_TCP_tw{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 75
# HELP huatuo_bamai_sockstat_container_UDPLITE_inuse Number of UDPLITE sockets in state inuse.
# TYPE huatuo_bamai_sockstat_container_UDPLITE_inuse gauge
huatuo_bamai_sockstat_container_UDPLITE_inuse{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_sockstat_container_UDP_inuse Number of UDP sockets in state inuse.
# TYPE huatuo_bamai_sockstat_container_UDP_inuse gauge
huatuo_bamai_sockstat_container_UDP_inuse{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 0
# HELP huatuo_bamai_sockstat_container_sockets_used Number of IPv4 sockets in use.
# TYPE huatuo_bamai_sockstat_container_sockets_used gauge
huatuo_bamai_sockstat_container_sockets_used{container_host="coredns-855c4dd65d-8v5kg",container_hostnamespace="kube-system",container_level="burstable",container_name="coredns",container_type="normal",host="hostname",region="dev"} 7
# HELP huatuo_bamai_sockstat_sockets_used Number of IPv4 sockets in use.
# TYPE huatuo_bamai_sockstat_sockets_used gauge
huatuo_bamai_sockstat_sockets_used{host="hostname",region="dev"} 409
指标 意义 单位 对象 标签
sockstat_sockets_used 系统层面当前正在使用的 socket 描述符总数 计数 系统
sockstat_TCP_inuse 当前处于 TCP 连接状态(如 ESTABLISHED、LISTEN 等,除 TIME_WAIT 外)的 socket 数量 计数 宿主,容器
sockstat_TCP_orphan 通常表示应用已关闭但 TCP 连接仍未结束 计数 宿主,容器
sockstat_TCP_tw 当前处于 TIME_WAIT 状态的 TCP socket 数量 计数 宿主,容器
sockstat_TCP_alloc 当前已分配的 TCP socket 对象总数 计数 宿主,容器
sockstat_TCP_mem TCP 套接字当前占用的内核内存页数 内存页 系统
sockstat_UDP_inuse 当前已绑定了本地端口的 UDP socket 数量 计数 宿主,容器

IO

iolatency 用来统计磁盘 I/O 延迟分布。可以把它理解成“把一次磁盘请求拆成几个阶段,再分别看每个阶段耗时多久”。

  • q2c:从请求进入队列到完成,反映整个 I/O 生命周期延迟
  • d2c:从驱动层下发到完成,更接近磁盘和驱动本身的耗时
  • freeze:磁盘冻结事件次数

队列

这些指标都会自动带上公共标签 host 和 region。其中容器维度指标还会固定带上 container_host、container_name、container_type、container_level、container_hostnamespace 标签。

# HELP huatuo_bamai_iolatency_blkdisk_d2c the disk d2c latency
# TYPE huatuo_bamai_iolatency_blkdisk_d2c gauge
huatuo_bamai_iolatency_blkdisk_d2c{disk="253:1",host="hostname",region="dev",zone="0"} 3
# HELP huatuo_bamai_iolatency_blkdisk_q2c the disk q2c latency
# TYPE huatuo_bamai_iolatency_blkdisk_q2c gauge
huatuo_bamai_iolatency_blkdisk_q2c{disk="253:1",host="hostname",region="dev",zone="0"} 3
# HELP huatuo_bamai_iolatency_container_blkdisk_d2c container blkio d2c latency
# TYPE huatuo_bamai_iolatency_container_blkdisk_d2c gauge
huatuo_bamai_iolatency_container_blkdisk_d2c{container_host="etcd-hostname",container_hostnamespace="kube-system",container_level="burstable",container_name="etcd",container_type="normal",disk="253:1",host="hostname",region="dev",zone="5"} 2
# HELP huatuo_bamai_iolatency_container_blkdisk_q2c container blkio q2c latency
# TYPE huatuo_bamai_iolatency_container_blkdisk_q2c gauge
huatuo_bamai_iolatency_container_blkdisk_q2c{container_host="etcd-hostname",container_hostnamespace="kube-system",container_level="burstable",container_name="etcd",container_type="normal",disk="253:1",host="hostname",region="dev",zone="5"} 2
指标 意义 单位 对象 标签
iolatency_blkdisk_q2c 宿主机磁盘整体 I/O 生命周期延迟统计,从入队到完成。分桶为:zone0 20-30ms,zone1 30-50ms,zone2 50-100ms,zone3 100-200ms,zone4 200-400ms,zone5 400ms+ 计数 宿主 host, region, disk, zone
iolatency_blkdisk_d2c 宿主机磁盘驱动到完成阶段的延迟统计,更接近设备处理耗时。分桶为:zone0 20-30ms,zone1 30-50ms,zone2 50-100ms,zone3 100-200ms,zone4 200-400ms,zone5 400ms+ 计数 宿主 host, region, disk, zone
iolatency_container_blkdisk_q2c 容器触发的整体 I/O 生命周期延迟统计,从入队到完成。分桶为:zone0 20-30ms,zone1 30-50ms,zone2 50-100ms,zone3 100-200ms,zone4 200-400ms,zone5 400ms+ 计数 容器 host, region, container_host, container_name, container_type, container_level, container_hostnamespace, zone
iolatency_container_blkdisk_d2c 容器触发的驱动到完成阶段延迟统计。分桶为:zone0 20-30ms,zone1 30-50ms,zone2 50-100ms,zone3 100-200ms,zone4 200-400ms,zone5 400ms+ 计数 容器 host, region, container_host, container_name, container_type, container_level, container_hostnamespace, zone

硬件

# HELP huatuo_bamai_iolatency_blkdisk_freeze the disk freeze event count
# TYPE huatuo_bamai_iolatency_blkdisk_freeze gauge
huatuo_bamai_iolatency_blkdisk_freeze{disk="253:1",host="hostname",region="dev"} 0
指标 意义 单位 对象 标签
iolatency_blkdisk_freeze 宿主机磁盘 freeze 事件次数 计数 宿主 host, region, disk

磁盘 IO 统计

diskio 通过读取 /proc/diskstats 和 /proc/stat 采集 per-device 磁盘 IO 指标和系统级 CPU iowait。与 iolatency 不同,diskio 基于 procfs 而非 eBPF,提供可通过 rate 计算延迟的累积计数器。

默认配置通过 BlackList 禁用该 collector。需要启用这些指标时,从 BlackList 中移除 diskio。

Counter 指标为累积值,需使用 Prometheus rate() 计算 per-second 值(IOPS、吞吐量)。Gauge 指标为瞬时值。平均延迟通过 PromQL 中的 I/O 时间 rate 除以请求 rate 计算。

# HELP huatuo_bamai_diskio_read_requests_total Total number of read requests completed successfully.
# TYPE huatuo_bamai_diskio_read_requests_total counter
huatuo_bamai_diskio_read_requests_total{device="sda",host="hostname",region="dev"} 1000
# HELP huatuo_bamai_diskio_write_requests_total Total number of write requests completed successfully.
# TYPE huatuo_bamai_diskio_write_requests_total counter
huatuo_bamai_diskio_write_requests_total{device="sda",host="hostname",region="dev"} 2000
# HELP huatuo_bamai_diskio_read_bytes_total Total number of bytes read from the device.
# TYPE huatuo_bamai_diskio_read_bytes_total counter
huatuo_bamai_diskio_read_bytes_total{device="sda",host="hostname",region="dev"} 2.56e+07
# HELP huatuo_bamai_diskio_written_bytes_total Total number of bytes written to the device.
# TYPE huatuo_bamai_diskio_written_bytes_total counter
huatuo_bamai_diskio_written_bytes_total{device="sda",host="hostname",region="dev"} 4.096e+07
# HELP huatuo_bamai_diskio_io_in_progress Number of I/O requests currently in flight (queue depth).
# TYPE huatuo_bamai_diskio_io_in_progress gauge
huatuo_bamai_diskio_io_in_progress{device="sda",host="hostname",region="dev"} 50
# HELP huatuo_bamai_diskio_read_time_seconds_total Total seconds spent by completed read requests.
# TYPE huatuo_bamai_diskio_read_time_seconds_total counter
huatuo_bamai_diskio_read_time_seconds_total{device="sda",host="hostname",region="dev"} 3
# HELP huatuo_bamai_diskio_write_time_seconds_total Total seconds spent by completed write requests.
# TYPE huatuo_bamai_diskio_write_time_seconds_total counter
huatuo_bamai_diskio_write_time_seconds_total{device="sda",host="hostname",region="dev"} 6
# HELP huatuo_bamai_diskio_disk_iowait_percent CPU time spent waiting for I/O during the collection interval.
# TYPE huatuo_bamai_diskio_disk_iowait_percent gauge
huatuo_bamai_diskio_disk_iowait_percent{host="hostname",region="dev"} 50
指标 意义 单位 对象 标签
read_requests_total 累积读请求完成数(field 4),使用 rate() 计算读 IOPS 计数 宿主 host, region, device
write_requests_total 累积写请求完成数(field 8),使用 rate() 计算写 IOPS 计数 宿主 host, region, device
read_bytes_total 累积读取字节数(field 6 × 512),使用 rate() 计算读吞吐量 字节 宿主 host, region, device
written_bytes_total 累积写入字节数(field 10 × 512),使用 rate() 计算写吞吐量 字节 宿主 host, region, device
read_time_seconds_total 已完成读请求的累积耗时(field 7) 秒 宿主 host, region, device
write_time_seconds_total 已完成写请求的累积耗时(field 11) 秒 宿主 host, region, device
io_in_progress 当前正在进行的 I/O 请求数,即队列深度(field 12) 计数 宿主 host, region, device
disk_iowait_percent 采集区间内 CPU 等待 I/O 完成的时间占比 百分比 宿主 host, region

XFS 文件系统统计

xfs 从 sysfs 采集每设备统计,指标前缀为 huatuo_bamai_xfs_,标签为 device、host、region。log_free_bytes 为 Gauge,_total 为累积 Counter。

指标后缀 意义 单位
log_free_bytes log 剩余可用空间 字节
log_space_sleep_total 等待 log 空间的累计次数 计数
alloc_blocks_total 累计分配 block 数 块
alloc_extents_total 累计分配 extent 数 计数
inode_attempts_total inode cache 累计请求数 计数
inode_missed_total inode cache 累计 miss 数 计数
buf_busy_locked_total buffer busy-locked 累计次数 计数
buf_locked_waited_total 等待 buffer 锁的累计次数 计数

无法获取 log 可用空间时不输出 log_free_bytes。新增 XFS 挂载后需重启 daemon。

通用系统

Soft Lockup

# HELP huatuo_bamai_softlockup_total softlockup counter
# TYPE huatuo_bamai_softlockup_total counter
huatuo_bamai_softlockup_total{host="hostname",region="dev"} 0
指标 意义 单位 对象 取值 标签
softlockup_total 系统 softlockup 事件计数 计数 物理机 BPF

HungTask

# HELP huatuo_bamai_hungtask_total hungtask counter
# TYPE huatuo_bamai_hungtask_total counter
huatuo_bamai_hungtask_total{host="hostname",region="dev"} 0
指标 意义 单位 对象 取值 标签
hungtask_total 系统 hungtask 事件计数 计数 物理机 BPF

GPU

当前版本支持的 GPU 平台:

  • MetaX
指标 描述 单位 统计纬度 指标来源
metax_gpu_sdk_info GPU SDK 信息 - version sml.GetSDKVersion
metax_gpu_driver_info GPU 驱动信息 - version sml.GetGPUVersion with driver unit
metax_gpu_info GPU 基本信息 - gpu
metax_gpu_board_power_watts GPU 板级功耗 瓦特(W) gpu sml.ListGPUBoardWayElectricInfos
metax_gpu_pcie_link_speed_gt_per_second GPU PCIe 当前链路速率 GT/s gpu sml.GetGPUPcieLinkInfo
metax_gpu_pcie_link_width_lanes GPU PCIe 当前链路宽度 链路宽度(通道数) gpu sml.GetGPUPcieLinkInfo
metax_gpu_pcie_receive_bytes_per_second GPU PCIe 接收吞吐率 Bps gpu sml.GetGPUPcieThroughputInfo
metax_gpu_pcie_transmit_bytes_per_second GPU PCIe 发送吞吐率 Bps gpu sml.GetGPUPcieThroughputInfo
metax_gpu_metaxlink_link_speed_gt_per_second GPU MetaXLink 当前链路速率 GT/s gpu, metaxlink sml.ListGPUMetaXLinkLinkInfos
metax_gpu_metaxlink_link_width_lanes GPU MetaXLink 当前链路宽度 链路宽度(通道数) gpu, metaxlink sml.ListGPUMetaXLinkLinkInfos
metax_gpu_metaxlink_receive_bytes_per_second GPU MetaXLink 接收吞吐率 Bps gpu, metaxlink sml.ListGPUMetaXLinkThroughputInfos
metax_gpu_metaxlink_transmit_bytes_per_second GPU MetaXLink 发送吞吐率 Bps gpu, metaxlink sml.ListGPUMetaXLinkThroughputInfos
metax_gpu_metaxlink_receive_bytes_total GPU MetaXLink 接收数据总量 字节 gpu, metaxlink sml.ListGPUMetaXLinkTrafficStatInfos
metax_gpu_metaxlink_transmit_bytes_total GPU MetaXLink 发送数据总量 字节 gpu, metaxlink sml.ListGPUMetaXLinkTrafficStatInfos
metax_gpu_metaxlink_aer_errors_total GPU MetaXLink AER 错误次数 计数 gpu, metaxlink, error_type sml.ListGPUMetaXLinkAerErrorsInfos
metax_gpu_status GPU 状态 - gpu, die sml.GetDieStatus
metax_gpu_temperature_celsius GPU 温度 摄氏度 gpu, die sml.GetDieTemperature
metax_gpu_utilization_percent GPU 利用率(0–100) % gpu, die, ip sml.GetDieUtilization
metax_gpu_memory_total_bytes 显存总容量 字节 gpu, die sml.GetDieMemoryInfo
metax_gpu_memory_used_bytes 已使用显存容量 字节 gpu, die sml.GetDieMemoryInfo
metax_gpu_clock_mhz GPU 时钟频率 兆赫兹(MHz) gpu, die, ip sml.ListDieClocks
metax_gpu_clocks_throttling GPU 时钟降频原因 - gpu, die, reason sml.GetDieClocksThrottleStatus
metax_gpu_dpm_performance_level GPU DPM 性能等级 - gpu, die, ip sml.GetDieDPMPerformanceLevel
metax_gpu_ecc_memory_errors_total GPU ECC 内存错误次数 计数 gpu, die, memory_type, error_type sml.GetDieECCMemoryInfo
metax_gpu_ecc_memory_retired_pages_total GPU ECC 内存退役页数 计数 gpu, die sml.GetDieECCMemoryInfo
  • 摩尔线程 (MThreads)

摩尔线程 GPU 监控通过 MTML(摩尔线程管理库)实现。库文件在启动时通过 SONAME 搜索(libmtml.so.2 然后 libmtml.so)自动发现。静态设备信息会被缓存,仅在重新初始化时重建;动态指标在每次抓取时采集。

配置:从 BlackList 中移除 mthreads_gpu 以启用。指标组由 [MetricCollector.Mthreads] 开关控制(EnableHealth、EnablePCIe、EnableMTLink)。

指标 描述 单位 标签 来源
huatuo_bamai_mthreads_gpu_library_info MTML 库信息 - version mtml.LibraryVersion
huatuo_bamai_mthreads_gpu_device_info GPU 信息 - gpu, name, brand, serial mtml.Device.Name/Brand/SerialNumber
huatuo_bamai_mthreads_gpu_device_pci_info GPU PCI 信息 - gpu, sbdf mtml.Device.PciSbdf
huatuo_bamai_mthreads_gpu_device_spec GPU 规格 - gpu, cores mtml.Device.GpuCores
huatuo_bamai_mthreads_gpu_device_bios_version GPU BIOS 版本 - gpu, bios mtml.Device.BiosVersion
huatuo_bamai_mthreads_gpu_device_musa_capability GPU MUSA 计算能力 - gpu, musa_capability mtml.Device.MusaComputeCapability
huatuo_bamai_mthreads_gpu_memory_info GPU 内存信息 - gpu, type, vendor, bus_width mtml.Device.InitMemory
huatuo_bamai_mthreads_gpu_pcie_link_max_speed_gt_per_sec GPU PCIe 最大链路速率(硬件能力) GT/s gpu mtml.Device.PciInfo
huatuo_bamai_mthreads_gpu_pcie_link_max_width_lanes GPU PCIe 最大链路宽度(硬件能力) lanes gpu mtml.Device.PciInfo
huatuo_bamai_mthreads_gpu_device_power_watts GPU 设备功耗 W gpu mtml.Device.PowerUsage
huatuo_bamai_mthreads_gpu_gpu_temperature_celsius GPU 温度 °C gpu mtml.Gpu.Temperature
huatuo_bamai_mthreads_gpu_memory_temperature_celsius GPU 内存温度 °C gpu mtml.Memory.Temperature
huatuo_bamai_mthreads_gpu_gpu_utilization_percent GPU 利用率(0-100) % gpu mtml.Gpu.Utilization
huatuo_bamai_mthreads_gpu_memory_utilization_percent GPU 内存利用率(0-100) % gpu mtml.Memory.Utilization
huatuo_bamai_mthreads_gpu_memory_total_bytes GPU 总内存 bytes gpu mtml.Memory.Total
huatuo_bamai_mthreads_gpu_memory_used_bytes GPU 已使用内存 bytes gpu mtml.Memory.Used
huatuo_bamai_mthreads_gpu_gpu_clock_mhz GPU 时钟频率 MHz gpu mtml.Gpu.Clock
huatuo_bamai_mthreads_gpu_gpu_max_clock_mhz GPU 最大时钟频率 MHz gpu mtml.Gpu.MaxClock
huatuo_bamai_mthreads_gpu_memory_clock_mhz GPU 内存时钟频率 MHz gpu mtml.Memory.Clock
huatuo_bamai_mthreads_gpu_memory_max_clock_mhz GPU 内存最大时钟频率 MHz gpu mtml.Memory.MaxClock
huatuo_bamai_mthreads_gpu_gpu_voltage_volts GPU 电压 V gpu mtml.Gpu.Voltage
huatuo_bamai_mthreads_gpu_gpu_power_limit_watts GPU 强制功耗限制 W gpu mtml.Gpu.EnforcedPowerLimit
huatuo_bamai_mthreads_gpu_gpu_power_default_limit_watts GPU 默认功耗管理限制 W gpu mtml.Gpu.PowerManagementDefaultLimit
huatuo_bamai_mthreads_gpu_fan_rpm GPU 风扇转速 RPM gpu, fan mtml.Device.FanRpm
huatuo_bamai_mthreads_gpu_fan_speed_percent GPU 风扇速度百分比 % gpu, fan mtml.Device.FanSpeed
huatuo_bamai_mthreads_gpu_gpu_pstate GPU 性能状态(0=P0) - gpu mtml.Device.PerformanceState
huatuo_bamai_mthreads_gpu_vpu_utilization_percent GPU VPU 利用率(0-100) % gpu mtml.Vpu.Utilization
huatuo_bamai_mthreads_gpu_vpu_encoder_utilization_percent GPU VPU 编码器利用率(0-100) % gpu mtml.Vpu.Utilization
huatuo_bamai_mthreads_gpu_vpu_decoder_utilization_percent GPU VPU 解码器利用率(0-100) % gpu mtml.Vpu.Utilization
huatuo_bamai_mthreads_gpu_vpu_clock_mhz GPU VPU 时钟频率 MHz gpu mtml.Vpu.Clock
huatuo_bamai_mthreads_gpu_pcie_link_speed_gt_per_sec GPU PCIe 当前链路速率 GT/s gpu mtml.Device.PciInfo
huatuo_bamai_mthreads_gpu_pcie_link_width_lanes GPU PCIe 当前链路宽度 lanes gpu mtml.Device.PciInfo
huatuo_bamai_mthreads_gpu_pcie_replay_total GPU PCIe 重放计数器 count gpu mtml.Device.PcieReplayCounter
huatuo_bamai_mthreads_gpu_mtlink_state GPU MtLink 状态(0=DOWN,1=UP,2=DOWNGRADE) - gpu, link mtml.Device.MtLinkState
huatuo_bamai_mthreads_gpu_mtlink_link_bandwidth_gb_s GPU MtLink 每链路最大带宽(设备静态规格,非实时吞吐) GB/s gpu mtml.Device.MtLinkSpec
huatuo_bamai_mthreads_gpu_mtlink_link_count GPU MtLink 最大支持链路数(设备静态规格) links gpu mtml.Device.MtLinkSpec

5.2 - 异常事件诊断

📖 概述

HUATUO 基于 eBPF 技术,对 Linux 内核中的 CPU 调度、内存子系统、网络协议栈、硬件错误等核心子系统实施实时异常事件观测。当内核触发 softlockup、OOM、硬件 MCE 等异常状态时,eBPF 程序通过挂钩(hook)内核函数(kprobe)或内核 tracepoint,在事件发生的第一时间采集进程信息、内核调用栈、网络上下文等现场数据,并经由 perf event 环形缓冲区传递至用户态处理程序,最终持久化至 Elasticsearch 或本地磁盘文件。

相比传统的基于内核日志(dmesg/syslog)采集方案,eBPF 事件观测具备更低的数据丢失风险——不会因内核日志缓冲区满溢而丢失关键事件;同时可捕获不会写入内核日志的短暂性异常(如调度 tick 间隔过长);并提供容器级别的事件关联信息,满足云原生场景下的精准定位需求。

当前支持 12 类事件的持续观测,覆盖 CPU 调度健康状态(sched_tick、softlockup、hungtask)、内存压力(memory_oom_kill、memory_reclaim_events)、网络协议栈(dropwatch、tcp_retransmit、net_rx_latency、netdev_events、netdev_bonding_lacp、netdev_txqueue_timeout)以及硬件可靠性(ras)等方面。

🎯 场景

Kubernetes 容器内存故障定位:在容器频繁 OOM 重启场景下,memory_oom_kill 事件同时记录被 OOM Killer 终止的进程(victim)与触发 OOM 的进程(trigger)的 memcg cgroup 指针及容器 ID,结合时序数据可快速定位内存资源争抢的根因容器,降低人工排查容器日志的时间成本。

AI 训练集群硬件故障感知:在 GPU 训练服务器上,ras 事件持续采集 MCE(Machine Check Exception)、EDAC 内存控制器错误和 PCIe AER(Advanced Error Reporting)错误,对错误进行严重程度分级(Corrected / UncorrectedRecoverable / UncorrectedFatal),在训练任务中断前提前感知硬件老化或单点故障,减少因硬件故障导致的训练任务损失。

网络性能毛刺分析:dropwatch 观测内核网络协议栈丢包行为,tcp_retransmit 观测 TCP 重传活动,net_rx_latency 检测单个数据包从网卡驱动到用户态的完整接收路径延迟,按阶段(网卡到内核、内核到 TCP、TCP 到用户态)分别设置阈值(默认 5ms / 10ms / 115ms),精准定位造成业务超时的网络层位置,提升网络问题根因定位效率。

主机调度健康观测:sched_tick(调度 tick 间隔,默认阈值 10ms)、softlockup(CPU 无法调度,约 1 秒)、hungtask(D 状态进程任务挂起)三类事件联合覆盖 CPU 调度路径的异常状态,当系统出现卡顿、响应超时等现象时,自动保留内核调用栈等诊断信息,支持在故障消失后的离线分析。

🚀 使用

配置参数

各事件可通过以下参数进行调优,参数均提供默认值,无需配置即可运行:

参数 默认值 说明
sched_tick.interval_threshold 10000000(10ms,纳秒) 调度 tick 间隔阈值
memory_reclaim.blocked_threshold 900000000(900ms,纳秒) 直接内存回收时间触发阈值
net_rx_latency.driver2net_rx 5(ms) 从网卡驱动到 __netif_receive_skb 的延迟阈值
net_rx_latency.driver2tcp 10(ms) 从网卡驱动到 tcp_v4_rcv 的延迟阈值
net_rx_latency.driver2userspace 115(ms) 从网卡驱动到用户态拷贝(skb_copy_datagram_iovec)的延迟阈值
net_rx_latency.excluded_host_netnamespace true 是否过滤宿主机网络命名空间(默认仅观测容器)
net_rx_latency.excluded_container_qos [] 需要排除的容器 QoS 级别列表
dropwatch.filter tcp 在 dropwatch 事件输出前执行的 tcpdump 风格报文过滤表达式
dropwatch.max_events_per_second 100 每秒最多输出的 dropwatch 事件数;0 表示不限速
dropwatch.exclude_containers [] 预留字段;当前 dropwatch 事件链路不会应用该配置
netdev.device_list [] 需要监控链路状态的网卡设备名称列表
ras.mce_thr_backoff 1800(秒) MCE 阈值中断(THR)事件上报冷却时间,防止中断风暴
issues_list [] 已知问题抑制规则;分别匹配 net_rx_latency 事件标题和 dropwatch 内核调用栈

事件列表

事件名称(tracer_name) 探针类型 触发条件 典型场景
sched_tick kprobe 调度 tick 间隔 >= 阈值(默认 10ms) 系统卡顿、网络延迟、调度延迟
softlockup kprobe CPU 长时间无法调度(约 1 秒) 系统软锁死、响应异常
hungtask kprobe D 状态进程任务挂起 瞬时批量 D 进程、IO 阻塞
memory_oom_kill kprobe OOM Killer 触发 容器/宿主机内存耗尽
memory_reclaim_events kprobe 容器进程直接回收时间 > 阈值(默认 900ms) 内存压力导致业务卡顿
ras tracepoint CPU/MEM/PCIe 硬件错误 硬件故障感知
dropwatch tracepoint 内核网络协议栈丢包 协议栈丢包导致业务毛刺
tcp_retransmit tracepoint;TLP 使用可选 kprobe TCP 重传或 Tail Loss Probe TCP 丢包、乱序、拥塞及延迟诊断
net_rx_latency kprobe 协议栈接收延迟超分段阈值 接收延迟引起业务超时
netdev_events netlink 网卡链路状态变化 网卡物理链路故障
netdev_bonding_lacp kprobe LACP 协议状态变化(仅 802.3ad 模式环境) 物理机与交换机故障边界界定
netdev_txqueue_timeout kprobe 网卡发送队列超时 网卡发送队列硬件故障

tcp_retransmit 的使用方式、字段、分类和丢包关联请参考 tcpshark 文档。

通用字段说明

所有事件数据均包含以下通用字段:

  • hostname:物理机 hostname
  • region:物理机所在可用区
  • uploaded_timestamp:数据上传时间
  • container_id:如果事件关联容器,则记录的容器 ID
  • container_hostname:如果事件关联容器,则记录的容器 hostname
  • container_host_namespace:如果事件关联容器,则记录容器的 K8s 命名空间
  • container_type:容器类型,例如 normal 普通容器,sidecar 边车容器等
  • container_qos:容器 QoS 级别
  • tracer_name:事件名称(如 sched_tick、memory_oom_kill 等)
  • tracer_id:此次的 tracing ID
  • observed_timestamp:触发 tracing 时间
  • tracer_type:观测类型,即时事件记录固定为 event
  • tracer_data:特定事件私有数据(详见各事件说明)

1. sched_tick 调度 tick 间隔

功能描述 检测相邻调度 tick 的总间隔;间隔达到阈值时记录当前内核调用栈和进程信息。该事件可发现 IRQ 长时间关闭、CPU 停顿或虚拟化调度延迟等问题,但不能单独证明软中断被禁用。comm 和 pid 表示上报 tick 所中断的任务,不表示造成延迟的任务。

适用场景

  • 内核或驱动长时间关闭本地中断,或 hardirq/NMI 长时间占用 CPU。
  • 虚拟机 vCPU 被宿主机长时间抢占,包括高 steal time 和调度停顿。
  • SMI、固件停顿、clockevent 延迟投递或丢失等底层异常。

使用边界 成功进入 NO_HZ 的正常停 tick 会被过滤。普通 CPU 高负载或 softirq backlog 本身不等同于 tick 延迟。阈值应高于目标机器的正常 tick 周期;事件栈仅表示延迟结束后首个 tick 的现场,需要结合 IRQ、steal time 和硬件指标定位根因。

数据存储 事件数据自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "uploaded_timestamp": "2025-06-11T16:05:16.251152703+08:00",
    "hostname": "***",
    "tracer_data": {
        "tick_interval_ns": 237328905,
        "tick_interval_threshold_ns": 10000000,
        "comm": "***-agent",
        "pid": 688073,
        "cpu": 1,
        "stack": "scheduler_tick/..."
    },
    "observed_timestamp": "2025-06-11T16:05:16.251+08:00",
    "tracer_type": "event",
    "region": "***",
    "tracer_name": "sched_tick"
}

字段含义解释

  • comm:触发事件的进程名称
  • stack:延迟结束后的首个调度 tick 上采集的内核调用栈
  • tick_interval_ns:相邻调度 tick 的总间隔(纳秒)
  • cpu:发生事件的 CPU 编号
  • tick_interval_threshold_ns:调度 tick 间隔阈值(纳秒),达到该值则记录事件
  • pid:上报 tick 所中断任务的进程 ID

2. dropwatch 协议栈丢包

功能描述 检测内核网络协议栈中的丢包行为,输出丢包时的内核调用栈、网络五元组、TCP 状态等信息。可通过可选的调用栈过滤器抑制经本机验证的噪声模式。type 字段为兼容 TCP 丢包类型而预留,当前未赋值。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "observed_timestamp": "2026-07-23T02:14:40.304775546Z",
    "tracer_data": {
        "drop_reason": "SKB_DROP_REASON_NOT_SPECIFIED",
        "source": "events",
        "comm": "kubelet",
        "pid": 1687046,
        "net_namespace_cookie": 123456789,
        "net_namespace_inum": 402653184,
        "netdev_queue_mapping": 3,
        "netdev_linkstatus": ["linkStatusUp"],
        "netdev_name": "eth0",
        "netdev_ifindex": 2,
        "packet_eth_proto": "0x0800",
        "packet_len_bytes": 1460,
        "layers": {
            "label": "IPv4/TCP",
            "ipv4": {
                "saddr": "10.79.68.62",
                "daddr": "10.134.72.4",
                "protocol": "TCP"
            },
            "tcp": {
                "sport": 8080,
                "dport": 49000,
                "seq": 1009085774,
                "ack_seq": 689410995,
                "sk_state": "ESTABLISHED"
            }
        },
        "stack": "kfree_skb/..."
    }
}

字段含义解释

  • type:预留丢包类型,当前未赋值且不会出现在 JSON 中;预留编码为 1(通用丢包)、2(SYN 洪泛)、3(半连接队列溢出)、4(全连接队列溢出)
  • drop_reason:内核数据包丢弃原因
  • source:事件来源(独立运行 dropwatch 时为 tools,由 huatuo-bamai 启动时为 events)
  • comm:触发丢包的进程名称
  • pid:进程 ID
  • net_namespace_cookie / net_namespace_inum:用于容器归属解析的网络命名空间值
  • netdev_queue_mapping:网卡队列索引
  • netdev_linkstatus:网卡链路状态标志列表
  • netdev_name:网卡设备名称
  • netdev_ifindex:网卡接口索引
  • packet_len_bytes:数据包长度(字节)
  • layers.ipv4.saddr / layers.ipv4.daddr:源 IP 和目的 IP 地址
  • layers.tcp.sport / layers.tcp.dport:源端口和目的端口
  • layers.tcp.seq / layers.tcp.ack_seq:TCP 序列号和确认序列号
  • layers.tcp.sk_state:丢包时 TCP 连接状态
  • stack:丢包发生时的内核调用栈

3. net_rx_latency 协议栈延迟

功能描述 检测协议栈接收方向(网卡驱动 → 内核协议栈 → 用户态主动收包)的分段延迟事件。在接收路径上设置三个观测点,任意阶段延迟超过对应阈值(默认:网卡到内核 5ms、内核到 TCP 10ms、TCP 到用户态 115ms)时触发,记录网络五元组、TCP 序列号、延迟位置及延迟时间。支持所有 TCP 状态的报文观测,不限于 ESTABLISHED 状态,可捕获 SYN、FIN、TIME_WAIT 等非 ESTABLISHED 状态下的接收延迟事件。默认过滤宿主机网络命名空间,仅观测容器网络。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "observed_timestamp": "2025-06-11T00:00:00Z",
    "tracer_data": {
        "comm": "nginx",
        "pid": 2921092,
        "latency_stage": "RX_STAGE_USERCOPY",
        "latency_ms": 95973,
        "latency_threshold_ms": 115,
        "tcp_state": "ESTABLISHED",
        "tcp_saddr": "10.156.248.76",
        "tcp_daddr": "10.134.72.4",
        "tcp_sport": 9213,
        "tcp_dport": 49000,
        "tcp_seq": 1009085774,
        "tcp_ack_seq": 689410995,
        "net_namespace_cookie": 123456789,
        "net_namespace_inum": 402653184,
        "packet_len_bytes": 26064
    }
}

字段含义解释

  • comm:触发事件的进程名称
  • pid:触发事件的进程 ID
  • latency_stage:延迟发生的阶段(RX_STAGE_NETIF 网卡到内核 / RX_STAGE_TCPV4 内核到 TCP / RX_STAGE_USERCOPY TCP 到用户态)
  • latency_ms:实际延迟时间(毫秒)
  • latency_threshold_ms:触发事件的延迟阈值(毫秒)
  • tcp_state:TCP 连接状态(支持所有状态,如 ESTABLISHED、SYN_SENT、FIN_WAIT、TIME_WAIT 等)
  • tcp_saddr / tcp_daddr:源 IP / 目的 IP 地址
  • tcp_sport / tcp_dport:源端口 / 目的端口
  • tcp_seq / tcp_ack_seq:TCP 序列号 / 确认序列号
  • net_namespace_cookie:网络命名空间 cookie(内核 ≥ 5.14 可用,用于高效容器关联)
  • net_namespace_inum:网络命名空间 inum
  • packet_len_bytes:数据包长度(字节)

4. memory_oom_kill 内存耗尽

OOM kill 追踪器现命名为 memory_oom_kill(此前为 memory_oom,更早为 oom),需同步更新黑名单、事件过滤器和告警查询。指标前缀现为 huatuo_bamai_memory_oom_kill_,替代 huatuo_bamai_memory_oom_ 及更早的 huatuo_bamai_oom_,需同步更新指标仪表盘和记录规则。历史事件保留原名称,跨版本查询需匹配这三个名称;内置事件仪表盘已覆盖三者。

功能描述 检测宿主机或容器内发生的 OOM(Out of Memory)事件,记录被 OOM Killer 终止的进程(victim)与触发 OOM 的进程(trigger)信息,以及对应容器和 memory cgroup 的详细信息,提供完整的故障快照。同时维护宿主机和各容器的 OOM 计数指标。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "trigger_memcg_css": "0xff4b8d8be3818000",
        "trigger_container_id": "***",
        "trigger_container_hostname": "***.docker",
        "trigger_pid": 3218804,
        "trigger_process_name": "java",
        "victim_memcg_css": "0xff4b8d8be3818000",
        "victim_container_id": "***",
        "victim_container_hostname": "***.docker",
        "victim_pid": 3218745,
        "victim_process_name": "java",
        "cgroup_memory_limit": 2147483648,
        "cgroup_memory_usage": 2143289344,
        "memory_snapshot": {
            "top_processes": [
                {
                    "pid": 3218745,
                    "process_name": "java",
                    "vm_rss": 1604321280,
                    "rss_anon": 1509949440,
                    "rss_file": 83886080,
                    "rss_shmem": 0,
                    "vm_swap": 0,
                    "total": 1593835520
                }
            ],
            "host_meminfo": {
                "MemAvailable": 3355443200,
                "Cached": 1073741824,
                "Slab": 268435456
            },
            "victim_cgroup": {
                "container_id": "***",
                "cgroup_path": "kubepods.slice/...",
                "current": 2143289344,
                "max": 2147483648,
                "stat": {
                    "anon": 1509949440,
                    "file": 83886080
                },
                "events": {
                    "oom": 1,
                    "oom_kill": 1
                }
            }
        }
    }
}

字段含义解释

  • victim_process_name / victim_pid:被 OOM Killer 终止的进程名称与 PID
  • victim_container_hostname / victim_container_id:被终止进程所在的容器主机名与容器 ID
  • victim_memcg_css:被终止进程对应的 memory cgroup 指针(十六进制)
  • trigger_process_name / trigger_pid:触发 OOM 的进程名称与 PID
  • trigger_container_hostname / trigger_container_id:触发进程所在的容器主机名与容器 ID
  • trigger_memcg_css:触发进程对应的 memory cgroup 指针(十六进制)
  • cgroup_memory_limit / cgroup_memory_usage:内核事件中携带的 cgroup 内存限制与使用量
  • memory_snapshot.top_processes:OOM 现场按 RSS/swap 排序的 Top 进程,包含 RssAnon、RssFile、RssShmem、VmRSS、VmSwap
  • memory_snapshot.host_meminfo:OOM 现场关键宿主机 /proc/meminfo 字段,如 MemAvailable、Cached、Slab、swap、anon/file 活跃页等
  • memory_snapshot.trigger_cgroup / victim_cgroup:触发容器和受害容器的 cgroup 路径、current/max、memory.stat 和 memory.events

5. softlockup 软锁死

功能描述 检测系统 softlockup 事件(CPU 长时间无法被调度,约 1 秒),提供导致锁死的目标进程信息、所在 CPU 及所有 CPU 的 NMI 回溯信息。采用退避(backoff)策略,同一轮事件风暴期间上报间隔从 10 分钟递增至最长 3 小时,防止重复上报。同时维护 softlockup 发生次数的计数指标。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "cpu": 15,
        "pid": 12345,
        "comm": "kworker/15:0",
        "cpus_stack": "2025-06-10 14:30:22 sysrq: Show backtrace of all active CPUs\nNMI backtrace for cpu 15\n..."
    }
}

字段含义解释

  • cpu:发生 softlockup 的 CPU 编号
  • pid:触发 softlockup 的进程 PID
  • comm:触发 softlockup 的进程名称
  • cpus_stack:所有 CPU 的 NMI 回溯信息(多行文本,包含时间戳和调用栈)

6. hungtask 任务挂起

功能描述 检测系统 hungtask 事件,捕获当前所有处于 D 状态(不可中断睡眠)的进程内核栈及所有 CPU 的回溯信息,用于保留故障现场。采用退避策略,同一轮事件风暴期间上报间隔从 10 分钟递增至最长 3 小时。同时维护 hungtask 发生次数的计数指标。注意:部分 Linux 发行版(如 Fedora 42)默认禁用 hungtask 检测,此时该观测器不会启动。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "tid": 2567042,
        "comm": "kworker/u48:2",
        "cpus_stack": "2025-06-10 09:57:14 sysrq: Show backtrace of all active CPUs\nNMI backtrace for cpu 33\n...",
        "blocked_processes_stack": "task:java            state:D stack:    0 pid: 12345 ..."
    }
}

字段含义解释

  • tid:触发 hungtask 检测的任务 TID
  • comm:触发 hungtask 检测的任务名称
  • cpus_stack:所有 CPU 的 NMI 回溯信息(多行文本,包含时间戳和调用栈)
  • blocked_processes_stack:D 状态进程的内核栈信息

7. memory_reclaim_events 内存回收

功能描述 检测容器进程发生直接内存回收(direct reclaim)的事件,当同一进程在 1 秒内直接回收时间超过阈值(默认 900ms)时触发,记录回收耗时、进程及容器信息。注意:该观测器仅记录容器进程的内存回收事件,宿主机进程的事件会被过滤。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "pid": 1896137,
        "tid": 1896138,
        "comm": "java",
        "reclaim_duration_ns": 1412702917
    }
}

字段含义解释

  • comm:触发直接内存回收的进程名称
  • pid:触发进程的 PID
  • tid:触发线程的 TID
  • reclaim_duration_ns:直接回收耗时(纳秒)

8. ras 硬件错误

功能描述 通过内核 tracepoint 检测 CPU、内存、PCIe 等硬件错误,支持 5 种硬件错误来源:MCE(Machine Check Exception)、EDAC(内存控制器)、ACPI/GHES(非标准硬件错误)、PCIe AER(高级错误上报)、MCE 阈值中断(THR)。错误按严重程度分级:Corrected(已纠正)、UncorrectedRecoverable(未纠正可恢复)、UncorrectedFatal(未纠正致命)。MCE 阈值中断事件采用冷却策略(默认 30 分钟),防止中断风暴触发大量重复上报。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

MCE 示例数据

{
    "tracer_data": {
        "dev": "CPU/MEM",
        "event": "MCE",
        "type": "UncorrectedRecoverable",
        "info": "{\"mcg_cpu_cap\":4096,\"banks_msr_status\":9295429630892703744,\"cpu\":2,\"socketid\":0,\"bank\":5}"
    }
}

PCIe AER 示例数据

{
    "observed_timestamp": "2025-06-11T00:00:00Z",
    "tracer_data": {
        "dev": "PCIe 0000:3b:00.0",
        "event": "AER",
        "type": "UncorrectedRecoverable",
        "info": "{\"dev_name\":\"0000:3b:00.0\",\"err_type\":\"UncorrectedRecoverable\",\"err_reason\":\"Completion Timeout\",\"tlp_header\":\"not available\"}"
    }
}

字段含义解释

  • dev:发生错误的硬件设备(如 CPU/MEM、PCIe 0000:3b:00.0)
  • event:错误类型(MCE / EDAC / NON_STANDARD / AER / MCE_THRESHOLD)
  • type:错误严重程度(Corrected / UncorrectedRecoverable / UncorrectedDeferred / UncorrectedFatal / Info)
  • observed_timestamp:顶层字段,表示用户态观测事件的 UTC 时间;kernel_observed_timestamp 表示内核观测事件的 UTC 时间
  • info:JSON 格式的详细错误信息,内容因 event 类型不同而不同

9. netdev_events 网络设备

功能描述 通过订阅内核 netlink RTM_NEWLINK 消息,检测网卡链路状态变化事件(down/up、MTU 变更、AdminDown、CarrierDown 等),输出接口名称、链路状态、MAC 地址及驱动信息。观测器启动时会扫描 device_list 中配置的网卡当前状态作为基线,后续仅上报状态变化事件。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "ifname": "eth1",
        "index": 3,
        "linkstatus": "linkStatusAdminDown, linkStatusCarrierDown",
        "mac": "5c:6f:69:34:dc:72",
        "start": false,
        "driver": "ixgbe",
        "driver_version": "5.1.0-k",
        "firmware_version": "3.25 0x80000421 1.2163.0"
    }
}

字段含义解释

  • ifname:网络接口名称(如 eth1)
  • index:接口索引号
  • linkstatus:链路状态变化描述(可包含多个状态)
  • mac:网卡 MAC 地址
  • start:是否为启动时扫描的基线事件(true:启动扫描,false:实时变化事件)
  • driver:网卡驱动名称
  • driver_version:网卡驱动版本
  • firmware_version:网卡固件版本

10. netdev_bonding_lacp LACP 协议

功能描述 检测 bonding 模式下 LACP(Link Aggregation Control Protocol,IEEE 802.3ad)协议的状态变化,读取并记录 /proc/net/bonding/ 下所有 bonding 接口的完整状态信息,包含模式、MII 状态、Actor/Partner 协商参数、Slave 链路状态等。仅在系统存在 IEEE 802.3ad bonding 模式接口时自动启用。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "content": "/proc/net/bonding/bond0\nEthernet Channel Bonding Driver: v4.18.0...\nBonding Mode: IEEE 802.3ad Dynamic link aggregation\nMII Status: down\n..."
    }
}

字段含义解释

  • content:完整的 bonding 接口状态信息(多行文本,包含所有 Slave 的 LACP 协商细节,等同于 /proc/net/bonding/bondX 文件内容)

11. netdev_txqueue_timeout 发送队列超时

功能描述 检测网卡发送队列超时(TX queue timeout)事件,记录发生超时的队列索引、设备名称和驱动名称,用于定位网卡发送方向的硬件故障。

数据存储 自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_data": {
        "queue_index": 3,
        "device_name": "eth0",
        "driver_name": "ixgbe"
    }
}

字段含义解释

  • queue_index:发生超时的发送队列索引
  • device_name:网卡设备名称
  • driver_name:网卡驱动名称

⚙️ 原理

整体架构

HUATUO 的异常事件观测基于 eBPF 技术,在内核态以极低的性能损耗采集异常事件现场数据,并通过用户态守护进程完成格式化、过滤、容器信息关联和持久化存储。

graph TB
    subgraph "Linux Kernel"
        direction TB
        K1["kprobe 挂钩\n(sched_tick / softlockup / hungtask\n memory_oom_kill / memory_reclaim_events\n net_rx_latency / netdev_txqueue_timeout\n tcp_retransmit TLP,可选)"]
        K2["tracepoint 挂钩\n(ras: MCE / EDAC / AER / ACPI\n dropwatch: skb/kfree_skb\n tcp_retransmit:\n tcp/tcp_retransmit_skb /\n tcp/tcp_retransmit_synack)"]
        K3["netlink 订阅\n(netdev_events: RTM_NEWLINK)"]
        K4["kprobe 挂钩\n(netdev_bonding_lacp: 802.3ad)"]
        PEB["Perf Event 环形缓冲区\n(8192 页)"]
    end

    subgraph "HUATUO 用户态"
        direction TB
        EH["Go 事件处理 goroutine\n(每类事件独立运行)"]
        CF["过滤器\n(阈值判断 / 降噪 / 已知问题过滤)"]
        CM["容器信息关联\n(CSS → ContainerID\n NetNS → ContainerID)"]
    end

    subgraph "存储"
        ES["Elasticsearch"]
        DISK["本地磁盘文件"]
    end

    K1 --> PEB
    K2 --> PEB
    K4 --> PEB
    PEB --> EH
    K3 --> EH
    EH --> CF
    CF --> CM
    CM --> ES
    CM --> DISK

事件处理流程

sequenceDiagram
    participant K as Linux Kernel
    participant B as eBPF Program
    participant P as Perf Event Buffer
    participant H as Go 事件处理器
    participant F as 过滤器
    participant S as 存储

    K->>B: 触发 kprobe / tracepoint
    B->>B: 采集现场数据<br/>(进程信息 / 内核栈 / 网络上下文)
    B->>P: 写入 perf event 环形缓冲区
    H->>P: 读取事件数据(阻塞等待)
    H->>F: 格式化并执行过滤<br/>(阈值 / 降噪 / 已知问题)
    F->>H: 通过过滤的事件
    H->>H: 关联容器信息<br/>(CSS / NetNS 映射)
    H->>S: 持久化存储<br/>(Elasticsearch / 本地文件)

5.3 - 全自动化追踪

📖 概述

HUATUO AutoTracing(全自动化追踪)是一种事件驱动的自动诊断机制。当物理机或容器出现 CPU 突增、D 状态进程堆积、磁盘 IO 打满、内存突发分配等性能异常时,系统依据预设阈值自动触发现场数据采集,无需人工介入即可保留完整的诊断快照。

采集内容包括 eBPF 火焰图(perf 工具系统级或容器级 CPU 调用栈采样)、D 状态进程内核调用栈、磁盘 IO 调用栈、进程内存使用排行等。为避免持续触发导致的数据冗余,各事件均内置冷却策略(默认 30 分钟),确保在事件风暴期间仅保留关键快照。

当前支持 5 类事件:cpusys(物理机 CPU sys 突增)、cpuidle(容器 CPU 使用率突增)、dload(容器 D 状态负载突增)、iotracing(磁盘 IO 异常)、memburst(内存突发分配)。

🎯 场景

AI 训练任务 CPU 热点定位:在 GPU 训练集群中,训练任务偶发性卡顿往往由内核态 CPU 占用率突增(cpusys)引起。AutoTracing 在 sys 占用率超过阈值的瞬间自动触发系统级 perf 火焰图采集,将内核调用栈热点以火焰图数据结构(flamedata)持久化,支持在故障消失后进行离线分析,避免人工复现困难。

Kubernetes 容器 CPU 性能毛刺分析:在微服务架构中,容器 CPU 使用率(cpuidle)的短暂突增可能导致响应延迟超时,但问题往往在告警响应前已恢复。AutoTracing 在容器 CPU 超阈值时自动触发容器级 perf 采样,生成精确到容器 cgroup 范围的火焰图,快速定位热点函数,降低依赖日志排查的时间成本。

云原生环境 D 状态进程堆积排查:在高 IO 负载或存储抖动时,容器内可能出现大量 D 状态(不可中断睡眠)进程,导致系统卡顿。dload 事件通过对容器负载均值进行指数加权移动平均(EMA)计算,在 D 状态进程负载超过阈值时自动抓取容器内及宿主机上相关进程的内核调用栈,精准定位阻塞根因。

磁盘 IO 瓶颈根因定位:在大数据或日志密集型业务中,磁盘 IO 利用率或写入带宽打满会导致应用请求堆积。iotracing 持续轮询 /proc/diskstats,在磁盘 IO 指标连续两次超过阈值时触发,采集高 IO 进程列表(含各进程读写字节数与打开文件详情)及正在等待 IO 调度的进程内核调用栈,快速缩小磁盘 IO 高消耗的进程范围。

🚀 使用

配置参数

各事件可通过以下参数进行调优,参数均提供默认值,无需配置即可运行:

参数 默认值 说明
cpuidle.user_threshold 75(%) 容器 CPU user 占用率触发阈值
cpuidle.sys_threshold 45(%) 容器 CPU sys 占用率触发阈值
cpuidle.usage_threshold 90(%) 容器 CPU 总占用率触发阈值
cpuidle.delta_user_threshold 45(%) 容器 CPU user 占用率增量触发阈值
cpuidle.delta_sys_threshold 20(%) 容器 CPU sys 占用率增量触发阈值
cpuidle.delta_usage_threshold 55(%) 容器 CPU 总占用率增量触发阈值
cpuidle.interval 10(秒) 检测间隔
cpuidle.interval_tracing 1800(秒) 同一容器触发冷却时间
cpuidle.run_tracing_tool_timeout 10(秒) perf 火焰图采集超时
cpusys.sys_threshold 45(%) 物理机 CPU sys 占用率触发阈值
cpusys.delta_sys_threshold 20(%) 物理机 CPU sys 占用率增量触发阈值
cpusys.interval 10(秒) 检测间隔
cpusys.interval_tracing 1800(秒) 全局触发冷却时间
cpusys.run_tracing_tool_timeout 10(秒) perf 火焰图采集超时
dload.threshold_load 5 容器不可中断进程负载 EMA 触发阈值
dload.interval 10(秒) 检测间隔
dload.interval_tracing 1800(秒) 同一容器触发冷却时间
iotracing.rbps_threshold 2000(MB/s) 磁盘读吞吐率触发阈值
iotracing.wbps_threshold 1500(MB/s) 磁盘写吞吐率触发阈值
iotracing.util_threshold 90(%) 磁盘 IO 利用率触发阈值
iotracing.await_threshold 100(ms) 磁盘 IO 平均等待时间触发阈值
iotracing.run_tracing_tool_timeout 10(秒) IO 调用栈采集超时
iotracing.max_proc_dump 10 最多采集的高 IO 进程数
iotracing.max_files_per_proc_dump 5 每个进程最多采集的打开文件数
memburst.delta_memory_burst 100(%) 匿名内存相对滑动窗口最早采样的增长率阈值(100% 即 ≥ 2 倍时触发)
memburst.delta_anon_threshold 70(%) 匿名内存占物理机总内存的比例阈值
memburst.interval 10(秒) 检测间隔
memburst.interval_tracing 1800(秒) 触发冷却时间
memburst.sliding_window_length 60 滑动窗口采样数(对应 600 秒历史数据)
memburst.dump_process_max_num 10 最多采集的内存消耗进程数

事件列表

事件名称(tracer_name) 观测对象 触发条件 典型场景
cpusys 物理机 sys > 45% 且 delta_sys > 20% 内核态 CPU 突增、系统调用热点
cpuidle 容器 (user>75% 且 delta_user>45%) 或 (sys>45% 且 delta_sys>20%) 或 (total>90% 且 delta_total>55%) 容器 CPU 使用率突增、热点函数分析
dload 容器 不可中断进程负载 EMA > 5 D 状态进程堆积、IO 阻塞
iotracing 物理机 磁盘 IO 指标连续两次超阈值 磁盘 IO 打满、IO 等待高延迟
memburst 物理机 匿名内存 ≥ 窗口最早值 2 倍且占总内存 ≥ 70% 内存突发分配、OOM 前兆

通用字段说明

所有事件数据均包含以下通用字段:

  • hostname:物理机 hostname
  • region:物理机所在可用区
  • uploaded_timestamp:数据上传时间
  • container_id:如果事件关联容器,则记录的容器 ID
  • container_hostname:如果事件关联容器,则记录的容器 hostname
  • container_host_namespace:如果事件关联容器,则记录容器的 K8s 命名空间
  • container_type:容器类型
  • container_qos:容器 QoS 级别
  • tracer_name:事件名称(如 cpusys、memburst 等)
  • tracer_id:此次的 tracing ID
  • started_timestamp:触发 tracing 时间
  • tracer_type:观测类型,自动追踪记录固定为 autotracing
  • tracer_data:特定事件私有数据(详见各事件说明)

1. cpusys

功能描述 周期性读取 /proc/stat,计算物理机 CPU sys 占用率及相邻两次采样的增量。当 sys 占用率超过阈值(默认 45%)且增量超过阈值(默认 20%)时,触发系统级 perf 采样,生成全机 CPU 火焰图数据。全局默认冷却 30 分钟,避免重复触发。

数据存储 事件数据自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_name": "cpusys",
    "tracer_data": {
        "system_percent": 52,
        "system_percent_threshold": 45,
        "system_percent_delta": 25,
        "system_percent_delta_threshold": 20,
        "flamedata": [
            {"level": 0, "value": 1000, "self": 0, "label": "all"},
            {"level": 1, "value": 350, "self": 350, "label": "do_syscall_64"}
        ]
    }
}

字段含义解释

  • system_percent:触发时物理机 CPU sys 占用率(%)
  • system_percent_threshold:sys 占用率触发阈值(%)
  • system_percent_delta:相邻两次采样的 sys 占用率增量(%)
  • system_percent_delta_threshold:sys 增量触发阈值(%)
  • flamedata:perf 采样生成的火焰图帧数据列表,每帧包含:
    • level:调用栈层级深度
    • value:该帧(含子帧)的采样计数
    • self:该帧自身(不含子帧)的采样计数
    • label:函数或进程名称标签

2. cpuidle

功能描述 周期性读取容器 cgroup CPU 统计,计算容器 CPU user、sys、总占用率及各指标的相邻增量。当任意一组阈值条件成立时(user>75% 且 delta_user>45%,或 sys>45% 且 delta_sys>20%,或 total>90% 且 delta_total>55%),触发容器级 perf 采样生成火焰图。同一容器默认 30 分钟冷却,避免重复触发。支持通过容器过滤器(filter)排除特定容器。

数据存储 事件数据自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_name": "cpuidle",
    "tracer_data": {
        "user_percent": 80,
        "user_percent_threshold": 75,
        "user_percent_delta": 48,
        "user_percent_delta_threshold": 45,
        "system_percent": 12,
        "system_percent_threshold": 45,
        "system_percent_delta": 5,
        "system_percent_delta_threshold": 20,
        "total_percent": 92,
        "total_percent_threshold": 90,
        "total_percent_delta": 53,
        "total_percent_delta_threshold": 55,
        "flamedata": [
            {"level": 0, "value": 1000, "self": 0, "label": "all"},
            {"level": 1, "value": 800, "self": 800, "label": "java/com.example.App.main"}
        ]
    }
}

字段含义解释

  • user_percent / user_percent_threshold:触发时容器 CPU user 占用率(%)及其阈值
  • user_percent_delta / user_percent_delta_threshold:user 占用率增量(%)及其阈值
  • system_percent / system_percent_threshold:触发时容器 CPU system 占用率(%)及其阈值
  • system_percent_delta / system_percent_delta_threshold:system 占用率增量(%)及其阈值
  • total_percent / total_percent_threshold:触发时容器 CPU 总占用率(%)及其阈值
  • total_percent_delta / total_percent_delta_threshold:总占用率增量(%)及其阈值
  • flamedata:容器级 perf 采样火焰图帧数据,字段含义同 cpusys

3. dload

功能描述 通过 netlink 及 cgroup 读取容器内进程状态,对不可中断(D 状态)进程的负载贡献进行指数加权移动平均(EMA)计算。当容器 D 状态负载 EMA 超过阈值(默认 5)时,采集容器内及宿主机中所有 D 状态进程的内核调用栈,支持已知问题过滤(issues_list)降低误报率。同一容器默认 30 分钟冷却。

数据存储 事件数据自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_name": "dload",
    "tracer_data": {
        "threshold": 5,
        "nr_sleeping": 120,
        "nr_running": 4,
        "nr_stopped": 0,
        "nr_uninterruptible": 8,
        "nr_iowait": 3,
        "load_avg": 7.23,
        "dload_avg": 6.81,
        "known_issue": "",
        "stack": "task:java            state:D stack:    0 pid: 12345 tgid: 12345 ...\n  io_schedule+0x18/0x40\n  ext4_file_write_iter+0x..."
    }
}

字段含义解释

  • threshold:D 状态负载 EMA 触发阈值
  • nr_sleeping:容器内睡眠状态进程数
  • nr_running:容器内运行状态进程数
  • nr_stopped:容器内停止状态进程数
  • nr_uninterruptible:容器内不可中断(D 状态)进程数
  • nr_iowait:容器内 IO 等待状态进程数
  • load_avg:触发时容器负载均值
  • dload_avg:触发时容器 D 状态负载 EMA 值
  • known_issue:命中的已知问题描述(为空表示未命中)
  • stack:D 状态进程的内核调用栈(多进程多行文本)

4. iotracing

功能描述 以 5 秒间隔轮询 /proc/diskstats,计算各磁盘设备的读写吞吐率、IO 利用率及 IO 等待时间。当任一指标连续两次采样均超过对应阈值时触发(自动忽略 md 设备),采集高 IO 进程列表(含各进程的读写字节数及打开文件统计)以及正在等待 IO 调度的进程内核调用栈。

数据存储 事件数据自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_name": "iotracing",
    "tracer_data": {
        "reason_snapshot": {
            "type": "ioutil",
            "device": "sda",
            "iostatus": {
                "read_bps": 120,
                "read_iops": 450,
                "read_await": 12,
                "write_bps": 2100,
                "write_iops": 890,
                "write_await": 145,
                "io_util": 95,
                "queue_size": 32
            }
        },
        "process_io_data": [
            {
                "pid": 12345,
                "comm": "java",
                "container_hostname": "app-pod-xxx",
                "fs_read": 0,
                "fs_write": 52428800,
                "disk_read": 0,
                "disk_write": 49152000,
                "file_stat": ["/data/logs/app.log"],
                "file_count": 1
            }
        ],
        "timeout_io_stack": [
            {
                "pid": 12345,
                "comm": "java",
                "container_hostname": "app-pod-xxx",
                "latency_us": 250000,
                "stack": {
                    "back_trace": [
                        "io_schedule+0x18/0x40",
                        "ext4_file_write_iter+0x2a0/0x4c0"
                    ]
                }
            }
        ]
    }
}

字段含义解释

  • reason_snapshot:触发 IO 采集的原因快照
    • type:触发类型(ioutil IO 利用率 / read_bps 读吞吐率 / write_bps 写吞吐率 / read_await 读等待时间 / write_await 写等待时间)
    • device:触发阈值的磁盘设备名称
    • iostatus:触发时各磁盘 IO 指标快照(read_bps/write_bps 单位 MB/s,read_await/write_await 单位 ms,io_util 单位 %,queue_size 为队列深度)
  • process_io_data:高 IO 进程列表,每条记录包含:
    • pid / comm:进程 PID 与进程名
    • container_hostname:进程所在容器 hostname(宿主机进程为空)
    • fs_read / fs_write:进程文件系统层面的读写字节数
    • disk_read / disk_write:进程磁盘层面的实际读写字节数
    • file_stat:进程当前打开的文件路径列表
    • file_count:进程打开的文件总数
  • timeout_io_stack:等待 IO 调度的进程调用栈列表,每条记录包含:
    • pid / comm:进程 PID 与进程名
    • container_hostname:进程所在容器 hostname
    • latency_us:IO 等待时长(微秒)
    • stack.back_trace:内核调用栈帧列表

5. memburst

功能描述 周期性采样物理机匿名内存(anonymous memory)使用量,维护长度为 60 个采样点(对应 600 秒)的滑动窗口。当当前匿名内存 ≥ 窗口最早采样值的 2 倍,且匿名内存占物理机总内存 ≥ 70% 时触发,采集内存消耗最多的前 N 个进程(默认 10 个)的 PID、进程名和 RSS 内存值。默认 30 分钟冷却。

数据存储 事件数据自动存储至 Elasticsearch 或物理机磁盘文件。

示例数据

{
    "tracer_name": "memburst",
    "tracer_data": {
        "top_memory_usage": [
            {
                "pid": 3456,
                "process_name": "java",
                "memory_size": 8589934592
            },
            {
                "pid": 3789,
                "process_name": "python3",
                "memory_size": 2147483648
            }
        ]
    }
}

字段含义解释

  • top_memory_usage:内存消耗最多的进程列表(按 RSS 降序排列),每条记录包含:
    • pid:进程 PID
    • process_name:进程名称
    • memory_size:进程 RSS 内存占用(字节)

⚙️ 原理

整体架构

HUATUO AutoTracing 以周期性轮询为基础,结合 eBPF 调用栈采集与 perf 火焰图生成,在内核层实现低开销的异常诊断数据采集。

graph TB
    subgraph "数据来源"
        P1["/proc/stat\n(物理机 CPU 占用率)"]
        P2["cgroup CPU 统计\n(容器 CPU 占用率)"]
        P3["netlink / cgroup\n(容器进程状态 / 负载均值)"]
        P4["/proc/diskstats\n(磁盘 IO 指标)"]
        P5["/proc/meminfo\n+ cgroup 内存统计"]
    end

    subgraph "HUATUO AutoTracing"
        DT["阈值检测\n(滑动窗口 / EMA / 连续两次超阈值)"]
        BO["冷却策略\n(30 分钟 backoff)"]
        PERF["perf 火焰图采集\n(系统级 / 容器级)"]
        BPF["eBPF kprobe\n(IO 调度延迟追踪)"]
        CM["容器信息关联\n(cgroup → ContainerID)"]
    end

    subgraph "存储"
        ES["Elasticsearch"]
        DISK["本地磁盘文件"]
    end

    P1 --> DT
    P2 --> DT
    P3 --> DT
    P4 --> DT
    P5 --> DT
    DT --> BO
    BO --> PERF
    BO --> BPF
    PERF --> CM
    BPF --> CM
    CM --> ES
    CM --> DISK

事件处理流程

sequenceDiagram
    participant M as 周期性指标采集
    participant D as 阈值检测器
    participant B as 冷却策略(backoff)
    participant C as 现场数据采集器
    participant S as 存储

    M->>D: 推送指标(每 10 秒)
    D->>D: 阈值判断(滑动窗口 / EMA / 连续两次)
    alt 超过阈值
        D->>B: 检查冷却状态
        alt 允许触发
            B->>C: 触发现场采集<br/>(perf 火焰图 / D 状态进程栈 / IO 进程列表)
            C->>C: 关联容器信息(cgroup → ContainerID)
            C->>S: 持久化存储(Elasticsearch / 本地文件)
        else 冷却期内
            B-->>D: 跳过本次触发
        end
    end

5.4 - 持续性能剖析

🚀 快速上手

示例基于仓库根目录下的 build/docker/docker-compose.yml,从零开始完成一次宿主机 CPU 性能剖析,并在 Grafana 中选择时间范围分析火焰图。

1. 启动服务

先修改配置,确保 huatuo-bamai 和 huatuo-apiserver 有写入 ES 的能力,才能正常保存 profile 数据。配置文件通过 docker compose 挂载到容器中,因此直接修改项目根目录下的文件即可。

huatuo-bamai.conf 中配置:

[HTTPServer.Auth]
    BearerToken = "REPLACE_WITH_NODE_TOKEN"

[Storage]
    [Storage.Elasticsearch]
        Address = "http://127.0.0.1:9200"
        Index = "huatuo_bamai"
        Username = "elastic"
        Password = "huatuo-bamai"

huatuo-apiserver.conf 中配置:

[Agent.Auth]
    BearerToken = "REPLACE_WITH_NODE_TOKEN"

[Elasticsearch]
    Address = "http://127.0.0.1:9200"
    Username = "elastic"
    Password = "huatuo-bamai"
    Index = "huatuo_bamai"

[Auth]
    [[Auth.Users]]
        ID = "administrator"
        BearerToken = "REPLACE_WITH_RANDOM_HEX"
        Admin = true

然后项目根目录执行:

docker compose --project-directory ./build/docker up

不加 -d,方便观察各组件启动日志。如需后台运行可另开终端操作,或自行加 -d。

启动后运行的组件及作用:

服务 作用 默认端口
huatuo-bamai 采集 Agent,执行 profiler 采样 19704
huatuo-apiserver API 入口,创建任务、下发、查询火焰图 12740
elasticsearch 存储 profile 数据(index:huatuo_bamai) 9200
grafana 火焰图面板展示 3000

2. 确认服务就绪

新开一个终端,执行以下命令确认服务就绪:

# Agent 健康检查
$ curl -s http://localhost:19704/version | jq .data.name

"huatuo-bamai"

# API Server 健康检查
$ curl -s http://localhost:12740/version | jq .data.name

"huatuo-apiserver"

# ES 索引状态
$ curl -s -u elastic:huatuo-bamai "http://localhost:9200/_cat/indices/huatuo_bamai?v"

health status index        uuid                   pri rep docs.count docs.deleted store.size pri.store.size dataset.size
yellow open   huatuo_bamai 147fzHJhQ820GjCKFLh5ZQ   1   1         42            0    297.8kb        297.8kb      297.8kb

设置环境变量便于后续调用:

API_BASE="http://127.0.0.1:12740"
API_TOKEN="REPLACE_WITH_RANDOM_HEX"

3. 创建宿主机 CPU 剖析任务

以 c 语言(原生,覆盖 C/C++/Go 等)为例,对宿主机整体采样 30 秒:

# hostname 需要使用节点的实际主机名
HOSTNAME=$(hostname)

JOB_ID=$(curl -s -X POST \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"type\": \"cpu\",
    \"language\": \"c\",
    \"mode\": \"oncpu\",
    \"scope\": \"host\",
    \"duration_seconds\": 30,
    \"hostname\": \"${HOSTNAME}\"
  }" \
  "${API_BASE}/v1/profiling" | jq -r .data.request_id)

echo "Job ID: $JOB_ID"

4. 验证数据已写入 ES

每 10 秒生成一个聚合窗口,30 秒任务约产生 3 条采样数据,等待 30 秒后确认数据写入:

$ curl -s -u elastic:huatuo-bamai "http://localhost:9200/huatuo_bamai/_count" \
  -H "Content-Type: application/json" \
  -d '{"query":{"exists":{"field":"profile_data.profile"}}}' | jq .count

3

5. Grafana 面板查看火焰图

示例是对本机进行剖析,打开 Continuous Profiling (host) 面板(容器是独立的面板):

操作步骤:

  1. 右上角选择时间范围,确保覆盖采样时间段
  2. 变量栏选择输入你的 hostname 和 选择 type 为 process_cpu:cpu:nanoseconds:cpu:nanoseconds
  3. 火焰图面板自动加载该时间窗口的聚合调用栈,随选择时间范围变化而自动聚合更新,focus block 可选定关心的调用栈
  4. symbol 排序、统计、筛选等操作在 top table 中进行,选择 Both 可展示

continuous-profiling-grafana-host.png

其他更多丰富维度的剖析任务参考 Profiling API。

🌐 Profiling API

huatuo-apiserver 通过 /v1/profiling 创建、监督和停止 Profiling Job。每次 HTTP 创建请求都是一次独立执行。Job 持久化在 huatuo-apiserver,Node 只维护内存中的 Operation 和 profiler 进程。

Job 控制不依赖 Elasticsearch;查询原始结果和生成 Dashboard 链接时才要求配置 Elasticsearch。Node 和 Apiserver 必须使用同一索引。

1. 请求约定

huatuo-apiserver 默认监听 :12740。以下示例使用环境变量统一设置服务地址和 Bearer token:

API_BASE="http://127.0.0.1:12740"
API_TOKEN="REPLACE_WITH_RANDOM_HEX"

每个请求必须在 Authorization 请求头中传入配置的 Bearer token:

Authorization: Bearer REPLACE_WITH_RANDOM_HEX

非管理员用户需要配置 /v1/profiling 和 /v1/profiling/** 权限。权限可带 HTTP 方法前缀,例如 GET /v1/profiling/**。成功响应仅包含 data:

{
  "data": {}
}

错误响应包含稳定错误码和可读消息:

{
  "error": {
    "code": "result_unavailable",
    "message": "Job state does not provide a complete Profiling result"
  }
}

客户端应根据 error.code 分支处理,不应解析 error.message。

2. 查询剖析能力

创建任务前,建议先查询服务端支持的剖析类型、语言、CPU 模式、内存模式和运行参数:

curl -sS \
  -H "Authorization: Bearer ${API_TOKEN}" \
  "${API_BASE}/v1/profiling/capabilities"

data.items 是随版本发布的静态能力表,每项包含:

字段 说明
type 剖析类型:cpu 或 memory
language 目标语言
modes 此类型与语言组合允许的 mode
supports_binary_match 是否允许 binary_match_path
supported_scopes 允许的 scope

当前 c、c++ 和 go CPU 剖析支持 oncpu、offcpu,java 和 python 仅支持 oncpu。内存剖析支持以下组合:

语言 mode 说明
c、c++、go virtual_alloc 虚拟地址空间分配
c、c++、go physical_alloc 物理页分配
c、c++、go physical_usage 当前物理页驻留
java object_alloc JVM 对象分配
java object_usage JVM 存活对象

3. 创建剖析任务

POST /v1/profiling 的 JSON 参数如下:

参数 是否必需 说明
type 是 剖析类型:cpu 或 memory
language 是 目标进程语言,必须与剖析类型匹配
mode 是 能力表为对应类型和语言声明的模式
duration_seconds 是 采集时长,单位为秒
hostname 是 运行目标进程的节点主机名,用于任务调度
scope 是 能力表允许的 host 或 container
container_id container 范围必需 目标容器 ID
binary_match_path 否 能力表允许时使用的可执行文件路径匹配条件

即使请求内容相同,每次请求也会创建一个新 Job。超过每节点或进程级活动 Job 配额时返回 HTTP 429。

创建宿主机 Go CPU 剖析任务:

curl -sS -i \
  -X POST \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "cpu",
    "language": "go",
    "mode": "oncpu",
    "scope": "host",
    "duration_seconds": 60,
    "hostname": "node-01"
  }' \
  "${API_BASE}/v1/profiling"

创建容器内 Java 存活对象剖析任务:

curl -sS -i \
  -X POST \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "memory",
    "language": "java",
    "mode": "object_usage",
    "scope": "container",
    "duration_seconds": 60,
    "container_id": "9f4c2f1a8b7d",
    "hostname": "node-01"
  }' \
  "${API_BASE}/v1/profiling"

创建成功返回 201 Created,Location 响应头指向新任务,响应体包含后续查询所需的任务 ID:

{
  "data": {
    "request_id": "<profile-job-id>",
    "hostname": "node-01",
    "duration_seconds": 60,
    "scope": "host",
    "type": "cpu",
    "language": "go",
    "mode": "oncpu",
    "status": "pending",
    "created_at": "2026-08-24T10:00:00Z",
    "updated_at": "2026-08-24T10:00:00Z"
  }
}
JOB_ID="<profile-job-id>"

4. 查询任务列表

GET /v1/profiling 支持以下查询参数:

参数 默认值 说明
limit 100 每页数量,取值范围为 1 到 1000
offset 0 起始偏移量,必须大于或等于 0

查询最新的 20 个 Profiling Job:

curl -sS -G \
  -H "Authorization: Bearer ${API_TOKEN}" \
  --data-urlencode "limit=20" \
  --data-urlencode "offset=0" \
  "${API_BASE}/v1/profiling"

服务端固定只返回 Profiling Job,并按 created_at 倒序排列。data.items 是任务数组,data.limit 和 data.offset 是实际使用的分页参数; data.has_more 表示是否还有下一页。非管理员只能查看自己创建的任务。

5. 查询单个任务

curl -sS \
  -H "Authorization: Bearer ${API_TOKEN}" \
  "${API_BASE}/v1/profiling/${JOB_ID}"

任务信息位于 data 字段:

字段 说明
request_id 后续所有请求使用的 Profiling Job ID
container_id 目标容器 ID;宿主机任务不返回该字段
hostname 目标节点主机名
duration_seconds 请求的剖析时长,单位为秒
scope host 或 container
type cpu 或 memory
language 目标进程语言
mode 从能力表中选择的剖析模式
binary_match_path 可执行文件匹配路径;未使用时不返回该字段
status 当前任务状态
terminal 终态详情 {outcome, reason, message};非终态不返回
created_at 任务创建时间
updated_at Job 状态最后更新时间
started_at 实际开始执行时间;尚未观察到运行时不返回
ended_at 进入终态的时间;运行期间不返回
result_url 完整结果可用时,由单任务详情接口返回的 Dashboard 链接

任务状态流转如下:

状态 说明
pending 任务已创建,正在等待 Agent 执行
running Agent 正在采集剖析数据
stopping 停止意图已持久化,正在异步执行停止操作
terminal 任务已结束,具体结果见 terminal.outcome

terminal.outcome 可取 completed、failed、stopped 或 unknown。failed 时通过 terminal.reason 和 terminal.message 定位原因; stopped 表示主动停止,结果可能不完整;unknown 表示 Apiserver 无法确认 Operation 最终状态,但持久发布的结果仍可能可用。

operation_lost 和 execution_timed_out 是 terminal.reason 的原因码,不是 Job 状态。其他失败原因包括等待启动或停止超时、Node 不可用、执行容量超过上限、 启动或执行失败、表示 Apiserver 构造的 Node 请求无效的 invalid_node_request,以及协议错误。

6. 获取原始剖析数据

GET /v1/profiling/:request_id/raw 仅在持久发布标记仍存在时返回 terminal.outcome 为 completed 或 unknown 的 Job 原始剖析窗口。发布标记 不存在或已经过期时返回 result_not_found。活动状态返回 result_not_ready; failed 和 stopped 返回 result_unavailable。

数据量可能较大,可以直接保存到文件:

curl -sS \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -o profile-raw.json \
  "${API_BASE}/v1/profiling/${JOB_ID}/raw?limit=100&offset=0"

剖析窗口位于响应体的 data.items 字段;data.limit、data.offset 和 data.has_more 描述分页。每条记录包含 uploaded_timestamp、started_timestamp、 profile_type 和兼容 pprof 的 profile 数据。 已持久发布但内容为空的结果仍返回成功,items 为空数组。显式 limit=0 返回参数 错误;只有未传 limit 才使用默认值。limit 默认值为 20, 最大值为 100。编码后的 Profile 数据超过 64 MiB 时返回 413 result_too_large,调用方应减小 limit 后重试。

火焰图每批读取并合并 100 个窗口,每次请求总上限为 100000 个窗口。一小时每秒产生 一个窗口时,全部 3600 个窗口都会参与合并。超过 100000 个窗口时返回错误,提示缩小 时间范围或过滤条件;查询或合并失败都不返回部分火焰图。原始 Profile 列表仍按 Job 查询,其每页上限不限制完整火焰图的聚合数量。

7. 停止任务

停止是异步意图,pending、running 或已经处于 stopping 的 Job 均可接收:

curl -sS \
  -X POST \
  -H "Authorization: Bearer ${API_TOKEN}" \
  "${API_BASE}/v1/profiling/${JOB_ID}/stop"

成功返回 200 OK 和当前 Job 快照。终态 Job 返回 409 Conflict。由于结果可能 不完整,stopped Job 不提供结果查询。

📖 profiler 命令行功能概述

profiler 是 HUATUO 提供的独立性能剖析命令行工具。它可以直接对宿主机进程或容器内进程采样,不依赖 huatuo-apiserver、Elasticsearch 或 Grafana。工具支持 C、C++、Go、Java 和 Python 进程,并将调用栈输出为折叠栈或 SVG 火焰图。

C、C++ 和 Go 使用基于 eBPF 的原生采集器,可观测 on-CPU、off-CPU 阻塞与调度延迟、虚拟内存分配、物理内存分配和物理内存驻留。Java 通过 async-profiler 观测 CPU、对象分配和存活对象;Python 通过 py-spy 观测 CPU。采集结果适合用于热点函数定位、内存增长归因、容器内进程分析和性能问题现场留存。

本节以下介绍 _output/bin/profiler 的独立使用方式。服务化的持续 Profiling 使用方式见上方 Profiling API。

🎯 应用场景

1. CPU 热点与调用路径定位

对 C、C++、Go、Java 或 Python 进程按固定频率采样调用栈,通过栈宽度判断 CPU 时间的主要消耗路径。原生采集器还可以使用 --cpuid 将采样限定到指定 CPU,以分析绑核任务或局部 CPU 热点。

2. 原生进程内存归因

对 C、C++ 和 Go 进程分别观测虚拟地址空间分配、物理页分配和当前物理页驻留。三种模式区分“申请了多少地址空间”“实际分配了多少物理页”和“当前仍驻留多少物理页”,用于定位 mmap、缺页分配和常驻内存增长的调用路径。

3. JVM 对象分配与存活对象分析

通过 async-profiler 采集 Java 对象分配或存活对象调用栈。对象分配适合定位高分配速率和 GC 压力来源;存活对象适合分析采集窗口内仍被引用的对象及其分配路径。

4. 容器与多进程任务分析

通过容器 ID 自动解析容器内目标进程,适合 Docker 和 containerd 工作负载。Java 和 Python 还支持逗号分隔的多个 PID,并可限制同时运行的采集子进程数量,适用于同一服务的多实例或父子进程分析。

🛠️ 功能使用

1. 构建与运行条件

在仓库根目录构建完整产物:

make build

生成的命令位于 _output/bin/profiler。原生采集依赖 Linux eBPF、perf event 和仓库构建出的 BPF 对象,通常需要 root 权限,并要求 kernel.perf_event_paranoid 允许采样。Java 需要 async-profiler,--tool-path 指向统一工具根目录,其中包含 bin/asprof 和 lib/libasyncProfiler.so。Python 使用同一根目录下的 py-spy。

查看当前版本的完整帮助:

_output/bin/profiler --help

命令的基本结构如下:

sudo _output/bin/profiler \
  --type <cpu|memory> \
  --language <c|c++|go|java|python> \
  --pid <pid> \
  --duration 30 \
  --aggr-interval 10 \
  --output-format flamegraph \
  --output-path ./profiles

--type 和 --language 为必填参数。Java、Python 和原生内存采集必须在 --pid 与 --container-id 中指定且仅指定一个目标;原生 CPU 采集未指定目标时可进行宿主机级采样。

2. 通用命令参数

参数 默认值 适用范围 说明
--type, -t 无 全部 观测类型:cpu 或 memory,必填
--language, -l 无 全部 目标语言:c、c++、go、java 或 python,必填
--pid, -p 无 全部 目标 PID;Java、Python 可使用逗号分隔多个 PID,原生采集最多一个 PID
--container-id 无 全部 目标容器 ID;不能与 --pid 同时使用
--duration, -d 10 全部 总采集时长,单位为秒,最小为 1
--aggr-interval 10 全部 聚合周期,单位为秒,不得大于采集时长
--freq, -F 99 CPU 每秒采样次数;Java 最大为 1000
--output-path . 本地输出 输出目录,不是输出文件名
--output-format collapsed 全部 collapsed、flamegraph、svg 或 remote
--output-storage /var/run/huatuo-toolstream.sock remote 远端上传使用的 Unix socket
--max-concurrent-procs 0 Java、Python 并发采集子进程上限;0 表示不限制
--tool-path 无 Java、Python 外部采集工具统一根目录,必填
--binary-match-path 无 Java、Python 按可执行文件路径匹配容器内目标进程
--huatuo-api-address 127.0.0.1:19704 容器目标 用于解析容器元数据的 HUATUO API 地址
--tracer-id 空;本地输出时内部生成 全部;remote 必填 toolstream 和远端存储共用的稳定采集任务 ID
--enable-pprof false 工具自身 在 :6000 暴露 profiler 进程自身的 Go pprof 接口
--version-format text 版本查询 --version 的输出格式:text、json 或 short
--help, -h - 全部 显示命令帮助
--version, -v - 全部 显示版本与构建信息

原生采集专用参数:

参数 默认值 适用范围 说明
--memory-mode 无 原生内存、Java 内存 内存观测维度;使用 --type memory 时必填
--cpuid 全部 CPU 原生 CPU CPU 列表或范围;off-CPU 样本按任务切出时所在 CPU 过滤
--cpu-mode oncpu 原生 CPU oncpu 按频率采样,offcpu 归因阻塞与可运行调度延迟
--require-hardware-pmu false 原生 on-CPU 强制使用硬件 PMU 采样;不可用时失败,不回退软件 CPU clock
--offcpu-phase all 原生 off-CPU 累计 all、blocked 或 runqueue 时间
--offcpu-min-duration-us 1000 原生 off-CPU 丢弃持续时间小于该微秒数的阶段
--offcpu-stats false 原生 off-CPU 收集 BPF 诊断统计;错误和清理路径会产生额外开销
--thread-group false 原生 同时采集目标 PID 所在线程组中的其他线程
--physical-memory-probability 100 原生物理内存 物理内存事件采样概率,范围为 1~100
--log-bpf-debug false 原生 输出 BPF 调试事件,常规采集不建议启用

日志参数:

参数 默认值 说明
--log-level error trace、debug、info、warn 或 error
--log-file stdout 日志文件路径,或 stdout
--log-size 100 日志轮转大小,单位 MB;0 表示不轮转,仅用于文件输出
--verbose false 等价于 --log-level debug --log-file stdout,并覆盖显式日志设置

3. C、C++ 和 Go 观测

C、C++ 和 Go 均使用原生 eBPF 采集器,命令只需替换 --language。CPU 模式按采样次数统计调用栈宽度,同时包含可解析的用户态栈和内核态栈。

sudo _output/bin/profiler \
  --type cpu \
  --language go \
  --pid 12345 \
  --duration 30 \
  --aggr-interval 10 \
  --freq 99 \
  --output-format flamegraph \
  --output-path ./profiles/go-cpu

如需包含同一进程的工作线程,增加 --thread-group。如需限定 CPU,增加 --cpuid 2,4-7。原生 CPU 也支持容器和宿主机级采样:

原生 on-CPU 采集优先使用硬件 CPU cycle event;硬件 PMU 不可用时回退软件 CPU clock。两种采样源下 --freq 均表示每秒采样次数。若软件时钟回退会掩盖 IRQ 关闭期间的 CPU 时间,可指定 --require-hardware-pmu。

# 采集指定容器
sudo _output/bin/profiler \
  --type cpu --language c --container-id <container-id> \
  --duration 30 --aggr-interval 10 \
  --output-format collapsed --output-path ./profiles/container

# 不指定 PID 或容器,采集宿主机
sudo _output/bin/profiler \
  --type cpu --language c \
  --duration 30 --aggr-interval 10 \
  --output-format flamegraph --output-path ./profiles/host

如需把线程离开 CPU 的时间归因到触发切出的调用路径,使用 off-CPU 模式:

sudo _output/bin/profiler \
  --type cpu --language go --pid 12345 --thread-group \
  --cpu-mode offcpu --offcpu-phase all \
  --cpuid 2,4-7 \
  --offcpu-min-duration-us 1000 \
  --duration 30 --aggr-interval 10 \
  --output-format flamegraph --output-path ./profiles/go-offcpu

off-CPU 是事件驱动采集,因此不使用 --freq。指定 --cpuid 时,仅记录任务从目标 CPU 切出后开始的区间;后续在其他 CPU 唤醒或切入不会改变该归属。火焰图直接以纳秒为数值,并增加 off-CPU blocked、scheduling delay (preempted)、scheduling delay (yielded) 等根节点。all 阶段同时累计阻塞与 runqueue 等待时间,但仍按这些根节点分开显示。采集端使用单一稳定的 BPF stack map,避免长时间睡眠跨越多轮读取后被错误解析到另一代栈。

原生内存支持以下维度:

--memory-mode 统计内容 适用问题
virtual_alloc 虚拟地址空间分配量及其调用栈 mmap 等虚拟内存申请过多、地址空间增长
physical_alloc 采集窗口内新分配的物理内存量 缺页触发的物理页分配热点、分配速率分析
physical_usage 采集时仍驻留的物理内存量 常驻内存来源、物理页未释放路径
sudo _output/bin/profiler \
  --type memory \
  --language c++ \
  --memory-mode physical_usage \
  --pid 12345 \
  --thread-group \
  --physical-memory-probability 100 \
  --duration 30 \
  --aggr-interval 10 \
  --output-format flamegraph \
  --output-path ./profiles/native-memory

--physical-memory-probability 仅适用于 physical_alloc 和 physical_usage。降低该值可减少高频内存事件的处理量,但火焰图中的值由采样事件估算,不再是逐事件统计。

4. Java 观测

Java CPU 采集依赖 async-profiler。单 PID、容器和多 PID 均可使用:

_output/bin/profiler \
  --type cpu \
  --language java \
  --pid 12345,12346 \
  --tool-path /opt/huatuo/tools \
  --max-concurrent-procs 2 \
  --duration 30 \
  --aggr-interval 10 \
  --freq 99 \
  --output-format flamegraph \
  --output-path ./profiles/java-cpu

Java 内存支持两个维度:

--memory-mode 统计内容 适用问题
object_alloc 采集窗口内的对象分配及分配调用栈 高分配速率、短命对象和 GC 压力来源
object_usage 存活对象及其分配调用栈 长生命周期对象、堆占用来源和疑似内存泄漏
_output/bin/profiler \
  --type memory \
  --language java \
  --memory-mode object_usage \
  --pid 12345 \
  --tool-path /opt/huatuo/tools \
  --duration 30 \
  --aggr-interval 10 \
  --output-format flamegraph \
  --output-path ./profiles/java-memory

使用容器 ID 时,将 --pid 替换为 --container-id <container-id>。如果容器内存在多个候选进程,可通过 --binary-match-path 指定目标可执行文件路径。

5. Python 观测

Python 当前仅支持 CPU 观测。--aggr-interval 必须与 --duration 相等,即一次采集只生成一个聚合窗口。--tool-path 指向包含 py-spy 的统一工具根目录。

_output/bin/profiler \
  --type cpu \
  --language python \
  --pid 12345,12346 \
  --tool-path /opt/huatuo/tools \
  --max-concurrent-procs 2 \
  --duration 30 \
  --aggr-interval 30 \
  --freq 99 \
  --output-format flamegraph \
  --output-path ./profiles/python-cpu

Python 不支持 --type memory。若需要 Python 内存分析,应使用独立的内存分析工具;当前 profiler 命令不会调用 memray 生成 Python 内存结果。

6. 火焰图与输出格式选择

格式 生成内容 选择建议
collapsed perf_<Unix 时间戳>.folded;每行是以分号分隔的调用栈及末尾计数 用于脚本检索、结果比较,或交给其他火焰图工具二次渲染
flamegraph flamegraph_<Unix 时间戳>.svg;内嵌交互脚本的 SVG 默认的人工分析格式,可在浏览器中搜索、缩放和查看栈帧数值
svg 与 flamegraph 相同的交互式 SVG 兼容显式要求 SVG 的调用方;当前实现与 flamegraph 等价
remote 不生成本地火焰图,通过 Unix socket 上传 pprof 兼容数据 接入 HUATUO 存储链路时使用,不适合离线查看

火焰图从下到上表示调用方向,矩形宽度表示该调用栈在当前观测维度中的累计值。不同类型的宽度含义不同:CPU 表示采样次数折算的 CPU 时间占比;内存模式表示相应的虚拟分配、物理分配、物理驻留、Java 对象分配或存活对象量。横向位置不表示时间先后。

折叠栈示例:

main;handleRequest;parsePayload 428
main;handleRequest;writeResponse 172

需要保留原始数据并支持后续使用不同配色或过滤规则重新渲染时,选择 collapsed。只需直接定位热点时,选择 flamegraph。remote 依赖 HUATUO toolstream Unix socket,要求提供非空的 --tracer-id,独立离线使用时不应选择该格式。

7. 根据集成测试复现

仓库集成测试提供了可执行的端到端示例。测试会创建目标进程、运行 profiler,并校验输出中的预期调用栈:

# 原生 CPU
sudo ./integration/run.sh test_profiler_native_cpu.sh

# 原生 off-CPU 阻塞与调度延迟
sudo ./integration/run.sh test_profiler_native_cpu_offcpu.sh

# 原生虚拟内存与物理内存
sudo ./integration/run.sh test_profiler_native_memory_virtual_alloc.sh
sudo ./integration/run.sh test_profiler_native_memory_physical_usage.sh

# Java CPU 与内存
sudo ./integration/run.sh test_profiler_java_cpu_multi_pid.sh
sudo ./integration/run.sh test_profiler_java_memory_usage_alloc.sh

# Python 多进程 CPU
sudo ./integration/run.sh test_profiler_python_cpu_multi_pid.sh

容器、线程组和指定 CPU 的示例分别位于 test_profiler_native_cpu_container.sh、test_profiler_native_cpu_thread_group.sh 和 test_profiler_native_cpu_cpuid.sh。运行前需完成 make build,并根据 integration/env.sh 配置 Java 或 Python 采集工具路径。

⚙️ 功能原理介绍

profiler 先根据语言和观测类型选择采集器。原生 on-CPU 采集器将 eBPF 程序挂载到 perf event;off-CPU 模式挂载调度切换、唤醒、退出和任务释放 tracepoint;原生内存采集器通过内核事件记录分配与释放路径;Java 和 Python 采集器分别启动 async-profiler 和 py-spy 子进程。采集记录进入统一聚合流水线,按调用栈合并计数,最后写入本地文件或上传远端存储。

flowchart LR
    CLI[profiler 命令参数] --> Select{语言与观测类型}
    Select -->|C/C++/Go| Native[eBPF 原生采集器]
    Select -->|Java| Java[async-profiler]
    Select -->|Python| Python[py-spy]
    Native --> Queue[采样记录队列]
    Java --> Queue
    Python --> Queue
    Queue --> Aggregate[按调用栈聚合]
    Aggregate --> Folded[collapsed 折叠栈]
    Aggregate --> SVG[交互式 SVG 火焰图]
    Aggregate --> Remote[Unix socket 远端上传]

--duration 控制采集生命周期,--aggr-interval 控制远端上传的快照周期。本地 collapsed、flamegraph 和 svg 在采集结束时写出最终聚合结果;remote 按聚合周期生成并上传快照。队列将采集与符号化、聚合和输出解耦,避免文件渲染阻塞采样路径。

🌟 结尾

5.5 - 硬件故障诊断

概述

HUATUO 华佗以零侵入、低开销的方式持续监听 Linux 内核上报的硬件错误事件,将结构化的故障记录持久化存储,并以 Prometheus 指标形式对外暴露汇总计数器,供告警与可视化系统使用。

应用场景

  • 通用计算

    大规模服务器集群中,内存 ECC 可纠正错误(CE)是常见的低级别故障信号。单次 CE 可由硬件自动修复,但若同一 DIMM 上 CE 频率持续升高,则预示着内存条即将失效。华佗通过 EDAC/MCE tracepoint 实时感知此类事件,使工程团队能够在内存彻底失效前完成预防性换件,避免意外宕机。

  • AI 计算

    AI 训练任务对硬件可靠性要求极高,单块故障的 PCIe 设备即可导致整个训练任务失败。华佗支持 PCIe AER 事件监测,能够实时上报 GPU、NVLink Bridge、RDMA 网卡(如 InfiniBand HCA)的链路层错误(Data Link Protocol Error、ECRC Error 等),为 AI 集群调度系统提供硬件健康状态数据,支撑故障节点的快速隔离与任务迁移。

  • 存储服务

    存储服务器通常配备大量 PCIe NVMe SSD 和 HBA 卡。PCIe AER 中的 Completion Timeout、Malformed TLP 等错误是存储设备性能抖动或掉线的先兆。华佗监控数据可与存储 IO 延迟指标联动,支撑根因分析。

  • 安全合规

    金融、政务等对合规有严格要求的行业,需要完整记录所有硬件故障历史。结构化事件存储(含时间戳、设备标识、错误类型、原始寄存器值)可直接作为硬件健康日志的合规存证。

监控原理

HUATUO 华佗通过 eBPF 技术观测内核的 MCE / EDAC / ACPI GHES / PCIe AER 子系统,当 eBPF tracepoint 被触发时,将原始事件写入 BPF Perf Event Buffer。用户态程序读取事件,解析结构体字段,生成结构化记录,并存储至本地或远端。总体架构如下:

RAS 原理

Linux 内核的 RAS 体系由多个相对独立的子系统协同构成,共同覆盖从 CPU 内部错误到 PCIe 链路错误的完整硬件故障谱系。

graph TB
    subgraph HW["硬件层"]
        CPU["CPU\nx86 / x86-64"]
        MEM["内存\nDDR4/DDR5 DIMM ECC"]
        Platform["平台硬件\nSoC / PCH"]
        PCIeDev["PCIe 设备\nGPU / NVMe / HCA / FPGA"]
    end

    subgraph FW["固件层"]
        BIOS["BIOS / UEFI\nCPER 缓冲区(APEI)"]
    end

    subgraph Kernel["Linux 内核 RAS 子系统"]
        MCE["MCE 子系统\narch/x86/kernel/cpu/mce"]
        EDAC["EDAC 子系统\ndrivers/edac"]
        GHES["ACPI GHES 子系统\ndrivers/acpi/apei"]
        AER["PCIe AER 子系统\ndrivers/pci/pcie/aer"]
    end

    subgraph TP["内核 Tracepoint"]
        TP1["tracepoint/mce/mce_record"]
        TP2["tracepoint/ras/mc_event"]
        TP3["tracepoint/ras/non_standard_event"]
        TP4["tracepoint/ras/aer_event"]
    end

    CPU -->|"MCE 异常(#MC)+ THR 中断"| MCE
    MEM -->|ECC 错误| EDAC
    Platform -->|APEI 错误记录| BIOS
    BIOS -->|CPER 缓冲区| GHES
    PCIeDev -->|AER 中断| AER

    MCE --> TP1
    EDAC --> TP2
    GHES --> TP3
    AER --> TP4
  • MCE

    MCE(Machine Check Architecture)是处理器内置的硬件容错机制,由 Intel 和 AMD 在各自的架构规范中定义。处理器内部存在若干 Bank(Machine Check Bank),每个 Bank 对应一类硬件资源(如 L1 Cache、L2 Cache、内存控制器、TLB 等)。当检测到硬件错误时,对应 Bank 的 MSR 寄存器(MCi_STATUS、MCi_ADDR、MCi_MISC)被填充错误信息,并触发 MCE 异常。

  • MCE THR

    MCE 支持阈值中断机制。当某类可纠正错误的计数超过预设阈值时,触发专用 APIC 中断(THR),而不升级为完整的 MCE 异常。此机制允许操作系统在错误频率异常升高时提前告警,而非等到错误完全不可纠正时才介入。

  • EDAC

    EDAC(Error Detection And Correction)是 Linux 内核中专门处理内存和硬件 ECC 错误的子系统,其目标是"检测并报告运行在 Linux 下的计算机系统中发生的硬件错误"。EDAC 驱动直接与内存控制器通信,解析 ECC 错误的物理位置(内存控制器编号、Channel、Slot、行列地址)。

  • ACPI GHES

    ACPI GHES(Generic Hardware Error Source,通用硬件错误源)是一种平台无关的硬件错误上报机制,由 BIOS/UEFI 通过 APEI(ACPI Platform Error Interface)规范定义。BIOS 固件将无法被特定驱动处理的硬件错误(如特定 SoC 内部错误、平台特定内存错误)写入 GHES 描述符中的 CPER(Common Platform Error Record)缓冲区。Linux 内核读取 CPER 记录,并上报无法被标准子系统解析的"非标准"错误部分。

  • PCIe AER

    PCIe AER(Advanced Error Reporting)是 PCIe 规范定义的错误上报机制,允许 PCIe 设备向操作系统精确报告链路层和事务层的错误类型。

指标总览

  • RAS 指标

    # HELP huatuo_bamai_ras_hw_total total RAS hardware error events by source type
    # TYPE huatuo_bamai_ras_hw_total counter
    huatuo_bamai_ras_hw_total{host="hostname",region="dev",type="acpi"} 0
    huatuo_bamai_ras_hw_total{host="hostname",region="dev",type="aer"} 0
    huatuo_bamai_ras_hw_total{host="hostname",region="dev",type="edac"} 0
    huatuo_bamai_ras_hw_total{host="hostname",region="dev",type="mce"} 0
    huatuo_bamai_ras_hw_total{host="hostname",region="dev",type="thr"} 0
    
  • 网卡丢包

    huatuo_bamai_netdev_hw_rx_dropped_total{host="hostname",region="dev",device="eth0",driver="ixgbe"} 0
    
  • RDMA PFC

    # HELP huatuo_bamai_netdev_dcb_pfc_received_total count of the received pfc frames
    # TYPE huatuo_bamai_netdev_dcb_pfc_received_total counter
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="0",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="1",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="2",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="3",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="4",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="5",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="6",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_received_total{device="enp6s0f0np0",host="hostname",prio="7",region="dev"} 0
    # HELP huatuo_bamai_netdev_dcb_pfc_send_total count of the sent pfc frames
    # TYPE huatuo_bamai_netdev_dcb_pfc_send_total counter
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="0",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="1",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="2",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="3",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="4",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="5",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="6",region="dev"} 0
    huatuo_bamai_netdev_dcb_pfc_send_total{device="enp6s0f0np0",host="hostname",prio="7",region="dev"} 0
    
  • 结构化存储

    此外,每个硬件错误事件均以结构化形式持久化(存储于本地 huatuo-local 目录或远端 ES/OS 存储等),包含以下公共字段:

    {
        "hostname": "hostname",
        "region": "dev",
        "uploaded_timestamp": "2026-03-05T18:28:39.153438921+08:00",
        "tracer_name": "netdev_event",
        "observed_timestamp": "2026-03-05T18:28:39.153+08:00",
        "tracer_type": "event",
        "tracer_data": {
            "ifname": "eth0",
            "index": 2,
            "linkstatus": "linkstatus_admindown",
            "mac": "5c:6f:11:11:11:11",
            "start": false
        }
    }
    

    linkstatus 字段的可能取值如下:

    • linkstatus_adminup 管理员开启网卡,例如 ip link set dev eth0 up
    • linkstatus_admindown 管理员关闭网卡,例如 ip link set dev eth0 down
    • linkstatus_carrierup 物理链路恢复
    • linkstatus_carrierdown 物理链路故障
    {
        "hostname": "localhost",
        "region": "xxx",
        "uploaded_timestamp": "2026-05-11T16:58:47.328548319+08:00",
        "tracer_name": "ras",
        "observed_timestamp": "2026-05-11T08:58:47.329Z",
        "kernel_observed_timestamp": "2026-05-11T08:58:47.328548319Z",
        "tracer_type": "event",
        "tracer_data": {
            "dev": "MEM",
            "event": "EDAC",
            "type": "Corrected",
            "info": "{\"err_count\":0,\"err_type\":\"Corrected\",\"err_msg\":\"memory read error\",\"label\":\"CPU_SrcID#0_Ha#0_Chan#0_DIMM#0\",\"mc_index\":0,\"top_layer\":0,\"mid_layer\":0,\"low_layer\":-1,\"addr\":7860269056,\"grain\":128,\"syndrome\":0,\"driver\":\" area:DRAM err_code:0000:009f socket:0 ha:0 channel_mask:1 rank:0\"}"
        }
    }
    
    字段名 含义
    Device 发生错误的硬件部件标识(如 CPU/MEM、MEM、ACPI、PCIe 0000:01:00.0)
    Event 事件子类型(MCE、EDAC、APIC、AER)
    ErrType 错误严重级别(见下表)
    ObservedTimestamp 用户态观测事件的 UTC 时间
    KernelObservedTimestamp 内核观测事件的 UTC 时间(Document 顶层字段)
    Info 具体事件的详细字段
    错误类型 含义 典型来源
    Corrected 已由硬件自动纠正,系统无感知 MCE CE, EDAC CE, ACPI Sev=1, AER Severity=2
    UncorrectedRecoverable 硬件无法纠正,但系统软件可修复的错误 MCE UE, EDAC UE, ACPI Sev=2, AER Severity=0
    UncorrectedDeferred 硬件无法纠正,需要延迟处理的错误 MCE MCI_STATUS_DEFERRED, EDAC HW_EVENT_ERR_DEFERRED
    UncorrectedFatal 硬件无法纠正的致命错误,需立即重启 EDAC FATAL, ACPI Sev=3, AER Severity=0
    Info 期望系统记录日志信息的错误类型 EDAC HW_EVENT_ERR_INFO, ACPI Sev=0

详细说明

  • MCE

    监控部件:CPU 核心、L1/L2/L3 Cache、TLB、内存控制器(IMC)、互连总线(QPI/UPI/Infinity Fabric)。

    字段名 MSR 来源 含义
    mcg_cpu_cap MCG_CAP 机器检查全局能力寄存器。低 8 位(Count)表示系统中 MC Bank 的数量。
    mcg_msr_status MCG_STATUS 机器检查全局状态寄存器**。
    banks_msr_status MCi_STATUS Bank 状态寄存器(最核心字段)。低 16 位为 MCA 错误代码(分类错误类型,如内存层次错误、总线错误等);高位包含 UC(不可纠正)、EN(已使能)、MISCV(MISC 有效)、ADDRV(ADDR 有效)、PCC(处理器上下文损坏)等控制位。
    banks_msr_addr MCi_ADDR 发生错误的物理内存地址(仅当 MCi_STATUS.ADDRV=1 时有效)。可用于定位故障 DIMM 或 Cache Line。
    banks_msr_misc MCi_MISC 补充信息寄存器(仅当 MCi_STATUS.MISCV=1 时有效)。
    mca_synd_msr MCA_SYND 综合征寄存器(AMD 专用)。
    mca_ipid_msr MCA_IPID 实例 ID 寄存器(AMD 专用)。
    instr_pointer RIP 寄存器 发生 MCE 时的指令指针(仅当 MCG_STATUS.EIPV=1 时可靠)。
    tsc_timestamp TSC 发生错误时的 CPU 时间戳计数器值(可与内核时钟换算为绝对时间)。
    walltime 内核时间 发生错误时的 Unix 时间戳(秒)。
    cpu — 发生 MCE 的逻辑 CPU 编号。
    cpuid CPUID 发生 MCE 的 CPU 的 CPUID 值(包含 Family/Model/Stepping)。
    apicid APIC ID 发生 MCE 的 CPU 对应的 APIC ID(可映射到物理核/超线程)。
    socketid — CPU 插槽编号(Socket ID)。多路服务器场景下用于区分物理 CPU。
    code_seg CS 寄存器 发生 MCE 时的代码段寄存器值(用于判断特权级)。
    bank — Bank 编号(通常 Bank 0=L1I,Bank 1=L1D,Bank 2=L2,Bank 4+=内存控制器,但编号因平台而异)。
    cpuvendor — CPU 厂商标识:0=Intel,1=未知,2=AMD。
  • EDAC

    监控部件:内存 ECC 错误。

    字段名 含义
    err_count 本次事件中累计的错误次数。
    err_type 错误严重级别。
    err_msg 人类可读错误描述字符串(如 "CE memory read error on CPU#0Channel#0_DIMM#0 (channel:0 slot:0 page:0x12345 offset:0x0 grain:8 syndrome:0x0)")。
    label 内存条物理位置标签(如 "CPU_SrcID#0_Ha#0_Chan#0_DIMM#0"),由 EDAC 驱动根据 DIMM 拓扑生成,可直接对应机器内部的内存插槽位置。
    mc_index 内存控制器编号(0-based)。多内存控制器服务器上用于区分不同 IMC。
    top_layer 内存层次结构顶层索引(通常为 Channel 编号,即内存通道号,-1 表示无效)。
    mid_layer 内存层次结构中层索引(通常为 Slot/Rank 编号,-1 表示无效)。
    low_layer 内存层次结构底层索引(通常为 Bank/Row 编号,-1 表示无效)。
    addr 发生错误的物理内存地址(64-bit 无符号整数,0 表示地址无效)。
    grain 错误粒度(Grain Size,字节数)。表示可能受影响的最小内存单元大小。
    syndrome ECC 综合征值。
    driver EDAC 驱动名称(如 "amd64_edac"、"sb_edac")。
  • ACPI GHES

    监控部件:平台特定硬件错误。

    字段名 含义
    severity ACPI/CPER 错误严重级别原始值。
    sec_type 错误部分类型 GUID(16 字节,十六进制字符串)。由 UEFI 规范和各硬件厂商定义,标识错误记录所属的硬件类别(如内存错误部分、PCIe 错误部分、ARM 处理器错误部分等)。
    fru_id FRU(Field Replaceable Unit,现场可替换单元)标识符 GUID(16 字节,十六进制字符串)。唯一标识发生错误的可更换硬件组件(如某块内存条、某个 PCIe 卡)。
    fru_text FRU 人类可读描述字符串(如 "CPU0_DIMM_A1")。
    data_len 原始错误数据载荷长度(字节数)。
    raw_data 原始错误数据的十六进制转储(空格分隔字节)。用于深度诊断,需结合具体硬件厂商文档解析。
  • PCIe AER

    监控设备包括 GPU、NVMe SSD、RDMA 网卡/HCA、FPGA 加速卡、PCIe Switch 等。

    字段名 含义
    dev_name PCIe 设备名称(BDF 格式),如 "0000:03:00.0",分别对应 Domain:Bus:Device.Function。
    err_type 错误严重级别(Corrected / Uncorrected / Fatal)。
    err_reason 具体错误原因描述字符串,由 AER 状态寄存器的比特位解码得出(见下方两张表)。
    tlp_header 触发错误的 TLP(Transaction Layer Packet)头部四元组(格式:{dword0, dword1, dword2, dword3},十六进制)。TLP 头部包含事务类型、地址、请求者 ID 等信息,是定位错误根因的关键数据。若 TlpHeaderValid=0 则显示 "not available"。
  • PCIe 可纠正错误类型

    位掩码 含义
    0x00000001 接收端错误。物理层接收到不符合规范的数据符号,通常由信号完整性问题引起(如过长连线、阻抗不匹配)。
    0x00000040 TLP(事务层数据包)错误。数据包的 LCRC(链路层 CRC)校验失败,表明事务层数据在传输中发生翻转,PCIe 链路层会自动重传该 TLP。
    0x00000080 DLLP(数据链路层数据包)错误。链路层控制包(如 ACK/NAK、流控更新)CRC 校验失败。
    0x00000100 重传序列号溢出。REPLAY_NUM 字段用于追踪重传次数,该错误表明自上次 ACK 以来已发生过多次重传,通常意味着链路质量持续较差。
    0x00001000 重传计时器超时。发送方在规定时间内未收到 ACK,触发 TLP 重传。持续出现表明链路延迟异常或接收端处理能力不足。
    0x00002000 顾问性非致命错误。本质上是一个不可纠正但被软件降级为可纠正处理的错误(需启用 AER capability 中的 ANFE 功能),常见于接收到 Unsupported Request Completion 的场景。
    0x00004000 已纠正内部错误。设备内部 ECC 或奇偶校验错误,已由设备自主纠正。
    0x00008000 头部日志溢出。AER 头部日志寄存器已满,后续错误的 TLP 头部无法被记录(但错误本身仍被计数)。
  • PCIe 不可纠正错误类型

    位掩码 含义
    0x00000001 未定义错误。保留位被置位,通常表明固件或硬件存在不合规行为。
    0x00000010 数据链路协议错误。收到了违反 DLLP 协议规范的数据包,属于严重的链路层故障。
    0x00000020 意外下线错误。物理链路在未经 Hot-Plug 通知的情况下突然断开(如设备意外掉电或接触不良),为热插拔场景下的高危错误。
    0x00001000 毒化 TLP。接收到数据有效位(EP,Error Poisoning)被主动设置为 1 的 TLP,表明上游发送方知晓该数据已损坏。此机制用于错误传播和隔离,避免静默数据损坏。
    0x00002000 流控协议错误。接收到违反 PCIe 流控信用(Credit)规则的数据包,属于严重的协议违规。
    0x00004000 完成超时。请求方(Requester)发出非 Posted 事务(如 Memory Read)后,在规定超时时间内未收到完成包(Completion)。常见于 NVMe 盘固件异常、RDMA 网卡驱动 Bug 或 PCIe 链路中断。
    0x00008000 完成方中止。接收端显式返回 CA(Completer Abort)状态的 Completion,表示请求被完成方拒绝。
    0x00010000 意外完成包。收到了无法与任何已发出的请求匹配的 Completion(Tag 不匹配),通常由设备固件 Bug 或数据路径错误引起。
    0x00020000 接收缓冲区溢出。接收端流控信用信息显示其缓冲区未满,但实际发生了溢出,属于严重的流控违规。
    0x00040000 格式错误的 TLP。数据包头部字段违反规范(如非法长度、保留字段被置位、不合法的地址范围),通常表明设备固件存在严重缺陷。
    0x00080000 端到端 CRC 错误。TLP 尾部的 ECRC 校验失败(需双端设备均支持 ECRC 功能),表明数据在整个传输链路(含 PCIe Switch 内部交换)中发生损坏,是高可靠性场景中的关键指标。
    0x00100000 不支持的请求错误。接收端返回 UR(Unsupported Request)状态,表明请求的事务类型或地址范围不被该设备支持。
    0x00200000 ACS(Access Control Services)违规。PCIe ACS 机制用于防止 PCIe 设备之间的对等(Peer-to-Peer)DMA 绕过 IOMMU,此错误表明发生了违反 ACS 策略的数据访问,在虚拟化安全场景中需重点关注。
    0x00400000 不可纠正的内部错误。设备内部发生无法自行纠正的 ECC 或奇偶校验错误(如 SRAM 双比特错误),通常意味着设备硬件损坏。
    0x00800000 多播(MC)TLP 被阻断。PCIe 多播(Multicast)TLP 被 ACS 或 MC 控制机制阻止。
    0x01000000 原子操作出口被阻断。AtomicOp(原子操作请求,如 FetchAdd、Swap、CAS)因 ACS 控制被阻止出站,常见于 RDMA/GPU 直连场景。
    0x02000000 TLP 前缀被阻断。带有 End-End TLP Prefix 的数据包被 ACS 或其他机制阻止转发。

总结

推荐在生产环境中部署华佗,实现全面的硬件错误监控与主动运维。

6 - 应用实践

6.1 - 存储服务

📖 概述

HUATUO(华佗)支持将采集到的 Linux 内核事件与 AutoTracing 数据持久化写入外部存储后端。当前支持 Elasticsearch 和 OpenSearch 两种存储系统。

采集到的事件在序列化为 JSON 后,同时写入节点本地目录(huatuo-local/)和配置的远端存储后端。本地目录保留事件的本地副本,远端存储提供持久化与结构化查询能力。

本文介绍 Elasticsearch 和 OpenSearch 的配置与验证方法。示例基于 Docker 部署,生产环境只需将地址替换为实际服务地址,配置方式一致。


🎯 应用场景

Kubernetes 云原生故障溯源

容器化环境中,Pod OOM、节点 Hung Task 等内核事件具有短暂性,日志往往在事件发生后被清理。将事件写入 Elasticsearch 或 OpenSearch 后,运维团队可按时间范围查询历史异常时间线,在事后复盘阶段精确定位间歇性故障的根因。

AI 计算集群稳定性审计

GPU 训练集群长期运行过程中,ras 硬件错误、iotracing I/O 延迟等事件的历史分布对容量规划和硬件健康评估至关重要。将采集数据持久化后,可通过聚合查询建立节点稳定性基线,为主动维护提供数据依据。

合规与事件留存

等保合规要求系统异常事件具备可追溯性。将 HUATUO 采集的内核事件写入 OpenSearch 并配置索引生命周期策略,可满足对事件留存周期和查询能力的合规要求。

可观测性平台集成

Elasticsearch 和 OpenSearch 均提供与 Grafana 的原生数据源对接能力。将 HUATUO 事件写入存储后,可在 Grafana 中构建内核事件趋势面板,与应用层指标叠加展示,实现历史数据分析与告警回顾。


💎 价值

维度 仅本地存储 接入外部存储后端
数据持久性 受节点磁盘容量限制,重启后可能丢失 数据持久化至分布式存储,支持长期保留
查询能力 无结构化查询,依赖文件搜索 支持全文检索、字段过滤、时间范围聚合
可视化集成 不支持 可直接对接 Grafana、Kibana 等可视化平台
多节点汇聚 数据分散在各节点本地 集中写入统一存储,支持跨节点查询
合规留存 难以满足留存周期要求 可配置索引生命周期策略,满足合规留存要求

🚀 使用

OpenSearch V2

1. 部署 OpenSearch

docker pull opensearchproject/opensearch:2.6.0
docker run -d --name opensearch --network host \
    -e "discovery.type=single-node" \
    opensearchproject/opensearch:2.6.0

2. 验证服务状态

curl -k -u admin:admin https://localhost:9200

返回示例:

{
  "name" : "22ca72df78c0",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "yxb3foceQVKzXXO6bHpPHQ",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.6.0",
    "build_type" : "tar",
    "build_hash" : "7203a5af21a8a009aece1474446b437a3c674db6",
    "build_date" : "2023-02-24T18:57:04.388618985Z",
    "build_snapshot" : false,
    "lucene_version" : "9.5.0",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

若验证失败,可通过以下命令查看容器日志:

docker logs opensearch

3. 配置 huatuo-bamai

在 huatuo-bamai.conf 中添加以下配置。OpenSearch 容器镜像默认用户名和密码均为 admin。存储配置的详细说明请参见配置指南。

[Storage.Elasticsearch]
    Address = "https://127.0.0.1:9200"
    Index = "huatuo_bamai"
    Username = "admin"
    Password = "admin"

4. 启动 huatuo-bamai

通过 --config-dir 指定配置文件所在目录:

./_output/bin/huatuo-bamai --region dev --config-dir .

当本地存储目录 huatuo-local/ 中生成文件(例如 net_rx_latency)时,说明已成功采集到内核事件。可使用以下命令从 OpenSearch 查询数据:

curl -k -u admin:admin \
    -X GET "https://localhost:9200/huatuo_bamai/_search?pretty" \
    -H "Content-Type: application/json" \
    -d '{"query": {"match_all": {}}}'

返回示例:

{
    "_index" : "huatuo_bamai",
    "_id" : "yjPG_50Bu_OF-hukxKR7",
    "_score" : 1.0,
    "_source" : {
      "hostname" : "hostname",
      "region" : "dev",
      "uploaded_timestamp" : "2026-05-07T00:11:49.753166222Z",
      "tracer_name" : "net_rx_latency",
      "observed_timestamp" : "2026-05-07T00:11:49.753166222Z",
      "tracer_type" : "event",
      "tracer_data" : {
        "comm" : "<nil>",
        "pid" : 0,
        "latency_stage" : "RX_STAGE_NETIF",
        "latency_ms" : 1776078133565,
        "tcp_saddr" : "127.0.0.1",
        "tcp_daddr" : "127.0.0.1",
        "tcp_sport" : 37736,
        "tcp_dport" : 9200,
        "tcp_seq" : 1080592402,
        "tcp_ack_seq" : 2465063876,
        "packet_len_bytes" : 781
      }
    }
}

查看文档记录总数,不查看具体列表。

curl -k -u admin:admin -X GET "https://localhost:9200/huatuo_bamai/_count?pretty"

返回示例:其中 count 数字 = 写入记录的总数。

{
  "count" : 2680,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}

Elasticsearch V8

1. 部署 Elasticsearch

docker pull docker.elastic.co/elasticsearch/elasticsearch:8.15.5
docker run -d --name elasticsearch --network host \
    -e "discovery.type=single-node" \
    -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
    -e "ELASTIC_PASSWORD=123456" \
    docker.elastic.co/elasticsearch/elasticsearch:8.15.5

2. 验证服务状态

curl -k -u elastic:123456 https://localhost:9200

返回示例:

{
  "name" : "ab0b562f8dbd",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "aVfOVgJTQXuhZ3HGotK3ww",
  "version" : {
    "number" : "8.15.5",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "b10896bcfe167cce44a84ba2771d101fb596d40d",
    "build_date" : "2024-11-21T22:06:13.985834967Z",
    "build_snapshot" : false,
    "lucene_version" : "9.11.1",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

3. 配置 huatuo-bamai

在 huatuo-bamai.conf 中添加以下配置。Elasticsearch 容器镜像默认用户名为 elastic,密码通过环境变量 ELASTIC_PASSWORD 设置。存储配置的详细说明请参见配置指南。

[Storage.Elasticsearch]
    Address = "https://127.0.0.1:9200"
    Index = "huatuo_bamai"
    Username = "elastic"
    Password = "123456"

4. 启动 huatuo-bamai

通过 --config-dir 指定配置文件所在目录:

./_output/bin/huatuo-bamai --region dev --config-dir .

当本地存储目录 huatuo-local/ 中生成文件(例如 net_rx_latency)时,说明已成功采集到内核事件。可使用以下命令从 Elasticsearch 查询数据:

curl -k -u elastic:123456 \
    -X GET "https://localhost:9200/huatuo_bamai/_search?pretty" \
    -H "Content-Type: application/json" \
    -d '{"query": {"match_all": {}}}'

返回示例:

{
    "_index" : "huatuo_bamai",
    "_id" : "WtNZAJ4BQ8x-thPHEY1i",
    "_score" : 1.0,
    "_source" : {
      "hostname" : "hostname",
      "region" : "dev",
      "uploaded_timestamp" : "2026-05-07T02:51:37.696263325Z",
      "tracer_name" : "net_rx_latency",
      "observed_timestamp" : "2026-05-07T02:51:37.696263325Z",
      "tracer_type" : "event",
      "tracer_data" : {
        "comm" : "<nil>",
        "pid" : 0,
        "latency_stage" : "RX_STAGE_NETIF",
        "latency_ms" : 1776078133565,
        "tcp_saddr" : "127.0.0.1",
        "tcp_daddr" : "127.0.0.1",
        "tcp_sport" : 2379,
        "tcp_dport" : 36706,
        "tcp_seq" : 950542706,
        "tcp_ack_seq" : 1960972383,
        "packet_len_bytes" : 91
      }
    }
}

查看文档记录总数,不查看具体列表。

curl -k -u elastic:123456 -X GET "https://localhost:9200/huatuo_bamai/_count?pretty"

返回示例:其中 count 数字 = 写入记录的总数。

{
  "count" : 2680,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}

Elasticsearch V7

V7 默认使用 HTTP,因此只需要在访问服务时替换为 HTTP 即可。

1. 部署 Elasticsearch

docker pull docker.elastic.co/elasticsearch/elasticsearch:7.10.1
docker run -d --name elasticsearch --network host \
    -e "discovery.type=single-node" \
    -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
    -e "ELASTIC_PASSWORD=123456" \
    docker.elastic.co/elasticsearch/elasticsearch:7.10.1

2. 验证服务状态

curl -k -u elastic:123456 http://localhost:9200

返回示例:

{
  "name" : "d88c9e8df48b",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "_ZZefWx4SniAc255t_lIVg",
  "version" : {
    "number" : "7.10.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "1c34507e66d7db1211f66f3513706fdf548736aa",
    "build_date" : "2020-12-05T01:00:33.671820Z",
    "build_snapshot" : false,
    "lucene_version" : "8.7.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

3. 配置 huatuo-bamai

[Storage.Elasticsearch]
    Address = "http://127.0.0.1:9200"
    Index = "huatuo_bamai"
    Username = "elastic"
    Password = "123456"

4. 启动 huatuo-bamai

通过 --config-dir 指定配置文件所在目录:

./_output/bin/huatuo-bamai --region dev --config-dir .

当本地存储目录 huatuo-local/ 中生成文件(例如 net_rx_latency)时,说明已成功采集到内核事件。可使用以下命令从 Elasticsearch 查询数据:

curl -k -u elastic:123456 \
    -X GET "http://localhost:9200/huatuo_bamai/_search?pretty" \
    -H "Content-Type: application/json" \
    -d '{"query": {"match_all": {}}}'

或者:
curl -k -u elastic:123456 \
    -X GET "http://localhost:9200/huatuo_bamai/_count?pretty"

⚙️ 原理

系统架构

HUATUO Storage 模块部署在节点上,将采集到的内核事件同时写入本地目录和 Elasticsearch 或 OpenSearch。两种存储后端共用同一套 [Storage.Elasticsearch] 配置接口,通过地址区分。

写入远端时使用 ES/OpenSearch 的 Bulk API(_bulk):事件先进入节点内的批量缓冲,由后台 worker 按"大小或时间"的阈值聚合后一次提交多条记录,并在传输层失败时按策略自动重试。

graph TB
    subgraph kernel["Linux 内核"]
        K1[内核事件]
        K2[AutoTracing]
    end

    subgraph huatuo["HUATUO Agent(节点级)"]
        T["采集层"]
        L["本地目录\nhuatuo-local/"]
        S["Storage 模块\nBulkIndexer 缓冲"]
    end

    subgraph backends["存储后端"]
        ES[Elasticsearch]
        OS[OpenSearch]
    end

    kernel --> T
    T --> L
    T --> S
    S -->|Bulk API + 自动重试| ES
    S -->|Bulk API + 自动重试| OS

过滤条件语义

内部存储查询 API 使用 AND 组合过滤条件。SQLite 和 Elasticsearch 对非 null 标量值采用以下约定:

操作符 含义
OpEq、OpNe 相等/不等;缺失和 null 值均不匹配
OpGt、OpGte、OpLt、OpLte 严格/包含边界的范围比较
OpIn 属于非空切片或数组中的标量值
OpExists、OpNotExists 存在/不存在非 null 的可查询值;不设置 Value

空字符串是字面值:OpEq("") 匹配空字符串;OpNe("") 要求字符串存在、非 null 且非空。查询缺失字段应使用 OpNotExists,OpEq("") 不再表示字段缺失。 主机 profiling 查询改用 container_hostname 的 OpNotExists,保留原有筛选行为。

比较值支持 Go 内建字符串、布尔、整数及有限浮点数,范围比较不接受布尔值。 time.Time 和 timeutil.Timestamp 统一转为保留九位小数的 UTC 字符串, OpIn 元素也采用相同规则。nil、指针、对象、嵌套集合及非有限数值返回 driver.ErrInvalidQuery。整数范围边界保留原始 JSON 精度,实际支持的数值范围 仍受后端和字段映射约束。

Elasticsearch 调用方必须指定实际用于精确比较的索引字段:keyword 映射使用 原字段;text 映射使用明确配置的 keyword 子字段,例如 hostname.keyword。 后端不会猜测子字段,也不会将 text 与 keyword 查询进行 OR 组合。 OpNe 对同一字段执行 exists 和 must_not term。全文匹配和数组整体相等 不属于这里的标量比较约定。

Elasticsearch 的存在性判断针对索引值,而不是 _source 中是否包含键。 ignore_above、null_value 和 normalizer 等映射设置可能改变存在性或相等行为。 要求与 SQLite 一致时,应使用保留目标标量语义的映射。日期字段不能与空字符串 比较;表达“已有日期值”时应使用存在性操作符。

数据写入流程

采集层调用 Save 后立即返回,事件落入 BulkIndexer 缓冲;后台 worker 在满足"字节阈值 / 时间阈值 / 进程退出"任一条件时将批次提交至远端。本地目录写入是同步落盘,与远端 Bulk 路径相互独立。

sequenceDiagram
    participant T as 采集层
    participant L as 本地目录(huatuo-local/)
    participant S as Storage 模块(BulkIndexer)
    participant B as ES / OpenSearch

    T->>S: 采集到内核事件,序列化为 JSON
    par 本地路径(同步)
        S->>L: 写入本地文件
    and 远端路径(异步批量)
        S->>S: 加入 Bulk 缓冲,立即返回
        Note over S: 满足 5 MB / 1 s / 退出 任一条件
        S->>B: POST /_bulk(多条记录)
        B-->>S: 200 OK + per-item 结果
        Note over S: 失败项通过 OnFailure 回调记录日志
    end

Bulk 批量写入机制

缓冲与刷新

参数 值 含义
FlushBytes 5 MB 缓冲累计达到该字节数立即刷新
FlushInterval 1 s 距上次刷新满 1 秒后强制刷新
NumWorkers 4 并发提交 Bulk 请求的后台 goroutine 数
进程退出 Close(ctx) SIGTERM/SIGINT 触发,限时 10 s 内排空缓冲

两级重试策略

Bulk 请求的失败语义分为两层,重试范围不同:

层级 触发条件 处理方式 是否重试
整批失败 传输错误(连接失败、超时、TLS)
HTTP 状态:429 / 502 / 503 / 504
客户端按指数退避自动重试:100 ms → 200 ms → 400 ms → 800 ms,最多 3 次 ✅ 自动
整批拒绝 HTTP 状态:400 / 401 / 403 / 404 / 413 等 不重试,整批所有记录全部丢弃,并通过 OnError 写错误日志 ❌ 丢弃
单条失败 200 OK 但 per-item 失败:版本冲突、字段映射错误、文档过大 不重试,仅该单条丢弃,通过 OnFailure 回调记录 index/id/status/type/reason ❌ 丢弃
单条成功 200 OK 且 per-item 成功 主分片写入成功;副本失败时记录包含索引、文档 ID 和失败分片数的警告日志 —

为什么这样设计:429/5xx 与传输错误是远端短暂不可用的信号,重试有效;4xx(除 429)与 per-item 错误是客户端语义问题(数据格式、权限),重试只会放大错误,应交给开发与运维侧排查日志后修正。

分批读取

Query(ctx, query, consume) 按顺序同步调用回调,每批至少一条记录。Limit 是跳过 Offset 条匹配记录后交给回调的总条数上限。storage.Store.Query 统一校验 Limit 必须大于零、Offset 和 BatchSize 非负;后端只消费已校验的参数。 BatchSize 控制每批条数,零使用统一默认值 100,每批不超过剩余总限额。 需要数据的调用方必须明确传入正数总上限。Count 和 Values 只使用查询中的 Filters,忽略 Sorts、Limit、Offset 和 BatchSize,包括零值及负数分页参数。 Count 统计全部匹配记录;Values 使用独立的 size 参数,由 storage.Store.Values 在调用后端前校验其非负约束,后端不再重复校验这两个操作的分页参数或 size。 本地文件存储仍不支持查询。

SQLite 使用一条带 LIMIT/OFFSET 的 SELECT 逐行读取。回调期间查询占用唯一数据库 连接,不能同步再次访问同一后端。任务恢复按 ID 升序每批读取 1000 个 Job,读取关闭后 再启动监督任务,通过 ID 游标继续查询,避免任务状态变化导致偏移分页漏读。 Elasticsearch 7/8 和 OpenSearch 统一使用 Scroll 快照,支持跨越结果窗口的读取和偏移。 深偏移仍需读取并跳过前面的记录,不同 API 请求不共享快照。

各批次独立持有数据。回调、解码、后端错误或取消都会立即终止查询,已执行回调的效果 不会回滚。Scroll 使用独立的有界上下文清理,即使原查询已取消也会尝试释放资源。 查询不自动重试,因为丢失响应时 Scroll 游标可能已经前进;写入重试策略保持不变。

API 仅在未传 limit 时填默认值:Job 列表默认 100、最大 1000;原始 Profile 每页默认 20、最大 100,并保留 64 MiB 响应限制。显式零值、负数或超过上限的 limit 返回参数 错误。每页额外读取一条判断 has_more。

火焰图边读取边合并 Profile,每次查询总上限为 100000 个窗口。额外读取一条检测超限, 超限时返回错误,提示缩小时间范围或过滤条件;读取或合并失败都不返回部分火焰图。 输入批次可随处理进度释放,但合并结果的内存仍随不同调用栈数量增长。

响应错误

HTTP 成功不代表结果完整。Query 和 Values 在搜索超时或分片失败时返回错误, Query 不交付失败批次,但此前已消费的批次不会回滚;Values 不返回部分结果。 Count 在分片失败时返回零和错误。搜索超时优先于分片失败返回。 分片错误包含失败分片总数,以及首条失败的类型、原因及嵌套原因(如果存在)。

收到成功 HTTP 响应后,DeleteByQuery 按超时、执行失败的顺序检查, 遇到首个错误即返回,同时保留响应中的已删除数量。 索引不存在时返回零且不报错,其他 HTTP 错误返回零和错误。 已完成的删除不会回滚。 直接写入和删除在副本失败时保留主分片操作成功的语义, 通过警告日志记录可用的失败详情。Bulk 写入也会报告副本失败,不重试已经成功的主分片写入。

HTTP 错误包含响应状态码和可用的 Elasticsearch 错误详情。无法识别或过大的错误响应体 以原文报告,限制为 4 KiB,并在截断时追加标记。

数据丢失场景

下列三种情况下,调用方调用 Save 时返回 nil,但事件最终未进入索引:

  1. 进程异常退出:SIGKILL 或宿主机断电时,BulkIndexer 内存缓冲尚未刷新的部分直接丢失(仅本地目录保留副本)。
    • 缓解:SIGTERM/SIGINT 走优雅退出路径,shutdown 时调用 Close 强制 flush,最长等待 10 秒。
  2. 整批被永久拒绝:4xx(非 429)类错误一次性丢弃整批所有记录。常见诱因:索引被禁用、密码失效、单条文档超过集群 http.max_content_length。
    • 排查:OnError 错误日志包含 ES 返回的 type 与 reason。
  3. 单条永久失败:mapping 冲突、版本冲突、文档语法错误。
    • 排查:OnFailure 错误日志按 index/id 定位失败记录。

本地目录始终保留副本:即使远端写入丢失,事件仍可从 huatuo-local/ 中找回,作为最终一致性的兜底。

解决的问题

将"逐事件 Index API"换成"BulkIndexer 批量 + 自动重试"主要解决以下四类问题:

问题 旧方案瓶颈 Bulk 方案的改进
TLS 握手 CPU 开销 每事件一次 HTTPS,握手在 FIPS / RSA-PSS 下占满 CPU 多条事件复用单连接 + 单次握手;TLS PSK ticket 缓存复用
远端 RTT 与吞吐 每事件一次往返,节点级写入受 RTT 限制 单次 Bulk 请求最多 5 MB,吞吐随批大小线性提升
远端短暂抖动 / 限流(429) 单次失败立即丢弃,无重试 客户端层面自动重试,吸收瞬态故障
采集层对存储后端解耦 远端慢会回压采集,影响内核事件采集时延 异步缓冲将采集与远端写入解耦,采集路径不被网络阻塞

🌟 结尾

内核观测时间

事件文档可包含顶层 kernel_observed_timestamp,其值为 UTC 日期时间。 observed_timestamp 是用户态观测时刻,uploaded_timestamp 是存储写入时刻。 三者不互相补填。旧文档及没有内核时间的事件省略新增字段;历史数据不自动迁移。 原始 kernel_observed_ns 仅用于进程内事件关联,不写入新文档。

6.2 - 数据源配置

HUATUO 通过 Prometheus 采集指标,并将事件数据写入 Elasticsearch。本文介绍 Docker Compose 和 Kubernetes 环境下的数据源与仪表盘配置方法。

不要在同一节点同时运行 Docker Compose、systemd 和 Kubernetes 版本的 huatuo-bamai。它们会争用 19704 端口和 /var/run/huatuo-toolstream.sock。

Docker Compose

build/docker/ 提供已配置完成的 Prometheus、Elasticsearch、Grafana 和 HUATUO 组件。

export ELASTIC_PASSWORD=huatuo-bamai
docker compose --project-directory ./build/docker up -d

服务使用宿主机网络:

服务 地址 用途
Elasticsearch http://localhost:9200 事件存储
Prometheus http://localhost:9090 指标采集
Grafana http://localhost:3000 可视化
huatuo-bamai http://localhost:19704 指标和事件采集

Grafana 默认账号为 admin/admin。首次登录后应立即修改密码。

已预配置的数据源

数据源由 build/docker/grafana/datasources/ 自动加载:

名称 类型 UID
huatuo-bamai-prom Prometheus huatuo-bamai-prom
huatuo-bamai-es Elasticsearch huatuo-bamai-es
huatuo-bamai-infinity Infinity huatuo-bamai-infinity-auto-flamegraph

已预配置的仪表盘

仪表盘由 build/docker/grafana/dashboards/ 自动加载:

  • Metric 大盘(宿主机)
  • Metric 大盘(容器)
  • HuaTuo 根因定位 AutoTracing
  • Continuous Profiling(宿主机)
  • Continuous Profiling(容器)
  • AutoTracing Flame Redirect

使用 Docker Compose 时不需要手动配置数据源或导入仪表盘。

Kubernetes

Prometheus 可以通过 Pod 注解或 ServiceMonitor 采集 HUATUO 指标。两种方式选择一种即可,避免重复采集。

Pod 注解

build/huatuo-daemonset.minimal.yaml 默认包含以下注解:

template:
  metadata:
    annotations:
      prometheus.io/scrape: "true"
      prometheus.io/port: "19704"
      prometheus.io/path: "/metrics"

Helm Chart 根据 charts/huatuo/values.yaml 中的以下配置生成注解:

metrics:
  enabled: true
  port: 19704
  path: /metrics

Prometheus 需要启用 Kubernetes Pod 服务发现,并通过 relabel 读取注解:

scrape_configs:
  - job_name: k8s-pods
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: "true"
      - source_labels:
          [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        regex: (.+)
        target_label: __metrics_path__
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: kubernetes_pod_name

ServiceMonitor

ServiceMonitor 仅适用于安装了 Prometheus Operator 和 ServiceMonitor CRD 的集群。以下示例假设 Helm release 名和命名空间均为 huatuo。如果实际名称不同,需要同步修改命名空间和 app.kubernetes.io/instance 标签。

创建 huatuo-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: huatuo
  namespace: huatuo
  labels:
    app.kubernetes.io/name: huatuo
    app.kubernetes.io/instance: huatuo
spec:
  clusterIP: None
  selector:
    app.kubernetes.io/name: huatuo
    app.kubernetes.io/instance: huatuo
  ports:
    - name: metrics
      port: 19704
      targetPort: 19704
      protocol: TCP

创建 huatuo-servicemonitor.yaml:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: huatuo
  namespace: huatuo
  labels:
    release: prometheus
spec:
  namespaceSelector:
    matchNames:
      - huatuo
  selector:
    matchLabels:
      app.kubernetes.io/name: huatuo
      app.kubernetes.io/instance: huatuo
  endpoints:
    - port: metrics
      path: /metrics
      interval: 30s
      scrapeTimeout: 10s

release: prometheus 必须匹配 Prometheus 实例的 spec.serviceMonitorSelector。不同 Prometheus Operator 安装方式可能使用不同标签。

如果使用 build/huatuo-daemonset.minimal.yaml,Pod 标签是 app: huatuo,需要将 Service 的 spec.selector 改为:

selector:
  app: huatuo

ServiceMonitor 选择的是 Service 的 metadata.labels,不是 Pod 标签。

应用配置:

kubectl apply -f huatuo-service.yaml
kubectl apply -f huatuo-servicemonitor.yaml

Grafana 数据源

不使用 Docker Compose 时,在 Grafana 中手动添加数据源。

Prometheus

  • URL:http://<prometheus-host>:9090
  • Access:Server (proxy)
  • UID:huatuo-bamai-prom

Elasticsearch

  • URL:http://<elasticsearch-host>:9200
  • Authentication:Basic Authentication
  • Username:elastic
  • Password:<password>
  • Index name:huatuo_bamai*
  • Time field name:uploaded_timestamp
  • UID:huatuo-bamai-es

Provisioning 配置示例见 build/docker/grafana/datasources/。

导入仪表盘

Docker Compose 会自动加载仓库内置仪表盘。外部 Grafana 按以下步骤导入:

  1. 打开 Grafana 的 Dashboards -> Import
  2. 上传或粘贴 build/docker/grafana/dashboards/*.json
  3. 点击 Load
  4. 选择 huatuo-bamai-prom 和 huatuo-bamai-es 数据源
  5. 点击 Import

也可以从 HUATUO 控制台导出仪表盘:

  1. 访问 http://console.huatuo.tech/dashboards
  2. 登录并选择仪表盘
  3. 点击 Export -> Export as JSON
  4. 勾选 “Export the dashboard to use in another instance”
  5. 复制 JSON 并导入目标 Grafana

控制台登录凭证由部署管理员提供,不应保存在公开文档中。

6.3 - 内核事件订阅

📖 概述

/v1/events/watch 是华佗(HUATUO)提供的实时内核事件订阅接口。客户端通过一次 HTTP POST 长连接即可持续接收节点上发生的内核异常事件。事件以 CloudEvents 1.0 规范封装,通过 Server-Sent Events(SSE) 协议推送。


🎯 应用场景

内核事件订阅将操作系统层的异常信号直接暴露给上层系统,消除了传统轮询带来的延迟与开销。以下是典型的集成场景。

故障自愈系统

内核事件是自愈决策的第一手信号源。订阅 events/watch 后,自愈控制器可在事件发生的瞬间触发处置动作,而不必等待监控系统的告警流转:

  • OOM 自愈:收到 memory_oom_kill 事件后,立即对触发容器执行扩容、重启或流量摘除,将服务中断时间从分钟级压缩到秒级。
  • Hung Task 自愈:收到 hungtask 事件后,自动隔离节点并驱逐 Pod,防止级联阻塞蔓延至整个集群。
  • 网络故障自愈:收到 netdev_txqueue_timeout 或 netdev_bonding_lacp 事件后,触发网卡重置或流量切换,实现分钟级网络链路自愈。
  • I/O 风暴自愈:收到 iotracing 事件后,结合 cgroup blkio 限速策略动态降低问题容器的磁盘 I/O 配额,保护同节点其他服务。

可观测性平台

将华佗内核事件接入可观测性平台,补齐应用指标和日志之外的内核视角:

  • 事件时间线关联:将 softlockup、memory_oom_kill 等内核事件叠加到 Grafana 时间线上,与应用错误率、延迟曲线精确对齐,快速定位根因。
  • 异常驱动告警:以内核事件替代固定阈值告警,降低误报率。例如收到 ras 硬件错误事件时直接触发高优告警,而不依赖 CPU 错误率超阈值。
  • 容量与稳定性分析:长期订阅 memburst、dload 等 AutoTracing 事件,建立节点稳定性基线,为容量规划提供内核级依据。
  • 多维下钻:事件中携带容器 ID、命名空间、地域等上下文,告警链接可直接下钻到对应的 Pod、Node、Region 视图。

安全审计与合规

  • 异常行为检测:memory_oom_kill、hungtask、softlockup 等事件若在非业务高峰期集中出现,可能指示资源滥用或恶意负载,触发安全审查流程。
  • 事件留存与追溯:将 CloudEvents 事件流写入消息队列(Kafka、Pulsar)或对象存储,满足等保合规对系统异常事件留存的要求。

混沌工程与压测验证

  • 故障注入验证:混沌工程平台注入网络延迟、内存压力等故障后,实时订阅 net_rx_latency、memburst 事件验证故障是否生效,取代人工观察。
  • 压测基线建立:压测期间持续订阅全量事件,记录首个内核异常事件的出现时机,精确标定系统承压极限。

AIOps 智能运维

  • 事件驱动根因分析:将内核事件作为特征输入 AI/ML 模型,结合应用指标进行多维根因推断,减少人工排查时间。
  • 预测性维护:对 ras 硬件错误、netdev_bonding_lacp 等硬件层事件建模,在设备彻底失效前提前预警并触发迁移。
  • 智能抑制与聚合:对同一时间窗口内同类事件自动聚合,避免告警风暴,向 On-call 工程师呈现精简的根因摘要。

💎 价值

维度 传统方案 接入华佗 events/watch
时效性 告警触发延迟 1–5 分钟 内核事件实时推送,延迟 < 1 秒
信号准确性 基于指标阈值,误报率高 事件源自内核判定,误报率接近零
上下文丰富度 指标维度有限 携带容器、节点、地域等完整上下文
集成成本 需自建 eBPF 采集或依赖第三方 Agent 一次 HTTP POST 即可订阅,标准 CloudEvents 格式
协议兼容性 各厂商私有格式 遵循 CloudEvents 1.0 标准,可接入任意兼容平台

🚀 使用

1. CloudEvents 规范说明

1.1 CloudEvents 1.0 信封字段

每条推送事件均为一个符合 CloudEvents 1.0 规范的 JSON 对象:

字段 类型 说明
specversion string 固定值 "1.0"
id string 事件唯一标识符(UUID v4),每条事件独立生成
source string 事件来源路径,格式 /huatuo/{hostname}/{tracer_name}
type string 固定值 "tech.huatuo.kernel.event"
datacontenttype string 固定值 "application/json"
time string 事件采集时间(RFC 3339 纳秒精度,UTC)
data object 事件数据体,即 WatchEventData 结构体

1.2 华佗事件数据结构(WatchEventData)

data 字段包含华佗的标准事件记录:

{
  "specversion": "1.0",
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "source": "/huatuo/node-1/memory_oom_kill",
  "type": "tech.huatuo.kernel.event",
  "datacontenttype": "application/json",
  "time": "2026-05-18T10:23:45.123456789Z",
  "data": {
    "hostname": "node-1",
    "region": "cn-beijing",
    "observed_timestamp": "2026-05-18T10:23:45Z",
    "tracer_name": "memory_oom_kill",
    "tracer_id": "abc123",
    "tracer_run_type": "auto",
    "container_id": "d3f1a2b4c5e6",
    "container_hostname": "app-pod",
    "container_host_namespace": "prod",
    "container_type": "docker",
    "container_qos": "guaranteed"
  }
}

WatchEventData 字段说明:

字段 类型 说明
hostname string 节点主机名
region string 节点所在地域
observed_timestamp string 事件生产者在用户态观测事件的 UTC 时间
kernel_observed_timestamp string 可选;内核观测事件的 UTC 时间
tracer_name string 触发事件的采集器名称(见下文内核事件列表)
tracer_id string 事件实例唯一 ID
tracer_run_type string 采集模式,auto(自动触发)或 manual
container_id string 容器 ID(容器级事件时存在)
container_hostname string 容器主机名
container_host_namespace string 容器所在命名空间
container_type string 容器运行时类型(docker / containerd 等)
container_qos string 容器 QoS 等级

2. 支持的内核事件列表

tracer_name 说明
memory_oom_kill 内存不足(OOM Killer)触发事件
hungtask 内核任务长时间 D 状态(Hung Task)检测
softlockup CPU 软锁死(Soft Lockup)检测
ras 硬件可靠性(RAS)错误,如 ECC 内存错误
dropwatch 内核网络数据包丢弃(Drop Watch)事件
netdev_events 网络设备状态变更事件(Link Up/Down 等)
netdev_txqueue_timeout 网络设备发送队列超时事件
netdev_bonding_lacp Bond 设备 LACP 协议异常事件
net_rx_latency 网络接收延迟异常事件
sched_tick 调度 tick 间隔异常追踪事件
memory_reclaim_events 内存回收异常事件
cpuidle CPU 空闲率异常(AutoTracing 自动触发)
cpusys CPU 系统态占用率异常(AutoTracing 自动触发)
dload 系统负载异常(AutoTracing 自动触发)
iotracing I/O 延迟异常(AutoTracing 自动触发)
memburst 内存突增异常(AutoTracing 自动触发)

3. POST 请求说明

3.1 接口地址

POST /v1/events/watch

3.2 请求头

Authorization: Bearer <node-token>
Content-Type: application/json

3.3 请求体结构

{
  "filters": {
    "tracer_name": "<regex>",
    "hostname": "<regex>",
    "container_hostname": "<regex>",
    "container_host_namespace": "<regex>",
    "container_qos": "<regex>",
    "region": "<regex>"
  }
}

filters 字段说明:

字段 类型 是否必填 说明
tracer_name string 否 按采集器名称过滤,支持正则表达式
hostname string 否 按节点主机名过滤,支持正则表达式
container_hostname string 否 按容器主机名过滤,支持正则表达式
container_host_namespace string 否 按容器命名空间过滤,支持正则表达式
container_qos string 否 按容器 QoS 过滤,支持正则表达式
region string 否 按地域过滤,支持正则表达式
  • 所有过滤字段均为可选;省略或留空表示匹配所有值。
  • 多个字段同时指定时,所有条件须同时满足(AND 语义)。
  • 过滤器在服务端生效,仅匹配的事件才会推送到客户端。

3.4 响应格式(SSE 流)

连接建立后,服务端以 SSE 格式持续推送事件:

data: {"specversion":"1.0","id":"...","source":"/huatuo/node-1/memory_oom_kill",...}\n\n

服务端还会定期发送心跳注释行以保持连接:

: ping\n

4. HTTP 服务事件流配置

在华佗配置文件的 [HTTPServer] 段配置事件流参数:

[HTTPServer]
    # 最大并发客户端连接数,超出后新连接返回 HTTP 429
    # Default: 100
    MaxEventStreamClients = 100

    # SSE 心跳间隔(秒),防止代理/负载均衡因空闲而断开连接
    # 连续 3 次心跳写入失败则主动关闭该客户端连接
    # Default: 30
    EventStreamKeepAliveIntervalSeconds = 30
配置项 默认值 说明
MaxEventStreamClients 100 /v1/events/watch 长连接上限,超出返回 HTTP 429
EventStreamKeepAliveIntervalSeconds 30 心跳间隔,应小于上游代理的 idle timeout

5. Curl 调用示例

5.1 订阅所有内核事件

curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
  -H "Authorization: Bearer <node-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "Cache-Control: no-cache" \
  -H "Connection: keep-alive" \
  -d '{}'

5.2 只订阅 OOM 事件

curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
  -H "Authorization: Bearer <node-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "Cache-Control: no-cache" \
  -H "Connection: keep-alive" \
  -d '{"filters": {"tracer_name": "^memory_oom_kill$"}}'

5.3 订阅指定节点的网络类事件

curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
  -H "Authorization: Bearer <node-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "Cache-Control: no-cache" \
  -H "Connection: keep-alive" \
  -d '{
    "filters": {
      "hostname": "^node-1$",
      "tracer_name": "netdev|dropwatch|net_rx_latency"
    }
  }'

5.4 订阅 prod 命名空间的容器事件

curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
  -H "Authorization: Bearer <node-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "Cache-Control: no-cache" \
  -H "Connection: keep-alive" \
  -d '{
    "filters": {
      "container_host_namespace": "^prod$"
    }
  }'

说明: -N 参数禁用 curl 缓冲,使 SSE 事件即时输出到终端。


6. 生成的 Go 客户端示例

POST /v1/events/watch 已纳入 Node OpenAPI 契约。以下示例直接使用生成的 请求类型、事件类型和客户端。

package main

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"os"
	"strings"
	"time"

	nodeapi "github.com/ccfos/huatuo/apis/v1/node"
)

func watchEvents(
	ctx context.Context,
	baseURL string,
	token string,
	filters nodeapi.WatchEventFilters,
) error {
	client, err := nodeapi.NewClient(
		baseURL,
		nodeapi.WithHTTPClient(&http.Client{}),
		nodeapi.WithRequestEditorFn(func(_ context.Context, request *http.Request) error {
			request.Header.Set("Authorization", "Bearer "+token)
			request.Header.Set("Accept", "text/event-stream")
			return nil
		}),
	)
	if err != nil {
		return fmt.Errorf("create Node API client: %w", err)
	}
	resp, err := client.WatchEvents(ctx, nodeapi.WatchEventsJSONRequestBody{
		Filters: &filters,
	})
	if err != nil {
		return fmt.Errorf("connect: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status: %d", resp.StatusCode)
	}

	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		line := scanner.Text()

		// 跳过心跳注释行和空行
		if line == "" || strings.HasPrefix(line, ":") {
			continue
		}

		// SSE data 行格式:`data: <json>`
		data, ok := strings.CutPrefix(line, "data: ")
		if !ok {
			continue
		}

		var event nodeapi.WatchEvent
		if err := json.Unmarshal([]byte(data), &event); err != nil {
			slog.Warn("parse event", "err", err)
			continue
		}

		fmt.Printf(
			"[%s] source=%s id=%s\n",
			event.Time.Format(time.RFC3339Nano),
			event.Source,
			event.ID.String(),
		)
		fmt.Printf("  data: %+v\n", event.Data)
	}

	return scanner.Err()
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	defer cancel()

	tracerName := "memory_oom_kill|hungtask|softlockup"
	err := watchEvents(ctx, "http://192.168.1.10:19704", "node-token", nodeapi.WatchEventFilters{
		TracerName: &tracerName,
	})
	if err != nil {
		slog.Error("watch events", "err", err)
		os.Exit(1)
	}
}

6.1 流式客户端选择

应使用生成的 Client.WatchEvents 方法直接获取 http.Response。不要对该接口使用 ClientWithResponses.WatchEventsWithResponse:后者会读取响应体直到 EOF,而 SSE 连接通常会持续到调用方取消 context。

6.2 重连机制建议

生产环境中,网络抖动或服务重启会导致连接断开,建议加入指数退避重连逻辑:

func watchWithRetry(
	ctx context.Context,
	baseURL string,
	token string,
	filters nodeapi.WatchEventFilters,
) {
	backoff := time.Second
	for {
		if err := watchEvents(ctx, baseURL, token, filters); err != nil {
			if ctx.Err() != nil {
				return
			}
			slog.Warn("disconnected, retrying", "err", err, "backoff", backoff)
			// time.NewTimer + Stop 确保 context 取消时计时器资源立即释放
			timer := time.NewTimer(backoff)
			select {
			case <-ctx.Done():
				timer.Stop()
				return
			case <-timer.C:
			}
			if backoff < 30*time.Second {
				backoff *= 2
			}
		}
	}
}

⚙️ 原理

系统架构

HUATUO Agent 部署在每个节点上,通过 eBPF、Kprobe、Tracepoint 等机制挂钩内核关键路径,将内核异常事件采集后经过滤、封装,以 SSE 长连接推送给多个并发订阅客户端。

graph TB
    subgraph kernel["Linux 内核"]
        K1[OOM Killer]
        K2[Hung Task 检测]
        K3[Soft Lockup 检测]
        K4[RAS 硬件错误]
        K5[网络子系统]
        K6[AutoTracing]
    end

    subgraph huatuo["HUATUO Agent(节点级)"]
        T["Tracer 采集层\neBPF / Kprobe / Tracepoint"]
        F["过滤器\nhostname / tracer / namespace / region"]
        CE["CloudEvents 1.0 封装\nid / source / time / data"]
        EW["EventsWatch 分发层\nSSE 长连接管理"]
    end

    subgraph clients["订阅客户端"]
        C1[故障自愈系统]
        C2[可观测性平台]
        C3[AIOps 系统]
        C4[安全审计系统]
    end

    kernel --> T
    T --> F
    F --> CE
    CE --> EW
    EW -->|SSE 推送| C1
    EW -->|SSE 推送| C2
    EW -->|SSE 推送| C3
    EW -->|SSE 推送| C4

事件采集与推送原理

客户端发起 POST 请求后,连接保持打开状态。内核每次触发异常事件,HUATUO Agent 完成过滤和封装后立即将事件写入所有匹配的 SSE 流,无需客户端轮询。

sequenceDiagram
    participant C as 客户端
    participant EW as EventsWatch
    participant T as Tracer 采集层
    participant K as Linux 内核

    C->>EW: POST /v1/events/watch {"filters": {...}}
    EW-->>C: 200 OK (Content-Type: text/event-stream)

    loop SSE 长连接持续推送
        K->>T: 内核事件触发(memory_oom_kill / hungtask / softlockup ...)
        T->>EW: 上报原始事件
        EW->>EW: 过滤器匹配
        alt 匹配成功
            EW-->>C: data: {CloudEvents JSON}\n\n
        else 不匹配
            note over EW: 丢弃,不推送
        end
        EW-->>C: : ping(按配置间隔发送心跳)
    end

事件处理流程

从内核事件产生到推送至客户端,经过采集、过滤、封装三个阶段,整体链路延迟小于 1 秒。

flowchart LR
    A([内核异常触发]) --> B["Tracer 采集\neBPF / Kprobe"]
    B --> C{过滤器匹配?}
    C -- 否 --> D([丢弃])
    C -- 是 --> E["封装 CloudEvents 1.0\nid / source / time / data"]
    E --> F[写入 SSE 流]
    F --> G([推送至订阅客户端])

🌟 结尾

6.4 - 性能剖析

火焰图格式

在性能剖析领域,collapsed 和 flamegraph 是最常用的两种火焰图格式,分别对应"原始数据"与"可视化视图"两个层次。

Collapsed 格式

标准语法与格式

collapsed 格式(又称 folded stacks)由 Brendan Gregg 定义,是火焰图的原始文本输入格式。每行代表一条唯一的调用栈及其采样计数。

基本规则:

frame1;frame2;frame3;...;frameN COUNT
组成部分 说明
frame1 栈底(入口/根帧),如 main、start_thread
; 帧分隔符(分号)
frameN 栈顶(当前执行帧,即采样命中点)
COUNT 采样次数(整数),与栈帧之间用空格分隔

格式要点:

  • 每行一条独立调用栈,相同栈路径的样本合并计数
  • 帧的排列顺序:从左到右为 根→叶(调用链方向)
  • 空行及 # 开头的行通常被视为注释,解析时忽略
  • COUNT 的语义取决于分析模式:CPU 采样时为采样次数,内存分配时为分配字节数,锁分析时为竞争时间(毫秒)

帧名称规范化

collapsed/folded 使用 ; 分隔栈帧,且没有统一的转义规则。为避免符号名称 被错误拆分,HUATUO 输出该格式时进行以下处理:

  • 将帧名称中的 ; 替换为 :
  • 将回车和换行替换为空格,确保每条调用栈只占一行
  • 仅修改 collapsed/folded 输出;内部符号及结构化格式保留原始名称

例如,generic::<[u8; 7]> 输出为 generic::<[u8: 7]>。

该处理有损:只在 ; 和 : 上不同的符号会合并。需要保留完整符号名称时, 应使用 pprof 等结构化格式。

扩展规范:

部分剖析工具(如 async-profiler)在标准格式基础上引入了帧类型注解,用于标识帧的运行时类别:

frameName_{type} COUNT
注解 含义 说明
_[j] JIT compiled Java JIT 编译后的 Java 方法
_[i] Interpreted Java 解释执行的 Java 方法
_[k] Kernel 内核态帧
_[n] Native C/C++ 原生 C/C++ 帧
_[t] Thread 线程帧

此外,部分工具支持带权重的折叠格式(weighted collapsed),用于差分火焰图:

frame1;frame2;frameN WEIGHT

其中 WEIGHT 为浮点数,表示该栈的权重值而非简单计数。

样本示例

CPU 分析示例(以下数据源自 async-profiler 官方文档):

FileConverter.main;FileConverter.convertFile;FileConverter.saveResult 21
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeInt 1
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeInt;java/io/ByteArrayOutputStream.write 5
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.writeUTF 12
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.writeUTF;java/lang/String.length 3
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.write 6
start_thread;thread_native_entry;Thread::call_run;VMThread::run;VMThread::inner_execute;VMThread::evaluate_operation;VM_Operation::evaluate;VM_GenCollectForAllocation::doit;GenCollectedHeap::satisfy_failed_allocation;GenCollectedHeap::do_collection;GenCollectedHeap::collect_generation;DefNewGeneration::collect;DefNewGeneration::FastEvacuateFollowersClosure::do_void 12

带帧类型注解的示例(async-profiler 扩展):

Main.run_[j];Service.process_[j];DAO.query_[j];mysql_real_query_[n] 45
Main.run_[j];Service.process_[j];DAO.query_[j];recv_[k] 18

核心用途

用途 说明
火焰图生成 作为 flamegraph.pl、inferno 等可视化工具的标准输入格式
差分分析 对比两次 collapsed 文件,生成红蓝差分火焰图,定位性能回归
程序化处理 纯文本格式,便于用 awk、sed、Python 等工具做自定义聚合与过滤
跨工具互操作 Brendan Gregg 定义的通用标准,几乎所有火焰图工具链都支持此格式
长期存储 文本格式体积小,适合归档和版本对比
CI/CD 集成 可在流水线中自动采集、diff、判断性能回归阈值

生成命令示例:

# 以 async-profiler 为例
asprof -d 30 -f profile.collapsed -o collapsed <PID>

Flamegraph 格式

标准语法与格式

flamegraph 格式是一个自包含的 HTML 文件,内嵌 SVG 可视化与 JavaScript 交互逻辑,可直接在浏览器中打开。

结构组成:

flamegraph.html
├── HTML 骨架 + CSS 样式
├── SVG 火焰图主体
│   ├── <g> 每个帧对应的矩形块
│   │   ├── <title> 帧名称 + 采样数/占比
│   │   └── <rect> 位置、宽高、颜色
│   └── ...
├── JavaScript 交互逻辑
│   ├── 点击缩放(zoom into subtree)
│   ├── 搜索高亮(search & highlight)
│   ├── 悬浮提示(tooltip)
│   └── 重置视图(reset zoom)
└── 元数据(title、total samples 等)

视觉编码规则:

维度 编码含义
X 轴 调用栈帧按字母序排列(非时间线),宽度与采样数成正比
Y 轴 调用栈深度,底部为根帧,顶部为叶帧
帧宽度 该帧在栈中出现的采样比例,越宽表示消耗资源越多
帧颜色 标识帧类型(见下表)

帧颜色规范(以 async-profiler 为参考):

注意:火焰图的颜色方案并非跨工具统一标准。Brendan Gregg 原始 flamegraph.pl 使用随机暖色调,颜色无语义含义;perf/bpftrace 通常按 DSO 着色或使用随机色;async-profiler 则按帧类型语义着色。以下为 async-profiler 的配色规范:

颜色 帧类型 说明
🟢 绿色 Java (interpreted) 解释执行的 Java 方法
🟡 黄/橙色 Java (JIT compiled) JIT 编译后的 Java 方法
🔴 红色 C/C++ (native) 原生 C/C++ 代码
🔵 蓝色 Kernel 内核态代码
⬜ 灰色 Other/Unknown 其他类型或未知帧

扩展特性(以 async-profiler 为参考):

  • Icicle Graph(冰柱图):自顶向下展示调用链(根在顶部),更符合自上而下的阅读习惯,通过 --reverse 选项或浏览器内 Reverse 按钮切换
  • 多线程视图:不同线程的调用栈并列展示在根级别
  • 搜索高亮:输入关键词后,匹配帧高亮为紫色,不匹配帧变暗
  • 采样信息提示:悬浮显示帧名、采样数、占总采样百分比
  • Cutoff 帧:标记为 [...] 的帧表示栈截断(如因栈深度限制)

样本示例

生成命令示例:

# 以 async-profiler 为例
asprof -d 30 -f flamegraph.html <PID>

交互操作:

  • 点击帧:缩放至该帧为全宽,仅展示其子树
  • 搜索框:输入关键词,匹配帧高亮
  • 悬浮:显示帧名、采样数、百分比
  • Reset Zoom:恢复全局视图

核心用途

用途 说明
热点定位 直观识别最宽的帧块,快速找到 CPU/内存消耗最大的代码路径
根因分析 从叶帧向上追溯,理解资源消耗的调用链上下文
团队协作 HTML 文件可直接分享,无需安装额外工具,浏览器即可查看
性能优化验证 优化前后各生成一张火焰图,对比帧宽度变化验证优化效果
非专业友好 可视化形式对非性能工程师也更易理解,便于跨团队沟通

两种格式对比

对比维度 Collapsed Flamegraph
格式类型 纯文本 HTML + SVG
人可读性 中等(需理解栈帧语法) 高(可视化,直觉理解)
机器可读性 高(易解析、易 diff) 低(需解析 HTML/SVG)
交互性 无 支持缩放、搜索、悬浮提示
文件大小 极小(KB 级) 较大(百 KB~MB 级)
工具链依赖 无(纯文本) 浏览器
差分分析 原生支持(diff 两个文件) 需转换为 collapsed 后 diff
典型使用场景 程序化处理、CI 对比、存档 人工分析、团队分享、演示

典型工作流:

采集 ──► collapsed ──► flamegraph.html(人工分析)
                  │
                  ├──► 差分火焰图(性能回归检测)
                  ├──► 自定义聚合脚本
                  └──► 归档存储

6.5 - 网络丢包

📖 概述

dropwatch 是 HUATUO 提供的网络丢包观测工具。它通过 tracepoint/skb/kfree_skb 采集软件丢包,并通过 raw_tracepoint/devlink_trap_report 采集支持 devlink trap 的硬件丢包,输出协议类型、IP 五元组、网络设备、丢包原因和内核调用栈。

dropwatch 支持基于 tcpdump 风格过滤表达式的内核侧过滤,过滤逻辑由内置的纯 Go pcap 编译器 internal/pcapfilter 在加载时编译为 eBPF 字节码,过滤完全在内核态执行,只有匹配的数据包才会上报到用户空间,降低对宿主机的性能影响。

此外,dropwatch 支持设备白名单/黑名单过滤、全局上报限速,并可与 huatuo-bamai 集成,将丢包事件存储至 Elasticsearch 进行长期分析。


🎯 场景

1. Kubernetes 云原生网络丢包诊断

在容器漂移、Pod 频繁重启、Service 端口冲突等场景下,通过 dropwatch 实时捕获 kfree_skb 事件并关联到具体容器,快速定位丢包根因。结合 --filter "tcp and port <service-port>" 过滤特定业务流量,将平均故障定位时间从小时级降低至分钟级。

2. 网络性能毛刺分析

针对间歇性网络延迟突增、吞吐下降等问题,通过 dropwatch 采集丢包事件,结合内核调用栈定位丢包发生的具体内核函数(如 tcp_v4_rcv、ip_output 等),辅助区分是防火墙丢弃、路由失败还是缓冲区溢出等原因。

3. 多租户环境网络隔离故障排查

在共享网络命名空间或 veth 设备的容器环境中,通过 --device 过滤指定网络设备,结合 --filter 过滤特定协议,精确采集目标容器的丢包事件,避免其他租户流量干扰诊断结果。

4. 与可观测性平台集成

通过 --output-storage 将丢包事件发送给 huatuo-bamai,存储至 Elasticsearch 后与指标、日志进行多维关联分析。将丢包事件叠加到 Grafana 时间线上,与应用错误率、延迟曲线对齐,实现内核丢包与应用异常的精确关联。


🚀 使用

1. 过滤表达式

过滤表达式采用 tcpdump 语法,由内置的纯 Go pcap 编译器 internal/pcapfilter 在加载时编译为 eBPF 字节码,过滤完全在内核侧执行,降低对宿主机影响,只有匹配的数据包才会上报到用户空间。

1.1 支持的表达式

internal/pcapfilter 支持 tcpdump 标准语法的一个子集,下列原语可以可靠使用:

协议

ip   ip6   tcp   udp   icmp   icmp6   igmp   pim   esp   ah   vrrp   arp   rarp
ip proto tcp      ip6 proto udp        (仅协议名,不支持数字协议号)

主机地址

host 10.0.0.1
src host 10.0.0.1
dst host 10.0.0.1

端口

port 80
src port 443
dst port 8080

网段(CIDR)

net 10.0.0.0/8
src net 192.168.1.0/24
dst net 172.16.0.0/12

组播与以太地址

ip multicast    ip6 multicast    multicast    ether multicast
ether host 00:11:22:33:44:55

布尔运算与分组

tcp and port 80
tcp or udp
not arp
tcp and (port 80 or port 443)
ip and src net 192.168.1.0/24 and tcp dst port 3306

1.2 不支持的表达式

下列表达式不支持,使用后会导致编译失败或产生错误的匹配结果:

表达式 原因
tcp[tcpflags] & tcp-syn != 0、ip[8]、tcp[0:4] 字节偏移表达式(proto[offset:size])未实现
ip proto 6、ip6 proto 17 不支持数字协议号,请改用协议名(如 ip proto tcp)
ether proto 0x0800 不支持十六进制 EtherType,请改用名字(如 ether proto ip)
sctp 关键字未识别
portrange 80-90、tcp portrange 1-100 不支持端口范围
less N、greater N 不支持按报文长度过滤
ip broadcast、ether broadcast 不支持广播匹配
vlan、mpls、pppoes 不支持隧道/封装关键字
gateway 不支持

1.3 推荐写法示例

# 监控所有 TCP 丢包(默认值——L2 和 L3 上下文均可靠)
--filter "tcp"

# TCP 和 UDP
--filter "tcp or udp"

# 指定目标主机(TCP 和 UDP 均适用)
--filter "dst host 10.0.0.1"

# 指定端口
--filter "tcp and port 443"

# 排除噪声主机
--filter "tcp and not host 169.254.169.254"

# 指定子网 + 指定端口
--filter "src net 192.168.1.0/24 and tcp dst port 3306"

# 监控非 TCP 的丢包(仅 UDP 和 ICMP——不要用 "not tcp",会捕获到未知 L3 事件)
--filter "udp or icmp"

# 仅监控 ARP 丢包(仅 L2 上下文有效,L3 永远不匹配)
--filter "arp"

--filter "ip" / --filter "ip6" 现可正确匹配对应 IP 协议族(L2 按 EtherType、L3 按版本 nibble)。若只关心特定传输层或主机,仍建议用更精确的 tcp、udp、host 或 ip proto <name>。


2. 运行 dropwatch

dropwatch [flags]
参数 默认值 说明
--bpf-path <path> 必填 dropwatch eBPF 对象文件路径
--filter <expr> (无) tcpdump 风格过滤表达式
--device <names> (无) 设备白名单:只采集这些设备的丢包,多个设备用逗号分隔(如 eth0,eth1)
--device-excluded <names> (无) 设备黑名单:排除这些设备的丢包;与 --device 互斥
--duration <n> 0 运行 N 秒后退出(0 表示持续运行直至 Ctrl-C)
--output <json|text> text 输出格式;设置 --output-storage 时会被忽略
--output-storage <path> (无) 通过 Unix socket 将事件发送给 huatuo-bamai
--task-id <id> (无) 关联本次会话的任务 ID;通常与 --output-storage 一起使用
--max-events-per-second <n> 0 全局上报限速,0 表示不限速;在 --device / --filter 后生效

--filter 与设备过滤相互正交,同时指定时两者均生效(AND 语义)。不指定 --device / --device-excluded 时采集所有设备。--device 和 --device-excluded 不能同时使用;白名单模式会丢弃没有 net_device 的 SKB,黑名单模式会放行没有 net_device 的 SKB。

dropwatch 启动时自动检测 devlink:devlink_trap_report。内核支持时同时加载软硬件丢包探针;不支持时记录 warning 并仅加载软件丢包探针。硬件丢包采集还要求网卡驱动注册 devlink drop trap,并将目标 trap action 配置为 trap。action 为 drop 时硬件不会向 CPU 提供报文副本,dropwatch 无法获取报文。

dropwatch 无需额外启动参数。使用前确认内核、驱动和目标 trap 均满足条件:

# 1. 确认内核提供 devlink trap tracepoint
test -e /sys/kernel/tracing/events/devlink/devlink_trap_report/id || \
  test -e /sys/kernel/debug/tracing/events/devlink/devlink_trap_report/id

# 2. 查看 devlink 设备及驱动注册的 trap
sudo devlink dev show
sudo devlink trap show <bus/device>

# 3. 为目标 DROP trap 启用报文上报
sudo devlink trap set <bus/device> trap <trap-name> action trap

# 4. 启动 dropwatch,并只查看硬件丢包
sudo dropwatch --bpf-path bpf/net_dropwatch.o --output json 2>/dev/null | \
  jq -c 'select(.drop_source == "hardware")'

<bus/device> 使用 devlink dev show 返回的设备标识,例如 pci/0000:03:00.0。诊断结束后应将 trap 恢复为变更前的 action。

该能力只采集驱动通过 DEVLINK_TRAP_TYPE_DROP 上报的报文,不会采集所有硬件报文,也不等同于网卡硬件丢包计数器。硬件队列满等丢包只有在驱动将其实现为 devlink drop trap 并上报时才可见;exception 和 control 类型的 trap 不会上报为丢包事件。

--filter、--device、--device-excluded 和 --max-events-per-second 同时作用于软件与硬件事件。文本输出将硬件原因表示为 reason=<group>/<trap> drop_source=hardware;JSON 输出使用独立的 drop_reason_group、drop_reason 和 drop_source 字段。

tcpshark --with-dropwatch 复用相同的来源分类和 reason 解析,也自动启用可用的硬件采集;成功关联时保留相同的 drop_source、drop_reason 和 drop_reason_group。参见 重传关联说明。

常用命令

# 文本格式输出,监控所有设备的 TCP 丢包
sudo dropwatch --bpf-path bpf/net_dropwatch.o --filter "tcp"

# 只监控 eth0 上的丢包
sudo dropwatch --bpf-path bpf/net_dropwatch.o --device eth0 --output json

# 排除 loopback
sudo dropwatch --bpf-path bpf/net_dropwatch.o --device-excluded lo --output json

# 设备过滤与协议过滤组合
sudo dropwatch --bpf-path bpf/net_dropwatch.o --device eth0 --filter "tcp and port 443" --output json

# 抓取 60 秒后退出
sudo dropwatch --bpf-path bpf/net_dropwatch.o --filter "tcp and port 443" --duration 60 --output json

# 将事件转发给正在运行的 huatuo-bamai 实例
sudo dropwatch --bpf-path bpf/net_dropwatch.o --filter "tcp" --output-storage /var/run/huatuo-toolstream.sock

# 采集 10 秒 JSON 输出,并排除调用栈包含 ip_finish_output 的事件
sudo dropwatch --output json --duration 10 --bpf-path bpf/net_dropwatch.o | jq -c 'select(.stack | test("ip_finish_output") | not)'

# 采集 10 秒 JSON 输出,只打印除 stack 之外的字段
sudo dropwatch --output json --duration 10 --bpf-path bpf/net_dropwatch.o | jq -c 'del(.stack)'

jq -c 会把每条匹配事件压缩成单行 JSON,便于保存为 NDJSON 或继续用管道处理。test("ip_finish_output") 判断 stack 是否匹配该正则,not 会把结果取反,因此上面的命令会排除包含 ip_finish_output 的调用栈;去掉 | not 后,就是只保留包含 ip_finish_output 的事件。del(.stack) 只从 jq 输出中删除 stack 字段,适合只查看时间、设备、进程、packet_* 元数据和 layers 协议字段。如需在存储前由用户态按调用栈过滤,可通过 huatuo-bamai 配置 EventTracing.IssuesList 实现(参见第 4 节)。


3. 事件数据结构

每条丢包事件以 NDJSON 对象(types.DropWatchTracing)表示。

字段 类型 说明
observed_timestamp string 用户态接收/格式化事件时生成的 UTC 时间(RFC3339Nano),不是内核 hook 时间
kernel_observed_timestamp string 内核观测事件的 UTC 时间(RFC3339Nano),由原始单调时钟转换。
type string 预留 TCP 事件类型,当前未设置(1 普通丢包、2 SYN flood、3/4 listen overflow)
drop_source string 丢包来源:software 表示内核协议栈,hardware 表示 devlink DROP trap
drop_reason string 软件丢包为 SKB_DROP_REASON_*;无法从内核 BTF 解析时记录 warning 并回退为数字。硬件丢包为 devlink trap 名称
drop_reason_group string devlink trap 分组名称,用于归类硬件丢包;软件丢包不输出该字段
drop_location string 软件丢包的 kfree_skb 调用地址(十六进制);硬件丢包不输出该字段
source string 事件来源;独立运行 dropwatch 时为 tools,由 huatuo-bamai 启动时为 events
comm string 丢包时的进程名
pid uint64 进程 TGID
container_id string 容器 ID(由 huatuo-bamai 解析填充,omitempty)
memory_cgroup_css_addr string 内存 cgroup CSS 地址,用于容器归属解析
net_namespace_cookie uint64 网络命名空间 cookie,用于容器归属解析
net_namespace_inum uint32 网络命名空间 inum,用于容器归属解析
netdev_name string 网络设备名(如 eth0)
netdev_ifindex uint32 网络接口索引
netdev_queue_mapping uint32 TX 队列映射
netdev_linkstatus []string 网络设备链路标志
packet_skb_addr string SKB 地址(十六进制,omitempty)
packet_eth_proto string 原始 EtherType(十六进制,如 0x0800)
packet_len_bytes uint32 内核 skb->len 快照;表示 SKB 逻辑长度,可能与线上帧长度不同
layers object 分层协议解析结果,缺失的层会省略
stack string 内核调用栈(换行分隔)

硬件事件的 stack 表示驱动调用 devlink trap 上报接口时的内核调用栈,不代表 ASIC 内部的实际丢弃位置。定位硬件原因时应以 drop_reason_group、drop_reason、设备信息和驱动文档为主。

layers 使用固定字段表达协议栈,不再依赖单独的协议枚举:

字段 说明
layers.label 协议组合标签,如 IPv4/TCP、IPv6/UDP、ARP、unknown
layers.ether 存在真实 Ethernet header 时输出二层字段:saddr、daddr、type、len;仅 IEEE 802.3 framing 的 len 非零
layers.ipv4 IPv4 字段:version、ihl、tos、len、id、flags、frag_offset、ttl、protocol、checksum、saddr、daddr
layers.ipv6 IPv6 字段:version、traffic_class、flow_label、len、next_header、hop_limit、saddr、daddr
layers.tcp TCP 字段:sport、dport、seq、ack_seq、data_offset、window、checksum、urgent、sk_state
layers.udp UDP 字段:sport、dport、len、checksum
layers.icmp ICMP/ICMPv6 字段:type、code、checksum、id、seq;ICMPv6 Echo 请求/应答包含标识符和序号,其他 ICMPv6 消息省略这两个字段
layers.arp ARP 字段:addr_type、protocol、hw_address_size、prot_address_size、operation、sender_mac、sender_ip、target_mac、target_ip

ICMPv6 Echo Request/Reply(类型 128/129)仅在捕获完整四字节 Echo 字段时 填充 id、seq。Echo 字段截断时保留通用 ICMPv6 字段,ID/Seq 为零; 通用头也被截断时仍保留已解码的 IPv6 层,layers.icmp 不存在。 其他 ICMPv6 消息类型不填充 Echo 字段。

JSON 分别省略值为零的 id、seq;文本仅在两者都为零时省略它们。 因此,输出字段缺失无法区分未包含或截断的 Echo 字段与合法的零值。


4. 与 huatuo-bamai 集成

huatuo-bamai 以子进程形式启动 dropwatch,并通过 --output-storage 将事件发送到内置处理流程,并最终存储到 Elasticsearch。典型参数如下:

dropwatch \
  --bpf-path <CoreBpfDir>/net_dropwatch.o \
  --output-storage /var/run/huatuo-toolstream.sock \
  --filter "tcp"

4.1 配置项参考(huatuo-bamai.conf)

[EventTracing]
    # 可选调用栈过滤。dropwatch 会丢弃 stack 匹配已配置正则的事件。
    # 默认值: []
    IssuesList = []

[EventTracing.Dropwatch]
    # standalone dropwatch 使用的 tcpdump filter。
    # 默认值: "tcp"
    Filter = "tcp"

    # 转发给 dropwatch --max-events-per-second。
    # 默认值: 100
    MaxEventsPerSecond = 100

[EventTracing.TCPRetransmit]
    # 使用 tcpshark 私有的 embedded dropwatch source。
    EnableDropwatch = false

standalone dropwatch 始终输出 raw DropWatchTracing。TCP 重传 local 关联会加载另一份 net_dropwatch.o,两个输入统一使用 EventTracing.TCPRetransmit.Filter,并且只输出定型后的 TCPRetransmitTracing 结果。两种模式可以并行;embedded drop 不会重复保存成 raw event。

4.2 噪声过滤

默认不启用任何调用栈噪声过滤。配置 EventTracing.IssuesList 后,huatuo-bamai 才会丢弃匹配事件。下表是可由运维人员配置的候选模式;启用前应结合本机内核和工作负载验证:

模式 调用栈帧前缀 原因
ARP/邻居表到期 neigh_invalidate/ 邻居表项到期清理,不影响任何活跃数据流。可从 EventTracing.IssuesList 移除对应规则以关闭过滤。
bnxt 网卡 TX 完成 bnxt_tx_int/ 或 __bnxt_tx_int/ Broadcom bnxt 网卡驱动在 DMA 发送完成后调用 kfree_skb 释放 SKB,此为正常行为,非丢包。

🌟 结尾

6.6 - 重传追踪

📖 概述

tcpshark --mode retransmit 通过内核跟踪点 tcp/tcp_retransmit_skb 和 tcp/tcp_retransmit_synack 观测 TCP 重传相关活动。显式开启 TLP 后,还会观测 tcp_send_loss_probe kprobe。根据事件类型,每条事件可携带 IP 四元组、TCP 状态、拥塞控制状态、重传计数器、序列号信息,以及用于解析容器归属的 socket 元数据。

用户态分类器根据事件类型、sk_state、ca_state 和乱序计数器生成连接阶段与原因标签。这些标签是用于运维分析的启发式分类,不是丢包根因的确定性证据。

过滤表达式由 internal/pcapfilter 在加载时编译并在内核中执行。无论是否开启 local 关联,SKB、SYN-ACK 和 TLP 三个 hook 都对同一种合成 L3 TCP 报文执行过滤。支持协议、地址、网段和端口条件;不提供以太网地址、payload、真实包长、IP/TCP options 或原始 byte-offset 语义。ether proto ip 等安全的 ethertype 判断会转换为 L3 判断。一对 IPv4-mapped IPv6 socket 地址按 IPv4 执行过滤;原始 perf record 仍保留 AF_INET6,用户态在匹配前规范化地址。local 关联模式还会把完全相同的表达式用于 embedded dropwatch。需要保留反向 ACK 或 SYN-ACK 证据时,应使用方向对称的表达式。


🎯 场景

1. TCP 网络质量与重传诊断

通过持续观测 RTO、快速重传、乱序倾向重传和 TLP 事件,识别连接建立、数据传输及连接关闭阶段的异常重传,辅助判断网络丢包、拥塞、乱序或对端不可达等问题。

2. Kubernetes 容器网络故障排查

结合容器 ID、网络命名空间和 socket cgroup 元数据定位发生重传的工作负载,并使用 --filter "tcp and port <service-port>" 聚焦特定服务流量,减少宿主机上其他连接的干扰。

3. 应用延迟与吞吐毛刺分析

将 TCP 重传事件与应用延迟、错误率和吞吐曲线对齐,分析 RTO 或连续重传是否与服务性能下降同时发生,辅助区分应用处理变慢与底层网络异常。

4. 与 dropwatch 关联定位丢包位置

使用 tcpshark local 模式在同一进程内关联重传与丢包。匹配会检查 network namespace、四元组方向、TCP sequence 或 ACK 证据及内核单调时间顺序。严格匹配表示找到了满足关联条件的丢包证据,软件或硬件来源以记录为准。no-match 保持 unknown,因为 source ready 不能证明更早的因果历史已经被观测。


🚀 使用

1. 运行 tcpshark

tcpshark --mode retransmit [flags]
参数 默认值 说明
--mode retransmit 必填 选择 TCP 重传追踪模式。
--enable-tlp、--tlp 关闭 同时挂载 tcp_send_loss_probe 并输出 TLP 事件。
--bpf-path <path> 非关联模式必填 单个 tcp_retransmit.o 文件路径。
--bpf-path-dir <dir> 关联模式必填 同时包含 tcp_retransmit.o 和 net_dropwatch.o 的目录。
--with-dropwatch 关闭 加载 embedded dropwatch 并与重传关联。
--filter <expr> (无) 三个重传 hook 共用的 L3 兼容 tcpdump 风格过滤器;local 模式下也与 embedded dropwatch 共用,见 §2。
--device <names> (无) 与 dropwatch 相同的网卡白名单,逗号分隔;只过滤 embedded dropwatch,要求 --with-dropwatch。
--device-excluded <names> (无) 网卡黑名单,与 --device 互斥;要求 --with-dropwatch。
--duration <n> 0 运行 N 秒后退出(0 表示持续运行直至 Ctrl-C)。
--max-events-per-second <n> 0 BPF 侧事件限速,0 表示不限速。
--output <json|text> text 输出格式;设置 --output-storage 时会被忽略。
--output-storage <path> (无) 通过 Unix socket 将事件发送给 huatuo-bamai。
--task-id <id> (无) toolstream 会话关联的任务 ID;必须与 --output-storage 一起使用。

显式同时指定 --output 和 --output-storage 时,--output 会被忽略并打印警告。

1.1 常用命令

# 文本格式输出全部重传相关事件
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o

# NDJSON 格式输出
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --output json

# 在 BPF 侧过滤指定目标主机和端口的常规重传 SKB
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --filter "dst host 10.0.0.1 and dst port 443"

# 本地关联;两个 BPF 输入使用同一个方向对称 filter
sudo tcpshark --mode retransmit --with-dropwatch --bpf-path-dir bpf \
  --filter "tcp and port 443"

# 包含 Tail Loss Probe 事件(默认关闭)
sudo tcpshark --mode retransmit --enable-tlp --bpf-path bpf/tcp_retransmit.o

# 最多输出 100 条事件/秒;超限时打印 rate limit hit 日志
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o \
  --max-events-per-second 100

# 在用户态过滤全部格式化事件类型,只保留目标端口 443
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --output json \
  | jq -c 'select(.tcp_dport == 443)'

# 运行 60 秒,仅保留分类为 RTO 的事件
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --duration 60 --output json \
  | jq -c 'select(.tcp_reason == "RTO")'

# 将事件转发给正在运行的 huatuo-bamai 实例
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o \
  --output-storage /var/run/huatuo-toolstream.sock

jq -c 将每条结果压缩成单行 JSON,便于保存为 NDJSON 或继续通过管道处理。

1.2 与 huatuo-bamai 集成

tcpshark 与 dropwatch 使用相同的 --output-storage 和 toolstream 流程。通用存储方式请参考 dropwatch 文档。TCP 重传追踪增加以下配置:

[EventTracing.TCPRetransmit]
    # 两种模式都由 tcpshark 使用;默认空值。
    Filter = ""

    # 设置为 true 时传入 tcpshark --enable-tlp;默认 false。
    EnableTLP = false

    # 使用 embedded dropwatch;默认 false。
    EnableDropwatch = false

    # 传给 tcpshark --max-events-per-second;默认 100,0 表示不限速。
    MaxEventsPerSecond = 100

EventTracing.TCPRetransmit.Filter 在两种模式下都控制重传采集。关闭 local 关联时,空值不传 --filter。开启 local 关联时,规范化后的表达式同时传给 tcpshark 的两个输入,空值规范化为 tcp。EventTracing.Dropwatch.Filter 保持独立,只控制 standalone dropwatch。

tcp_retransmit tracer 默认位于全局 BlackList 中。需要启用时,应将其移除并 重启 huatuo-bamai。local 关联使用私有 dropwatch source,因此 standalone dropwatch 可以继续位于黑名单中。


2. 过滤表达式

tcpshark 使用与 dropwatch 相同的 tcpdump 风格过滤表达式。完整语法、限制和更多示例请参考 dropwatch 文档。

# 指定目标主机和端口
--filter "dst host 10.0.0.1 and dst port 443"

# 观察两个网段之间的双向流量
--filter "(src net 10.10.0.0/16 and dst net 10.20.0.0/16) or (src net 10.20.0.0/16 and dst net 10.10.0.0/16)"

local 模式下,同一个表达式必须覆盖两个流量方向。方向性 selector 可能排除反向 ACK 或 SYN-ACK drop 证据,降低结果可信度。

local 模式拒绝 ether host 02:00:00:00:00:01 等依赖以太网地址的 primitive。ether proto ip 和 ether proto ip6 可转换为 raw-IP version 判断,因此受支持。


3. 事件数据结构

每条事件以 NDJSON 对象(types.TCPRetransmitTracing)表示。带 omitempty 标签的字段在值为空或零时不会输出。

字段 类型 说明
observed_timestamp string 用户态接收/格式化事件时生成的 UTC 时间(RFC3339Nano),不是内核 hook 时间。
kernel_observed_timestamp string 内核观测事件的 UTC 时间(RFC3339Nano),由原始单调时钟转换。
comm string 当前内核执行上下文的进程名,不一定是 socket 所属进程。
pid uint64 当前执行上下文的 TGID,不一定是 socket 所属进程的 TGID。
container_id string huatuo-bamai 解析出的容器 ID,见 §3.2。
memory_cgroup_css_addr string 用于解析容器归属的 socket 内存 cgroup CSS 十六进制地址。
net_namespace_cookie uint64 用于解析容器归属的 socket 网络命名空间 cookie。
net_namespace_inum uint32 用于解析容器归属的 socket 网络命名空间 inum。
tcp_saddr string 源 IP 地址。
tcp_daddr string 目的 IP 地址。
tcp_sport uint16 源端口。
tcp_dport uint16 目的端口。
tcp_state string TCP socket 状态,如 ESTABLISHED、SYN_SENT 或 NEW_SYN_RECV。
phase string 分类结果:connect、data 或 close。
tcp_reason string 分类结果:RTO、fast_retransmit、TLP 或 unknown。
event_type string tcp_retransmit_skb、tcp_retransmit_synack 或 tcp_send_loss_probe。
ca_state uint8 拥塞控制状态:0=Open、1=Disorder、2=CWR、3=Recovery、4=Loss。
icsk_retransmits uint8 当前重传计数器快照。
icsk_pending uint8 inet_connection_sock 中原始的待处理定时器状态,取值见下表。
reord_seen uint32 连接累计乱序计数器。
dsack_dups uint32 累计 DSACK 重复计数器。
tcp_seq uint32 SKB 事件使用 TCP_SKB_CB(skb)->seq;TLP 使用 snd_nxt;字段可用时 SYN-ACK 使用 request snt_isn。
tcp_ack_seq uint32 SKB 事件使用 tcp_sk(sk)->rcv_nxt;TLP 使用 snd_una;字段可用时 SYN-ACK 使用 request rcv_nxt。
tcp_end_seq uint32 SKB 事件使用 TCP_SKB_CB(skb)->end_seq;字段可用时 SYN-ACK 使用 request snt_isn + 1;TLP 中省略。
tcp_flags string 渲染后的 TCP flag 集合,如 `SYN
skb_addr string 十六进制重传队列 SKB 指针;SYN-ACK 和 TLP 事件中不存在。
drop_location string local 关联分类:software、hardware 或 unknown;匹配时与 drop_source 相同,与 dropwatch 的内核地址字段语义不同。
drop_source string 匹配丢包的来源:software 或 hardware;无法识别 ABI 来源时为 unknown,未匹配时省略。
drop_reason string 与 dropwatch 一致:软件丢包为 BTF 解析的 SKB_DROP_REASON_*,不可解析时为十进制数字,旧内核不支持时为 NOT_SUPPORTED;硬件丢包为 devlink trap 名称。
drop_reason_group string devlink trap 分组,如 l2_drops,用于聚合硬件丢包原因;软件丢包和未匹配事件省略。
correlation_reason string 唯一终态:matched、unsupported、warmup、wait_timeout、queue_full 或 interrupted;未启用关联时省略。
matched_net_namespace bool 同一 TCP flow 下观测到相同 namespace 的 drop,独立于报文和时间检查。严格匹配时必为 true;false 时省略。
drop_perf_status object 未匹配事件的 embedded dropwatch 累计计数;map_counters_available 标记 map 计数是否可用,读取失败时仍保留有效的 reader lost_samples。
drop_stack string 匹配到的 drop 调用栈;未匹配的栈不做符号化。
source string 事件来源。独立运行 tcpshark 时为 tools,由 huatuo-bamai 启动时为 events。

icsk_pending 是 hook 时刻的定时器状态快照,不是重传原因的稳定枚举。TLP 分类以明确的 event_type=tcp_send_loss_probe 为准,不依赖 icsk_pending=5。

值 内核状态 含义
0 None 当前没有待处理的发送定时器事件。
1 ICSK_TIME_RETRANS 重传超时定时器(RTO)。
2 ICSK_TIME_DACK 延迟 ACK;现代内核将该状态保存在 icsk_ack.pending 并使用独立的 delayed-ACK timer,因此通常不会出现在 icsk_pending 中。
3 ICSK_TIME_PROBE0 零窗口探测定时器。
4 版本相关 当前主线内核不再定义该值;旧内核曾将其用于 Early Retransmit,更早的内核曾用于 Keepalive。
5 ICSK_TIME_LOSS_PROBE Tail Loss Probe(TLP)定时器。
6 ICSK_TIME_REO_TIMEOUT 乱序超时定时器,主要用于 RACK 丢包判断。

3.1 文本输出格式

文本输出保留面向终端的可读布局,同时覆盖与 JSON 相同的事件变量。可选变量仅在非零或非空时显示,字符串值不添加 JSON 引号或转义。为兼容原文本格式,state、skb、seq、end、ack、flags、ca、retrans 和 reason 分别对应 JSON 中的 tcp_state、skb_addr、tcp_seq、tcp_end_seq、tcp_ack_seq、tcp_flags、ca_state、icsk_retransmits 和 correlation_reason。

<timestamp> [<phase>/<tcp_reason>] <saddr>:<sport> > <daddr>:<dport> state=<STATE> event_type=<TYPE> [kernel_observed_timestamp=<UTC>] [SYNACK] [skb=<ADDR>] seq=<N> [end=<N>] ack=<N> [flags=<FLAGS>] pid=<N> comm=<COMM> ca=<N> retrans=<N> icsk_pending=<N> [reord_seen=<N>] [dsack_dups=<N>] [container_id=<ID>] [memory_cgroup_css_addr=<ADDR>] [net_namespace_cookie=<N>] [net_namespace_inum=<N>] [drop_location=<LOCATION>] [drop_source=<SOURCE>] [drop_reason=<REASON>] [drop_reason_group=<GROUP>] [reason=<REASON>] [matched_net_namespace=true] [dropwatch_map_counters_available=<true|false> dropwatch_perf_lost=<N> dropwatch_lost_samples=<N> dropwatch_rate_limited=<N>] [source=<SOURCE>]

示例:

2026-07-23T02:14:40.304775546Z [data/RTO] 127.0.0.1:19996 > 127.0.0.1:42128 state=ESTABLISHED event_type=tcp_retransmit_skb kernel_observed_timestamp=2026-07-23T02:14:40.304Z skb=0xffff931c14fdf800 seq=3154974646 end=3154991030 ack=948393597 flags=ACK|PSH pid=1420 comm=kube-apiserver ca=4 retrans=4 icsk_pending=0 net_namespace_inum=4026531992

示例中的 pid 和 comm 表示 hook 运行时的执行上下文;工作负载归属应使用 container_id 和 socket 元数据判断。

非空的 drop_stack 会作为事件行之后的缩进调用栈行输出,不使用行内 drop_stack= token。

3.2 容器 ID 解析

tcpshark 自身无法访问 Pod 管理器。独立输出时通常没有 container_id,但可用时仍会输出 socket memcg 和网络命名空间元数据。通过 huatuo-bamai 运行时,空的 container_id 按 memory_cgroup_css_addr、net_namespace_cookie、net_namespace_inum 的顺序解析。

全部解析未命中时,事件仍会存储,但 container_id 保持为空。pid 和 comm 描述的是 hook 执行上下文,不能作为判断 socket 归属的回退依据。


4. 内核事件与分类

4.1 内核挂载点

挂载点 内核位置 事件含义 可用数据
tracepoint tcp/tcp_retransmit_skb __tcp_retransmit_skb() 对一个重传队列 SKB 发起了重传尝试;tcpshark 事件不保留内核发送结果。该 SKB 是 headerless 的,因此序列号来自 TCP_SKB_CB(skb),ACK 来自 tcp_sk(sk)->rcv_nxt。 SKB 指针、TCP seq/end_seq/ack/flags、socket 状态、CA 状态、定时器和乱序计数器。
tracepoint tcp/tcp_retransmit_synack tcp_rtx_synack() tcp_rtx_synack() 成功提交了一次被动建连 SYN-ACK 重传。 request socket 地址和端口;没有重传 SKB 指针及 TCP seq/ack。
kprobe tcp_send_loss_probe tcp_send_loss_probe() 正在准备 Tail Loss Probe;仅在指定 --enable-tlp 时采集。 socket 元数据及 snd_nxt/snd_una;没有 SKB 指针或渲染后的 TCP flags。

BPF 程序通过 BPF_CORE_READ 等辅助方法执行 CO-RE 字段读取,因此在支持的内核布局上无需为每个内核版本重新编译 C 源码。

4.2 连接阶段

常规 SKB 事件的阶段由 sk_state 决定;SYN-ACK 事件在用户态使用固定阶段。

下面以 TCP 三次握手说明 connect 阶段及对应的重传观测点:

sequenceDiagram
    participant C as 客户端
    participant S as 服务端
    Note over C,S: 初始状态:CLOSED / LISTEN
    C->>S: ① SYN
    Note left of C: SYN_SENT(2)<br/>phase=connect
    opt SYN 未被确认
        C-->>S: SYN 重传<br/>tcp_retransmit_skb
    end
    Note right of S: SYN_RECV(3) 或 NEW_SYN_RECV(12)<br/>phase=connect
    S->>C: ② SYN + ACK
    opt 最终 ACK 未到达
        S-->>C: SYN-ACK 重传<br/>tcp_retransmit_synack
    end
    C->>S: ③ ACK
    Note over C,S: ESTABLISHED(1)<br/>后续常规数据 SKB 事件 phase=data

图中的三条实线表示首次握手报文,不会产生 tcpshark 事件;只有可选框中的重传路径会被观测。主动端 SYN 重传通过 tcp_retransmit_skb 上报,被动端 SYN-ACK 重传通过 tcp_retransmit_synack 上报,两者都归类为 connect。

完整阶段映射如下:

阶段 来源状态或事件 说明
connect SYN_SENT(2)、SYN_RECV(3)、NEW_SYN_RECV(12) 或 tcp_retransmit_synack 连接建立。
data ESTABLISHED(1) 或无法识别/默认状态 数据传输或默认分类。
close FIN_WAIT1(4)、FIN_WAIT2(5)、TIME_WAIT(6)、CLOSE_WAIT(8)、LAST_ACK(9)、CLOSING(11) 连接关闭。

4.3 原因分类

事件或条件 原因 含义
tcp_retransmit_synack RTO SYN-ACK 重试定时器路径的固定用户态标签。
tcp_send_loss_probe TLP 可选 Tail Loss Probe hook 的固定用户态标签。
tcp_retransmit_skb,ca_state=4(Loss) RTO socket 当前处于 TCP_CA_Loss。
tcp_retransmit_skb,ca_state=3(Recovery) fast_retransmit Recovery 路径重传。
tcp_retransmit_skb,ca_state=0..2,connect/close 阶段 RTO 当前分类器使用的阶段回退结果。
tcp_retransmit_skb,ca_state=0..2,data 阶段 unknown 当前快照不足以生成其他标签。

分类器只观察 hook 时刻的 socket 状态,无法重建完整的 ACK/丢包历史。因此应把 tcp_reason 视为聚合标签,而不是经过验证的根因。

4.4 乱序启发式判断

4.5 运维解读

没有任何事件类型可以无条件丢弃。相比只按 event_type 或 tcp_reason 过滤,更推荐按速率、比例和服务影响设置阈值。通用的 huatuo-bamai 噪声过滤机制请参考 dropwatch 文档。

模式 通常优先级 建议
tcp_reason=RTO 高 排查持续增长或与服务异常相关的 RTO;它通常比 Recovery 路径重传带来更大延迟影响。
tcp_reason=fast_retransmit 中 结合丢包、拥塞及 SACK/RACK 行为分析。
tcp_reason=TLP 视上下文而定 这是可选信号;用于告警前应确认已主动开启 TLP 采集。
event_type=tcp_retransmit_synack 单次通常较低 重复出现可能意味着握手可达性、主机出口、防火墙、客户端或网络问题。

配置告警时,应按服务或连接聚合并结合流量规模判断。繁忙主机上的少量绝对计数可能无害,而低流量关键服务上的突发事件可能具有较大影响。


5. 与 dropwatch 关联

指定 --with-dropwatch 后,一个 tcpshark 进程持有两条 perf 输入。重传最多等待 100ms,让较晚送达用户态的 dropwatch 事件参与匹配;候选 drop 的内核单调时间必须早于重传且相差不超过 1s。embedded source 不输出 raw drop 文档;独立启用的 standalone dropwatch 仍是另一条 raw event stream。

关于双 perf stream 的读取乱序、100ms 到达窗口、1s 因果窗口及 negative evidence 的限制,参见 TCP retransmit 与 dropwatch 关联的难点。

与独立 dropwatch 一样,embedded source 自动检测并启用 devlink DROP trap 采集(HardwareAuto),无需额外硬件开关;tracepoint 不可用时输出 warning 并继续采集软件丢包。硬件可见性取决于驱动是否上报目标 DROP trap,条件见 dropwatch 硬件丢包说明。

--device、--device-excluded、--filter、--max-events-per-second 使用与 dropwatch 相同的参数名。网卡白名单/黑名单仅限制 embedded dropwatch,同时作用于其软件和硬件事件;白名单拒绝没有网卡信息的记录,黑名单允许这类记录。重传输入仍按共同的 L3 filter 采集,设备过滤可能减少可匹配的 drop 证据。

匹配后保留 drop_source、drop_reason 和硬件的 drop_reason_group。drop_reason 是观测到的丢包原因,tcp_reason 是重传触发分类,correlation_reason 是本次关联的唯一结束原因,独立诊断字段描述观测限制。BTF reason 表每次会话加载一次;加载失败时记录 warning,软件 reason 回退为数字,硬件 trap 解析不受影响。

# 使用与 dropwatch 相同的网卡和流量参数,查看匹配到的硬件 drop
sudo tcpshark --mode retransmit --with-dropwatch --bpf-path-dir bpf \
  --device eth0 --filter "tcp and port 443" --output json \
  | jq -c 'select(.drop_source == "hardware")'

硬件 drop 也必须满足相同的 namespace、TCP 和时间约束。缺少 namespace 或 TCP 匹配字段时保持未匹配;未匹配不能证明发生了硬件丢包。硬件 drop_stack 是驱动上报 trap 时的内核栈,不是 ASIC 内部的丢弃位置。

来源枚举未知时,匹配记录的 drop_source 和 drop_location 均为 unknown;不会根据 reason 或调用栈猜测来源;该记录仍输出 correlation_reason=matched。

5.1 关联结果

结果 必须满足的证据 输出
出方向 segment 匹配 network namespace、地址族、方向、四元组、单调时间顺序相同,且 SYN/data/FIN sequence range 重叠。 输出 correlation_reason=matched,以及丢包来源、原因和 drop_stack。
反方向 ACK 匹配 相同 namespace 中的反向四元组、ACK flag、单调时间顺序,且 ACK 覆盖重传 sequence end。 输出 correlation_reason=matched,以及丢包来源、原因和 drop_stack。
无严格匹配 缺少本地证据不能证明实际丢包位置。 drop_location=unknown、唯一的 correlation_reason 和独立诊断信息。

不存在仅四元组、仅 SKB pointer、跨 namespace 或 ambiguous 的正向匹配。matched_net_namespace 在报文和时间检查前记录同一 flow(任一方向)下相同 namespace 的 drop。该值为 true 不代表严格匹配成功,完整关联仍以 correlation_reason=matched 为准。false 或字段缺省表示尚未观测到 namespace 匹配,不代表已确认 namespace 不同。匹配到的 drop 只消费一次,同一连接的后续 drop 仍可继续匹配。只有成功匹配后才做调用栈符号化。

5.2 关联终态

每条已定型事件只有一个 correlation_reason:

值 含义
matched 找到严格匹配的 drop,包括来源未知的已匹配记录。
unsupported 事件缺少匹配规则要求的类型、namespace、时间或 sequence 证据。
warmup 100ms 等待到期仍未匹配,且重传发生时间早于 embedded source ready。
wait_timeout 100ms 等待到期仍未匹配,且重传发生时间不早于 embedded source ready;这里是关联等待超时,与 TCP 的 RTO 无关。
queue_full 有界重传等待队列已满,该事件在到期前被淘汰。
interrupted 关联循环退出,该事件尚未到期的等待被中断,包括正常停止和异常退出。

匹配和容量检查前先处理到期事件。退出时使用同一个时间点:已到期项为 warmup 或 wait_timeout,剩余等待项为 interrupted。每条事件只定型一次,随后移出队列。无法解码或从 drop 缓存淘汰的记录不会直接结束某条重传的关联。

单值字段替代原原因数组,tcpshark 与接收端需要配套更新。文本输出使用 reason=<值>;未启用关联时省略该字段。

warmup 仅用于等待到期且重传内核时间早于 embedded source ready 的事件。重传时间等于或晚于 ready 时,等待到期统一使用 wait_timeout,包括 ready 后不足 1s 的情况。drop 到重传的 1s 上限只约束候选匹配,不定义预热期。处理延迟不会改变分类。严格匹配、不支持、队列淘汰和提前中断保留各自原因。该终态表达观测时序,不证明未匹配的具体根因。

matched_net_namespace 保留为独立布尔诊断字段,同时用于已匹配和未匹配结果;可以与丢失计数同时存在,不改变终态原因。

退出时不再读取 dropwatch perf ring 中尚未交给关联器的记录;未读取的尾部 drop 原本仍可能与被中断的重传匹配。

5.3 Dropwatch Perf Status

每个非空输出批次在写出前读取一次状态,包括全部匹配的批次。未匹配事件保留状态快照:

字段 含义
map_counters_available 两个 map 计数是否均可用;false 区分计数不可用与观测值为零。
perf_lost embedded dropwatch 未能写入 perf stream 的累计事件数;仅在 map 计数可用时有效。
lost_samples 用户态 reader 从 PERF_RECORD_LOST 累计的丢失样本数;map 读取失败时仍有效,为零时省略。
rate_limited embedded dropwatch 被限速拒绝的累计事件数;仅在 map 计数可用时有效。

状态读取失败时,map_counters_available=false,perf_lost、rate_limited 的零值表示不可用,快照仍保留 lost_samples。先尝试输出本批事件,再返回状态错误;写出失败则停止该批次,并保留两个错误。已匹配事件保留 matched 与丢包元数据,不附加未匹配状态快照。

计数绑定本次 dropwatch 实例,重新加载时归零;map 与 reader 分别采样,不构成原子快照。它们不能证明某条重传未匹配的根因,丢失和限流也不会追加或替换关联原因。

5.4 使用条件与排查方式

观测结果 检查项
software 结合 drop_reason、tuple、方向、sequence、namespace 检查匹配栈。
hardware 按 drop_reason_group、trap 名称和驱动文档检查硬件丢包;匹配仍是基于报文证据的关联。
unknown 且 loss counter 非零 收紧共同 filter、增大 perf 容量或调整 embedded dropwatch 限速后重新采集。
correlation_reason=wait_timeout 100ms 内没有严格候选;结合诊断标记和计数排查。
correlation_reason=queue_full 等待队列已满,收紧采集范围。
correlation_reason=interrupted 检查采集停止或错误,关联等待被提前结束。
matched_net_namespace=true,但关联未匹配 同一 flow 下已观测到相同 namespace 的 drop,继续检查报文和时间条件;该标记本身不构成完整关联。
correlation_reason=warmup 等待到期,且重传发生时间早于 source ready;继续采集后续重传,本次结果无法排除 source ready 之前的丢包。
drop_location 不存在 off 模式。

huatuo-bamai 会向 local 关联的两个输入传入同一个规范化 EventTracing.TCPRetransmit.Filter。采集范围一致可以避免两个 source 观察不同流量,但在缺少可靠因果起点边界时,no-match 仍不能成为确定结论。


🌟 结尾

6.7 - IRQ 追踪

独立命令采集系统全部 CPU 或指定 CPU 的 irq/softirq 栈,并生成火焰图:其中 sources 表示发起 softirq 的来源,victims 表示被 softirq 直接抢占的任务。由 ksoftirqd 处理 softirq 时,ksoftirqd 是执行者而不是 victim,因此不会记录为 victim。huatuo-bamai 守护进程的 AutoTracing.IRQTracing tracer 正是通过 shell 调用这一工具;直接运行它即可得到相同的结果,无需任何存储后端或配置文件。

构建

常规项目构建会自动发现 cmd/irqtracing 并编译 bpf/irqtracing.c:

make build

命令输出到 _output/bin/irqtracing,BPF 对象输出到 _output/bpf/irqtracing.o;运行时需要通过 --bpf-path 指定该对象。

运行

sudo ./_output/bin/irqtracing \
  --bpf-path ./_output/bpf/irqtracing.o \
  --duration 5 > irqtracing.folded

--target-cpu 可选,默认值为 -1,表示跟踪系统中的全部 CPU。工具只挂载 irq/softirq_raise / irq/softirq_entry tracepoint;全量模式聚合所有 CPU 的 source 和直接被抢占的 victim,指定非负 CPU 时只保留该 CPU 的数据。两种模式都采集 --duration 秒(默认 3)。

--max-events-per-second-per-cpu N 可选,用于限制每个被跟踪 CPU 每秒采集的 source 和 victim 栈样本总数。省略该参数或设置为 0 时不限速;启用时 N 必须在 2 到 8589934590 之间。每 CPU 额度在 softirq_raise 和 softirq_entry 之间尽量均分;奇数额度多出的一个样本分给 raise。全 CPU 模式下,系统总额度随被跟踪 CPU 数量增长。

本地结果写入 stdout。--output text 是默认值,也是 collapsed 的兼容别名, 输出可供火焰图工具使用的 folded stack:

source;source[raiser,NET_RX];raise_softirq_[k] 12
victim;victim[worker(123),NET_RX];work 4

使用 --output flamegraph 或 --output svg 可直接输出 SVG 火焰图:

sudo ./_output/bin/irqtracing \
  --bpf-path ./_output/bpf/irqtracing.o \
  --duration 5 \
  --output flamegraph > irqtracing.svg

需要平台 profile 数据和丢样计数时使用 --output json:

sudo ./_output/bin/irqtracing \
  --bpf-path ./_output/bpf/irqtracing.o \
  --duration 5 \
  --output json > irqtracing.json

JSON 内容:

{
  "flamedata": { "...": "profile 树,未采集到任何数据时为 null" },
  "nmissed": 1234
}

flamedata 与平台消费的 profile 格式一致(ProfileType 为 irqtracing:irq:count:irq:count)。每条栈以 source 或 victim 为根,后接 source[comm,VEC] 或 victim[comm(pid),VEC] 标签帧,再依次是用户态栈帧和带 _[k] 后缀的内核态栈帧。VEC 取值为 HI、TIMER、NET_TX、NET_RX、BLOCK、IRQ_POLL、TASKLET、SCHED、HRTIMER、RCU 之一,未知向量显示为 VEC<n>。

nmissed 是采集窗口内丢弃的样本总数。样本丢弃要么来自 --max-events-per-second-per-cpu 启用的 first-N 预算,要么来自 counts 映射已满——两者都意味着火焰图不完整。当丢弃计数无法读取时,工具会直接失败而不是写出结果。非零的 nmissed 同时会在 stderr 输出一条告警;JSON 字段是守护进程消费的持久化契约。

--output-storage <socket> 会通过 Toolstream 发送 JSON,而不在本地输出,且 必须同时指定 --task-id;此时显式指定的 --output 会被忽略。运行日志被 丢弃,本地结果写入 stdout,失败信息和丢样告警写入 stderr。

进程在以下情况退出:采集时长到达、调用方取消、或收到 SIGHUP、SIGQUIT、SIGINT、SIGTERM 信号。

由 huatuo-bamai 调用

守护进程的 AutoTracing.IRQTracing tracer 在其规则检测到某个 CPU 出现 irq/softirq 突增或持续高利用率时,会调用守护进程可执行文件旁的 irqtracing 二进制(CoreBinDir 由守护进程自身路径推导;源码构建树中即 _output/bin/irqtracing),传入 --bpf-path <CoreBpfDir>/irqtracing.o、--target-cpu <cpu>、--duration <RunTracingToolTimeout> 和 --max-events-per-second-per-cpu <MaxEventsPerSecond>。MaxEventsPerSecond 默认是 1000,因此 raise 和 entry 默认各限制为 500/s。CLI 通过 Toolstream 返回结果并合并进保存的 tracing 数据;守护进程另外记录 rule、trigger_cpu、trace_duration 和 hit_cpus。

7 - 开发手册

7.1 - 扩展观测能力

HUATUO 支持 Metrics、Event 和 AutoTracing 三种观测扩展。三者共享注册机制, 但触发方式、运行开销和数据输出不同。

类型 触发方式 数据输出 适用场景
Metrics 周期采集 Prometheus 持续观测系统性能和长期趋势
Event 内核事件或阈值触发 ES、本地文件,可选 Prometheus 常态运行并捕获异常现场
AutoTracing 系统异常触发 ES、本地文件,可选 Prometheus 按需执行开销较高的上下文采集

模式说明

Metrics

Metrics 通过 procfs、sysfs 或 eBPF 周期采集系统状态,以 Prometheus 格式输出, 适合实时监控和长期趋势分析。内置采集能力包括:

  • CPU:sys、usr、util、load、nr_running 等。
  • 内存:vmstat、memory_stat、directreclaim、asyncreclaim 等。
  • IO:d2c、q2c、freeze、flush 等。
  • 网络:ARP、socket memory、qdisc、netstat、netdev、sockstat 等。

Event

Event 常态监听内核事件或阈值条件,在异常发生时保存内核运行上下文。该模式 面向需要持续开启的低开销观测,数据写入 Elasticsearch 和本地文件,也可以 同步生成 Prometheus 指标。内置事件包括:

  • 调度 tick 间隔异常 sched_tick。
  • 内存异常分配 memory_oom_kill。
  • 软锁定 softlockup。
  • D 状态进程 hungtask。
  • 内存回收 memory_reclaim_events。
  • 异常丢包 dropwatch。
  • 网络接收延迟 net_rx_latency。

AutoTracing

AutoTracing 在检测到系统异常后自动调用诊断工具,采集火焰图或上下文快照等 现场信息。它适合采集成本较高、无法持续运行的诊断数据。结果写入 Elasticsearch 和本地文件,也可以转换为 Prometheus 指标。内置能力包括:

  • CPU 空闲和系统态异常追踪 cpuidle、cpusys。
  • D 状态负载追踪 dload。
  • 内存突发分配追踪 memburst。
  • 磁盘 IO 异常追踪 iotracing。

Event 和 AutoTracing 都属于 Tracing 模式,共享 ITracingEvent 接口。它们既能 保存异常上下文用于根因分析,也能通过实现 Collector 将统计结果暴露给 Prometheus。

添加 Metrics

自定义 Metrics 通过 /metrics 接口输出 Prometheus 指标。

实现 Collector

在 core/metrics 下创建实现 Collector 接口的类型:

type Collector interface {
    Update() ([]*Data, error)
}

type exampleMetric struct{}

func (c *exampleMetric) Update() ([]*metric.Data, error) {
    return []*metric.Data{
        metric.NewGaugeData("example", value, "example value", nil),
    }, nil
}

注册采集器

通过 FlagMetric 将实现注册为指标采集器:

func init() {
    tracing.RegisterEventTracing("example", newExampleMetric)
}

func newExampleMetric() (*tracing.EventTracingAttr, error) {
    return &tracing.EventTracingAttr{
        TracingData: &exampleMetric{},
        Flag:        tracing.FlagMetric,
    }, nil
}

管理 BPF 对象

同一个实现同时提供 Start 和 Update 时,两个方法可能并发执行。不要直接在 collector 字段中读写 bpf.BPF 接口;使用 internal/bpf/bpf_ref.go 提供的 Reference 和 Lease 管理对象生命周期:

type example struct {
    object bpf.Reference
}

func (c *example) Start(ctx context.Context) (retErr error) {
    object, err := bpf.LoadBpf(bpf.ThisBpfOBJ(), nil)
    if err != nil {
        return err
    }

    if err := object.Attach(); err != nil {
        return errors.Join(err, object.Close())
    }
    if err := c.object.Publish(object); err != nil {
        return errors.Join(err, object.Close())
    }
    defer func() {
        retErr = errors.Join(retErr, c.object.UnPublish())
    }()

    <-ctx.Done()
    return nil
}

func (c *example) Update() ([]*metric.Data, error) {
    lease, ok := c.object.Acquire()
    if !ok {
        return nil, nil
    }
    defer lease.Release()

    items, err := lease.DumpMapByName("example_map")
    if err != nil {
        return nil, fmt.Errorf("dump example_map: %w", err)
    }

    return buildMetrics(items), nil
}

接口约束:

  • Publish 将对象所有权转移给 Reference。成功后不要直接调用object.Close()。
  • Acquire 返回的 Lease 将 BPF 对象固定到本次 Update 结束;必须配对调用Release,且不要复制 Lease。
  • UnPublish 先阻止新的 Acquire,再等待所有 Lease 释放,最后关闭 BPF 对象并返回关闭错误。
  • Publish 和 UnPublish 必须串行调用。当前框架保证同一实例的 Start 不会并发执行。

因此,已经开始的 Update 可以完整使用原 BPF 对象;Start 退出或重启时, 对象只会在这些 Update 完成后关闭。

添加 Event

Event 需要实现 ITracingEvent:

type ITracingEvent interface {
    Start(ctx context.Context) error
}

type exampleEvent struct{}

func (e *exampleEvent) Start(ctx context.Context) error {
    // 检测事件并采集上下文。
    // storage.Save 将数据写入 ES 和本地存储。
    storage.Save("example", containerID, time.Now(), eventData)
    return nil
}

注册时使用 FlagTracing:

func init() {
    tracing.RegisterEventTracing("example", newExampleEvent)
}

func newExampleEvent() (*tracing.EventTracingAttr, error) {
    return &tracing.EventTracingAttr{
        TracingData: &exampleEvent{},
        Interval:    10,
        Flag:        tracing.FlagTracing,
    }, nil
}

如果事件还需要输出 Prometheus 指标,可以同时实现 Collector,并将 tracing.FlagMetric 合并到 Flag。

添加 AutoTracing

AutoTracing 与 Event 使用相同的 ITracingEvent 接口和注册框架:

type exampleAutoTracing struct{}

func (t *exampleAutoTracing) Start(ctx context.Context) error {
    // 异常触发后采集上下文。
    storage.Save("example", containerID, time.Now(), tracingData)
    return nil
}

func init() {
    tracing.RegisterEventTracing("example", newExampleAutoTracing)
}

func newExampleAutoTracing() (*tracing.EventTracingAttr, error) {
    return &tracing.EventTracingAttr{
        TracingData: &exampleAutoTracing{},
        Interval:    10,
        Flag:        tracing.FlagTracing,
    }, nil
}

项目中的 core/metrics、core/events 和 core/autotracing 提供了完整实现示例, 包括 BPF map 交互、容器信息获取、数据存储和 Prometheus 输出。

7.2 - 开发调试

全组件联调

开发 Compose 使用当前工作区源码构建开发镜像,并启动采集器、API Server、 Elasticsearch、Prometheus 和 Grafana。采集器需要访问宿主机内核和 cgroup, 因此应在 Linux 主机的项目根目录使用 root 权限运行:

sudo make compose-dev-up

Compose 在前台聚合所有组件日志。修改源码后,按 Ctrl+C 停止环境并重新执行 命令;Docker 会复用工具链和 Go 编译缓存。

调试结束后删除容器、数据卷和开发镜像:

sudo make compose-dev-down

该命令会删除 Elasticsearch 数据卷。需要保留联调数据时,不要执行该命令。

BPF 调试

BPF 代码可以使用 bpf_dbg() 和 bpf_dbg_msg() 宏在内核态输出调试信息。 宏定义位于 bpf/include/bpf_dbg.h。调试功能包含编译时和运行时两级开关, 默认完全关闭。

添加调试埋点

每个使用调试宏的 BPF 源文件都需要声明独立的调试 map:

#include "bpf_dbg.h"

BPF_DBG_MAP(native_cpu);

SEC("perf_event")
int prog(void *ctx)
{
        bpf_dbg_msg(ctx, native_cpu, "enter prog");
        bpf_dbg(ctx, native_cpu, "pid and addr", pid, addr, 0);
        return 0;
}

bpf_dbg_msg() 只输出消息;bpf_dbg() 还可以携带最多三个 u64 参数。

编译调试对象

通过 BPF_DEBUG=1 将 -DDEBUG_BPF 传给 Clang:

make BPF_DEBUG=1

只重新生成构建产物而不重新编译二进制:

make BPF_DEBUG=1 gen-build

BPF_DEBUG=0 是默认值。此时宏展开为空操作,调试 perf event array、事件结构、 bpf_ktime_get_ns 和 bpf_perf_event_output 都不会进入 BPF 对象。

启用运行时输出

编译调试对象后,启动 profiler 时还需要增加 --log-bpf-debug。当前只有 native profiler 支持该开关:

./profiler --type cpu --language native --log-bpf-debug ...

加载 BPF 对象时,bpf.NewDbg(true) 会在 LoadBpf 前将 bpf_dbg_enabled 常量改写为 1。未启用时,verifier 会将对应分支作为死代码 消除。每个 BPF 对象维护独立开关,互不影响。

查看调试输出

用户态以 Debug 级别输出调试事件:

  • file:BPF 源文件名。
  • line:源文件行号。
  • ts:转换为 UTC 墙钟时间的事件时间戳。
  • msg:调试消息。
  • args:最多三个 u64 参数;全部为零时省略。
bpf_dbg: file=native_oncpu_profiler.c line=120 ts=2026-01-11T08:30:00.123456Z msg=enter prog args=[0x1f4 0xffff8881 0x0]

只有同时使用 BPF_DEBUG=1 编译并在运行时指定 --log-bpf-debug,才会产生 调试输出。

7.3 - 集成测试

集成测试用于验证 huatuo-bamai在使用模拟的 /proc 和 /sys 文件系统时,能够正确启动并对外暴露符合预期的Prometheus指标。

测试运行的是真实的可执行文件,并通过校验 /metrics 接口的输出结果,确保指标采集与暴露逻辑正确,而不依赖宿主机的内核或硬件环境。

脚本执行流程

该集成测试脚本主要包含以下步骤:

  1. 生成临时的bamai.conf配置文件
  2. 使用模拟的 procfs 和 sysfs 启动 huatuo-bamai 服务
  3. 等待 /metrics 接口可访问
  4. 从 /metrics 接口拉取所有指标数据
  5. 校验所有预期指标是否存在且内容匹配
  6. 停止服务并清理相关资源
  7. 若任意一个预期指标缺失或不匹配,测试将直接失败

运行方式

请在项目根目录下执行集成测试:

bash integration/run.sh

指定文件名可以只运行一个集成测试,第二个参数指定循环次数,默认为 1:

bash integration/run.sh test_metrics_exclude_filter.sh 10

或通过 Makefile 执行:

make integration

前置检查与结果汇总

两套测试共用 integration/run.sh,默认运行 integration;e2e/run.sh 转发到 --suite e2e。现有 make integration、make e2e 和单用例入口继续可用:

bash integration/run.sh --suite e2e
bash integration/run.sh --suite e2e test_metrics.sh 2
bash e2e/run.sh test_metrics.sh 2

两套测试都在独立的 UTS、mount namespace 中运行,hostname 为 huatuo-dev, 挂载传播设为 private。integration 由用例启动服务;e2e 在每例之前启动默认 bamai, 结束时停止服务并检查日志。integration 成功或跳过后删除工作目录,失败时保留; e2e 保留工作目录。

runner 只检查 _output 目录是否存在,不逐个检查项目二进制和 BPF 产物。 直接运行脚本前先执行 make build; 目录存在不代表构建完整,缺失产物会在实际使用时导致失败。

用例使用同一个函数检查 PATH 中的命令或可执行文件路径,缺失时跳过:

require_commands jq curl ss
require_commands "${PROFILER_TOOL_DIR}/bin/asprof"

证书等输入文件使用 require_readable 检查,不可读时输出具体路径并跳过:

require_readable "${KUBELET_CERT}" "${KUBELET_KEY}"

skip 输出原因并以 77 退出,仍会执行 EXIT 清理。runner 将 0 记为 PASS、 77 记为 SKIP,其他状态记为 FAIL;清理失败也记为 FAIL。只有成功和跳过时, runner 返回 0。失败时停止后续用例,并输出截至当前的统计。

每次执行都单独计数,包括重复运行,例如:

summary: total=5 passed=3 skipped=2 failed=0

两套测试缺少 root 权限或 namespace 命令时,所选用例全部计为 SKIP。

失败时的行为

  • huatuo-bamai 服务指标和日志将直接输出到标准输出,便于问题定位
  • 临时工作目录将被保留,用于后续调试分析

成功时的行为

  • 显示验证成功的metrics 列表

后台进程清理

stop_and_wait_by_pid PID [TIMEOUT] 停止并等待子进程,返回其退出码。 未传入 PID 或 PID 为空时立即返回成功,调用方可以直接传入已初始化的 PID 变量。 等待结束后清空变量;尽力清理时使用 || true,退出码属于测试契约时应进行断言。

网络命名空间辅助函数

网络测试可以在加载 lib.sh 后加载 integration/lib_namespace.sh:

  • namespace_setup NAME 创建一个网络命名空间,通过 NETNS 暴露名称。 调用方负责启用 lo 并配置所需地址。
  • namespace_setup_with_pair PREFIX SERVER_ADDR CLIENT_ADDR [PREFIX_LENGTH] 创建通过 veth 连接的两个命名空间,配置地址并启用接口,地址前缀长度默认为 24。 命名空间、接口和地址通过 NETNS_SERVER、NETNS_CLIENT、 NETNS_VETH_SERVER、NETNS_VETH_CLIENT、NETNS_SERVER_ADDR、 NETNS_CLIENT_ADDR 暴露。
  • namespace_cleanup 删除上述函数创建的命名空间并清空状态,支持重复调用。 用例应在 EXIT 清理中先停止命名空间内的进程,再调用该函数。

如何新增指标测试

第一步:新增或更新模拟数据

如果新增的指标依赖 /proc 或 /sys 文件内容,请在以下目录中新增或修改模拟数据:

integration/fixtures/

目录结构需与真实内核文件系统保持一致。

第二步:添加预期指标

在以下目录中新建一个文件:

integration/fixtures/expected_metrics/
├── cpu.txt
├── memory.txt
└── ...

每一行(非空、非注释行)表示一条期望的 Prometheus 指标,指标内容必须与 /metrics 接口返回结果完全一致,新增的*.txt 文件会被测试脚本自动加载并参与校验。

第三步:运行测试

bash integration/run.sh

当任意一个预期指标缺失或不匹配时,测试将失败。

7.4 - 在 GitHub Actions 中调试虚拟机

使用 OS Distro QEMU Test workflow 可以在支持的 Linux 虚拟机中测试分支, 无需在本地准备虚拟机。

准备 Fork 仓库

  1. Fork 仓库,并在 fork 仓库的 Actions 页面启用 workflows。
  2. 将 SSH 公钥添加到 GitHub 账号。只有启动 workflow 的用户 可以连接。
  3. 将需要调试的分支 push 到 fork 仓库。

启动虚拟机

  1. 在 fork 仓库中打开 Actions > OS Distro QEMU Test > Run workflow。
  2. 选择需要调试的分支。
  3. 保留默认值 ubuntu24.04、填写以逗号分隔的多个发行版,或者使用 all 选择所有支持的发行版。
  4. 在 Debug VM 选项中选择保留虚拟机的时机和时长。
  5. 启动 workflow,并打开对应的 Test in VM job。

如果没有 Run workflow 按钮,请先同步 fork 仓库的默认分支。GitHub 只会 显示默认分支中存在的手动 workflow。

选择 Before test 可以在测试前检查或修改虚拟机;选择 After test on failure 可以排查失败测试;选择 After test always 会在测试结束后保留 虚拟机。

连接虚拟机

打开 job 中的 Debug VM: Retain ... step,并按日志提示操作:

  1. 在本地终端运行日志中的完整 SSH 命令,连接 GitHub runner。
  2. 在 runner 中运行日志中的 ssh -i ... root@<VM_IP> 命令,进入虚拟机。

日志中的命令已经包含正确的密钥路径和虚拟机地址,直接复制即可。

结束调试

退出虚拟机,然后在 runner 中释放 workflow:

cd "$GITHUB_WORKSPACE"
touch continue

workflow 随后继续执行测试或清理。仅断开 SSH 不会释放 workflow;如果不执行 上述命令,workflow 会一直等待到所选保留时间结束。

7.5 - BPF ABI 指南

BPF 侧 C 结构体是 perf event ABI 的唯一来源。Go 类型由 BPF 对象中的 BTF 自动生成,禁止手工维护同构结构体。

约定

一个 ABI 域对应一个 C 头文件和一个 Go 生成文件:

项目 约定
域名 <domain>
C 头文件 bpf/include/abi/<domain>_types.h
C 结构体前缀 <domain>_
Go 生成文件 internal/bpf/abi/<domain>_types_generated.go

<domain> 必须以小写字母开头,只能包含小写字母、数字和下划线。避免使用 前缀重叠的域,例如 net 和 net_rx。

实现

以下示例创建 sample 域。

1. 定义 ABI 头文件

新建 bpf/include/abi/sample_types.h:

// Copyright 2026 The HuaTuo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __BPF_ABI_SAMPLE_H__
#define __BPF_ABI_SAMPLE_H__

#include "bpf_abi.h"

#define SAMPLE_DATA_LEN 4

struct sample_detail {
	u32 code;
	u8 data[SAMPLE_DATA_LEN];
};

struct sample_event {
	u64 kernel_observed_ns;
	struct sample_detail detail;
	u32 tgid;
	u8 kind;
	u8 pad[3];
};

BPF_ABI_EXPORT(sample_detail);
BPF_ABI_EXPORT(sample_event);

#endif /* __BPF_ABI_SAMPLE_H__ */

要求:

  • 结构体名称必须使用 <domain>_ 前缀。
  • 每个需要生成 Go 类型的结构体都必须调用 BPF_ABI_EXPORT(<type>),包括嵌套结构体。
  • 头文件只定义 ABI 类型和相关常量,不放置 BPF 程序、map 或业务逻辑。
  • 数组长度必须是编译期常量,且在所有 BPF 编译单元中保持一致。

2. 在 BPF 程序中使用

ABI 头文件依赖 u8、u16 等基础类型,应在 vmlinux.h 和项目基础头文件 之后包含:

#include "vmlinux.h"

#include "bpf_common.h"
#include "abi/sample_types.h"

发送 perf event 时直接使用 ABI 结构体:

struct sample_event event = {};

event.kernel_observed_ns = bpf_ktime_get_ns();
event.tgid = bpf_get_current_pid_tgid() >> 32;
event.kind = kind;

bpf_perf_event_output(ctx, &events, COMPAT_BPF_F_CURRENT_CPU,
		      &event, sizeof(event));

必须零初始化事件,避免将未初始化的 padding 写入 perf buffer。传给 bpf_perf_event_output 的结构体必须定义在 ABI 头文件中。

3. 生成并使用 Go 类型

在项目根目录运行:

make gen-build

生成结果位于:

internal/bpf/abi/sample_types_generated.go

Go 代码直接引用生成类型:

import "github.com/ccfos/huatuo/internal/bpf/abi"

var event abi.SampleEvent
if err := reader.ReadInto(&event); err != nil {
	return fmt.Errorf("read sample event: %w", err)
}

生成文件包含结构体、SampleEventSize 大小常量,以及基于 unsafe.Sizeof 和 unsafe.Offsetof 的布局断言。生成文件只读,禁止手工 修改。

类型和布局

类型 支持情况
1、2、4、8 字节定宽整数 支持
非零长度的定长数组 支持
满足相同约束的嵌套结构体 支持
目标类型受支持的 typedef 支持
指针、union、enum、浮点类型 不支持
位域、_Bool 不支持
零长度数组、柔性数组 不支持
递归、字段重叠或非字节对齐结构体 不支持

布局要求:

  • 使用 u8、s16、u32、s64 等定宽整数。
  • 按对齐要求排列字段,必要时使用 u8 pad[N] 明确 padding。
  • 不使用 long、unsigned long 等随平台变化的类型。
  • 不使用 __attribute__((packed)) 绕过布局校验。
  • 同名结构体出现在多个 BPF 对象中时,字段、偏移和大小必须完全一致。

C 名称按下划线转换为 Go 导出名称,例如 sample_event 转换为 SampleEvent,kernel_observed_ns 转换为 KernelObservedNS。字段语义和跨层命名遵循 事件字段命名规范。避免使用会映射为相同 Go 名称的 C 名称,例如 sample_id 和 sample_i_d。

验证

新增或修改 ABI 后执行:

make gen-build
go test ./build/bpfabi-tool
make check

新增 perf event 还应增加解码测试,至少覆盖:

  • 整数边界值和本机字节序。
  • 嵌套结构体和数组首尾元素。
  • padding 前后的字段。
  • 样本大小与生成的 <GoType>Size 常量。

常见错误

错误 检查项
has no "<domain>_" btf anchors 头文件是否被 BPF 源文件包含;类型是否调用 BPF_ABI_EXPORT
without matching abi header 头文件名与结构体前缀是否一致;域前缀是否重叠
differs between objects 条件编译、数组长度宏和依赖头文件是否导致布局变化
go offset is ... btf offset is ... 调整字段顺序或增加显式 padding,不要使用 packed
go type name ... collides 重命名映射为相同 Go 名称的 C 类型或字段

7.6 - TCP retransmit 与 dropwatch 关联的难点

本文说明 local correlation 的证据边界与实现。两条事件流没有共同事件 ID, 关联只能根据 namespace、四元组、TCP sequence/ACK 和单调时间作保守推断。

1. 结果语义

结果 含义
software 找到满足全部严格条件且尚未被消费的 host software drop。
hardware 找到满足相同严格条件的 devlink DROP trap 记录。
unknown 未找到严格匹配,或匹配记录的来源枚举未知;correlation_reason 明确区分已匹配与未匹配终态。

no-match 不能证明问题位于网络或硬件。drop 可能发生在采集启动前、另一个 network namespace,或记录虽送达但缺少 TCP 匹配字段。

shutdown 使用同一个时间点处理等待项:已到期项为 warmup 或 wait_timeout,未到期项为 interrupted。这些未匹配结果均输出 drop_location=unknown 和独立诊断信息。

2. 三种运行场景

  1. tcpshark --bpf-path <tcp_retransmit.o> 只采集并直接输出重传。
  2. tcpshark --with-dropwatch --bpf-path-dir <dir> 在一个进程内加载 tcp_retransmit.o 与 net_dropwatch.o,统一持有两条 perf 输入、timer、输出和关闭。
  3. huatuo-bamai 的 standalone dropwatch 仍由 cmd/dropwatch 独立运行,只输出 raw DropWatchTracing,与 embedded source 不共享状态。

三种 retransmit hook 在两种模式下都使用 synthetic L3 filter。关联模式只使用 EventTracing.TCPRetransmit.Filter;空值规范化为 tcp,同一表达式传给两个 BPF 对象。依赖 Ethernet 地址、无法在 synthetic L3 输入上等价执行的表达式会在 启动前被拒绝。EventTracing.Dropwatch.Filter 只控制 standalone dropwatch。

两个命令都使用 HardwareAuto 自动检测 devlink trap 支持。tracepoint 不可用时 输出 warning 并继续软件采集;可用时只接收驱动上报的 DROP trap。 --device / --device-excluded 与独立 dropwatch 同名、互斥,只过滤 embedded source 的软件及硬件记录,要求 --with-dropwatch。重传输入仍使用共同的 L3 filter。

丢包来源和原因由 internal/dropwatch.ResolveMetadata 统一解析。软件 reason 从每会话一次加载的 BTF 表查找;未知值为十进制数字,旧内核不支持为 NOT_SUPPORTED。硬件 reason 和 group 分别来自 trap 名称与分组,不查软件 reason 表。reader 保留独立的来源和 reason 字符串,不借用可复用的 ABI buffer。 成功匹配后输出 drop_source、drop_reason、drop_reason_group;no-match 省略这些字段,输出唯一关联结束原因和独立诊断。drop_location 在匹配时直接使用 drop_source,未匹配时为 unknown;它表示关联分类,不同于独立 dropwatch 的内核地址。未知来源不根据 reason 或 stack 推断。 硬件记录缺少 namespace 或 TCP 匹配字段时无法建立严格匹配。

3. 两条时间约束

dropwatch 与 retransmit 使用不同 perf reader,事件还可能来自不同 CPU,因此 用户态接收顺序不等于内核发生顺序:

CPU 2: drop       monotonic_ns=180,暂留在 dropwatch ring
CPU 0: retransmit monotonic_ns=200,先到达用户态
用户态: retransmit(200) -> drop(180)

实现使用两个不同维度的窗口:

retransmitRetentionDuration = 100 * time.Millisecond
maxDropToRetransmitAge     = time.Second
  • 100ms 是用户态送达乱序预算。retransmit 先到时进入等待队列,到期仍未匹配则 输出 unknown。处理任一新事件前先结算已经到期的记录;timer 只负责唤醒, 不决定 deadline 语义。
  • 1s 是因果候选年龄,使用 BPF kernel_observed_ns 判断。候选必须满足:
drop.kernel_observed_ns <= retransmit.kernel_observed_ns
retransmit.kernel_observed_ns - drop.kernel_observed_ns <= 1s

observed_timestamp 是用户态墙上时间,受调度和系统时间调整影响,不参与匹配。 固定 1s 是当前支持合同,不等于 Linux 所有 RTO 的理论上限;窗口外结果保持 unknown。

4. 有界状态

等待中的 retransmit 与尚未匹配的 drop 共用包内泛型存储 store[T],分别持有 独立实例。容器只负责容量、TTL、flow 索引和删除;严格匹配与结果定型由关联器负责。

type storeEntry[T any] struct {
    value    T
    flow     *flowKey
    deadline time.Time
    sequence uint64
    node     *list.Element
}

type store[T any] struct {
    byDeadline   list.List
    byFlow       map[flowKey][]*storeEntry[T]
    capacity     int
    ttl          time.Duration
    nextSequence uint64
}
实例 存储值 容量 TTL
retransmitStore waitingRetransmit,内联在 entry 中 1024 100ms
dropStore *dropEvent 4096 1s + 100ms

waitingRetransmit 只包含原始事件、匹配字段和跨 namespace 候选标记; 不再持有链表节点、deadline 或独立 ID。drop 也无需额外的缓存包装类型。 链表的 Element.Value 与 flow 桶引用同一个 *storeEntry[T],entry 的 node 指回所属链表元素。entry 引用业务数据中不再修改的 flow,避免重复保存地址。 删除会同时解除两个索引的引用,并清空 flow 切片尾部; 最后一个条目移除后删除空桶。

flow 索引按两个 AddrPort 的规范顺序建键,正反向只占一个桶;原始方向仍保留 在业务数据中,供严格匹配使用。namespace 不进入索引键,保证跨 namespace 的相似候选仍能设置诊断标记。匹配扫描与切片删除为 O(k),k 是该 flow 的候选数; 定位最早 deadline 与链表摘除为 O(1)。

每个实例的 TTL 固定,关联循环传入单调不减的处理时间,因此链表插入顺序也是 deadline 顺序。链表用于到期清理和容量淘汰,不向业务暴露 FIFO 消费接口。 等待容量满时,最早 deadline 的记录以 queue_full 定型;drop 容量满时直接淘汰最早 deadline 的候选。因果判断仍使用 kernel_observed_ns。

两个 reader 通过无缓冲 channel 交付事件,容器只由单个关联循环访问,无锁、 无独立清理 goroutine。一个 timer 始终取两个实例的最早 deadline,因此仅有 drop 缓存、没有等待重传时也会按期清理。每次输入和 timer 唤醒均先清理到期项, 再匹配、检查容量;now >= deadline 即为过期。100ms 是匹配截止时间, 同步输出的阻塞仍可能延迟实际清理和结果送达。

5. 严格匹配

正向 segment 候选要求:

  • 相同 network namespace、地址族和方向四元组;
  • drop 不晚于 retransmit,且时间差不超过 1s;
  • TCP SYN/data/FIN sequence range 与重传 range 重叠;
  • RST 和不受支持的重传类型不产生正向匹配。

反向 ACK 候选使用反向四元组,并要求 ACK 覆盖重传 sequence end。SYN 与 SYN-ACK 使用各自更严格的 ACK/SYN 条件。

多个 drop 候选先选最大的 drop.kernel_observed_ns;时间相同时选较大的插入序号。 drop 后到时,选择插入序号最小的严格匹配重传,同时扫描其余候选以记录各重传的 namespace 匹配状态。严格匹配后立即从 deadline 和 flow 两个索引删除,只能消费一次。 正向 segment 和反向 ACK 共用 flow 索引,先检查 namespace,再检查报文和时间条件。 同一 flow 下出现相同 namespace 的 drop 时,将 netNamespace 置为 true; 即使时间或 sequence 不满足严格匹配条件,该标记也会保留,后续不匹配候选不会清除它。 严格匹配的结果必定为 true。无候选、只有其他 namespace 或 namespace 无法比较时, 该值为 false,表示未观测到匹配,不能据此断定 namespace 不同。 输出通过 NetNamespace 保留该状态,JSON 和文本字段为 matched_net_namespace,false 时省略。

6. 互斥的关联终态

types.CorrelationReason 为 string 类型;每次关联只输出一个 correlation_reason:

Go 常量 JSON 值 含义
CorrelationMatched matched 严格匹配成功,包括来源为 unknown 的 drop。
CorrelationUnsupported unsupported 当前规则无法处理该重传,不进入等待队列。
CorrelationWarmup warmup 等待到期仍未匹配,重传发生时间早于 source ready。
CorrelationWaitTimeout wait_timeout 等待到期仍未匹配,重传发生时间等于或晚于 source ready。
CorrelationQueueFull queue_full 该重传因等待队列已满,在到期前被淘汰。
CorrelationInterrupted interrupted 关联循环退出,未到期的等待被提前中断。

各结束分支直接赋值 correlationResult.reason。输出侧使用该枚举判定结果,并校验 matched 必须带 drop,其他五种终态不能带 drop;空值、未知值或矛盾组合返回错误。 关闭关联时省略输出字段;启用关联后所有已定型事件都包含一个有效终态。 单值字段替代原原因数组,工具端和接收端同步迁移。

等待到期时,按重传的内核时间选择终态:早于 source ready 时为 warmup, 等于或晚于 ready 时为 wait_timeout,包括 ready 后不足 1s 的情况。 drop 到重传的 1s 上限只用于候选匹配,不参与预热判定。用户态处理延迟 不会改变分类。该判定仅在到期分支执行,严格匹配、 不支持、队列淘汰和提前中断保留各自原因。预热不阻止匹配,也不提前结束等待。

matched_net_namespace 保留为独立诊断标记:同一 flow 下观测到相同 namespace 的 drop,独立于报文和时间条件;不能替代 correlation_reason=matched。

该标记可与限流、丢失计数并存,不改变唯一终态。无法规范化的 drop 与 drop 缓存容量淘汰不会直接结束重传等待,也不会生成重传级原因。

7. Perf 状态

状态由三个独立通道组成,各报各的、互不求和:

  • perf_lost:累计 per-CPU bpf_perf_out_dropwatch map(公共 perf output 计数设施 bpf_perf_output.h),BPF 在 bpf_perf_event_output 负返回时 +1(如当前 CPU 未 attach reader)。
  • lost_samples:用户态从 PERF_RECORD_LOST 累计的 ring buffer 溢出数,只在 reader 运行期间可见,处理丢失记录后即可通过 ReadStatus 查询。
  • rate_limited:限流状态 map bpf_rlimit_dropwatch 的 total_missed。

ReadStatus 成功时设置 HasMapCounters=true,JSON 为 map_counters_available=true。出错时该标记为 false,PerfLost、RateLimited 为零,表示不可用而非没有丢失或限流;LostSamples 仍有效,输出保留该快照。 这些值为实例累计计数,map 和 reader 分别采样,不能推断某条重传未匹配的根因。

用户态汇总所有 CPU 的 perf_lost,每次返回当前快照,不检查计数回退或 uint64 加法溢出。该状态只说明 证据完整性,不会把 no-match 提升为确定性网络分类。旧的 active epoch、 旧的双 slot、inflight、frontier 和 drain 水位机制已删除。

关联侧根据 IPv4 total length 或 IPv6 payload length 计算 TCP sequence span。 GSO/offload 下 IP header 长度可能无法覆盖完整 skb;当前不据此扩大匹配范围。

dropwatch 热路径为:

software: hardware marker lookup/delete -> device/pcap filter
          -> status map lookup -> bpf_ktime_get_ns() -> rate limit -> perf output
hardware: device/pcap filter -> status map lookup -> bpf_ktime_get_ns()
          -> rate limit -> perf output

因此 filter 拒绝的事件不支付状态 lookup、事件时间 helper 或计数更新成本; software drop 会先消费可能存在的 hardware dedup marker,避免被拒绝的 kfree 路径遗留 marker。

8. Shutdown

tcpshark 使用一个 errgroup.WithContext 管理 retransmit rate-limit reader、 retransmit reader、embedded dropwatch reader 和关联循环。所有命令 worker 共享同一个 groupCtx;任一 worker 返回错误后取消其余 worker,Wait 返回首个 worker 错误。资源关闭错误由各 owner 使用 errors.Join 合并,不再为 shutdown 维护私有 错误聚合 group。

internal/dropwatch.Tracer 持有 dropwatch 的 object、reader 和限流告警 worker。 实例 context 派生自 groupCtx;限流告警失败会取消实例读取,具体错误由 Close 返回。ReadInto 在读取前检查关闭和取消原因;进入底层读取后原样返回读取结果, 不再以随后发生的关闭或取消覆盖结果。ReadStatus 在 context 取消后仍可读取最终计数。 Close 先取消实例、 detach 并关闭事件与告警 reader,再等待告警 worker 退出,最后释放 object; 关闭告警 reader 会唤醒空闲读取,无需等待轮询期限。 状态类型为 types.DropwatchStatus,输出字段为 drop_perf_status,包含 map 计数可用性标记。

正常结束或 worker 失败时:

  1. 取消共享 groupCtx;
  2. 两个 ReadInto reader 和关联循环退出;
  3. 不再读取 dropwatch perf ring 中尚未交给关联器的记录;
  4. 调用 drainRetransmits(now),以同一时间先结算到期项,再清空未到期项, 按 deadline 顺序写入 output;
  5. 等全部 worker 退出后,关闭 dropwatch 与 retransmit Tracer,收集内部告警 worker 和资源释放错误;
  6. 最后结束 socket output;调用者传入的 io.Writer 不由会话关闭。

shutdown 已到期项输出 warmup 或 wait_timeout,未到期项输出 interrupted,均保留 drop_location=unknown、诊断标记和 perf 状态。已匹配或已淘汰的事件不会重复定型。 尾部 drop 仍可能丢失,因此原本可以匹配的重传也可能被提前结束。

9. 文件职责

cmd/tcpshark/
├── main.go                         # 信号、版本、CLI 入口
├── cli.go                          # 参数校验、路径与 filter 规范化
├── run.go                          # 全局 BPF 生命周期、超时、功能分派
└── retransmit/                     # 重传功能包
    ├── config.go                   # Config、RunConfig
    ├── run.go                      # Run:资源组合、执行和清理
    ├── tracer.go                   # Open、ReadInto、Close 和限流 worker
    ├── bpf_load.go                 # loadBPF、attachBPF 和探针选择
    ├── event_reader.go             # 双流读取、转换和 channel 交付
    ├── correlation_loop.go         # select、timer、退出结算
    ├── correlation.go              # 关联状态机、过期结算
    ├── correlation_match.go        # 候选选择、消费与 namespace 匹配状态
    ├── match.go                    # 匹配类型、namespace/flow/sequence/ktime
    ├── store.go                    # 条目存储、flow 索引、容量与过期管理
    ├── event.go                    # 两类 ABI record 转换
    ├── classify.go                 # 重传分类
    ├── correlation_output.go       # 状态补全、符号解析、关联输出字段
    └── output.go                   # text、JSON、socket 输出

顶层通过 retransmit.Run(ctx, *RunConfig) 运行重传功能,不操作 ABI record、 channel、关联 timer 或状态统计。CLI 先校验标量及参数组合,再编译检查 filter。 设置非空 --output-storage 时不校验 --output 的值;若显式指定后者, 向 stderr 提示其被忽略。未设置 storage 时仅接受 json 或 text。 RunConfig.Tracing 是采集配置, Dropwatch 为 nil 时直接读取并输出;非 nil 时使用两个读取 worker 和单个关联 循环。关联能力是重传功能的组合,不是通用采集框架。

Run 在 attach 探针之前建立输出,返回前完成全部会话 worker 等待、最终结算、 Tracer 关闭及输出结束。配置失败、取消与运行失败均释放已取得资源。 关闭时不保证排空内核缓冲区。普通模式不创建关联 channel 或 timer。

retransmit.Tracer 仍只拥有重传 BPF 对象、reader 和限流告警 worker; 事件转换、分类、关联及输出属于同包的功能会话,不进入 Tracer。 Open 失败会回滚全部已获取资源;会话 context 取消后仍需调用 Close。限流 worker 失败会取消采集,原始错误同时由 Close 保留。 ReadInto 已开始读取后直接保留底层结果,不再检查关闭或取消状态。 Close 可以打断读取,但调用者必须串行调用 Close。 进程级 bpf.Init/Shutdown 和运行超时由命令层统一管理。

关联器在处理每个输入和 timer 时统一清理过期候选,缓存不重复执行过期检查。 关联器返回匹配证据、单值终态和诊断标记,不修改输出字段;correlation_output.go 统一构造最终输出, 每个非空输出批次在入口读取一次 dropwatch 状态,空批次不读取;仅对匹配 drop 解析符号。 即使本批全部匹配也读取状态,读取失败时仍写出本批事件,再返回状态错误; 若写出失败则停止本批输出,并合并写出错误与状态错误。只有 no-match 事件携带 状态快照与诊断标记;读取失败也保留有效的 reader 计数,匹配事件不附加这些字段。 内部重传事件只保留 ABI record 和读取成功时的 observedAt;等待队列不保存 完整的展示对象。匹配直接从 ABI 地址构造 netip 地址,使用 ABI 事件枚举, 不经过字符串转换。IPv4-mapped IPv6 仍归一化为 IPv4。 单事件读取统一处理丢失重试和读取时间;普通模式复用事件并同步输出, 关联模式直接读取到独立事件后发送,避免覆盖等待中的数据。 run.go 统一创建 channel、启动读取 worker,并由发送方关闭 channel; 读取函数不启动 goroutine,不使用消费回调或借用事件合同。 最终输出时才构造 TCPRetransmitTracing;ObservedTimestamp 使用原读取时间, 不使用关联结束时间。ABI、SYNACK flags 补全和匹配规则保持不变。

cmd/tcpshark/retransmit 不提供 Go internal 导入限制;约定仅由 tcpshark 使用,不反向依赖命令层。两个命令共享的采集实现位于:

internal/dropwatch/
├── config.go    # Config、HardwareMode 和配置校验
├── tracer.go    # Tracer API、newTracer 资源接管、回滚及 worker 生命周期
├── load.go      # loadBPF、attachBPF:加载、map/reader 准备及探针挂载
├── netdev.go    # 设备过滤与 map 配置
├── status.go    # ReadStatus:输出失败、ring 丢失和限流计数
├── reason.go    # LoadReasonNames:内核 BTF reason 表和数字回退
├── metadata.go  # ResolveMetadata:来源、软件 reason、硬件 trap/group
└── decode.go    # DecodePacket:ABI header 适配及 packet.Parse

Open(ctx, *Config) 返回具体的 *Tracer。每个实例只能有一个 ReadInto 调用者;状态读取和关闭允许并发,但同一实例的 Close 必须由资源所有者串行调用。 首次关闭返回清理错误;后续调用返回 nil,不重试清理,也不重复返回首次错误。 调用方拥有全局 BPF 初始化和关闭,且必须在最终 结算后关闭 Tracer。DecodePacket 的返回值不借用原始 record 内存。 符号化和 DropWatchTracing 展示转换仍位于 standalone 命令,TCP 关联直接保留 ABI 中的 KernelObservedNS、namespace、stack PC 和已解析的丢包来源及原因。没有新增通用 BPF 消费循环接口。

7.7 - 时间格式约定

面向集成与存储的统一时间戳格式。

时间格式约定

HUATUO 统一以 UTC 和固定九位纳秒精度序列化时间戳:

2006-01-02T15:04:05.000000000Z

例如,2026 年 7 月 22 日 00:30:00.123456789 的事件会写为:

2026-07-22T00:30:00.123456789Z

为什么使用固定 UTC 精度

  • 所有主机都使用 Z 时区,分布式查询无需处理不同的本地时区。
  • 九位小数使字符串宽度固定,因此字典序与时间序一致,便于日志文件和关键字索引存储排序。
  • 纳秒精度能够保留 Go time.Time 与事件追踪流水线提供的时间信息。

集成建议

向 HUATUO 写入数据时,请使用统一辅助函数:

timestamp := timeutil.FormatUTC(time.Now())

读取时间戳时使用 timeutil.Parse。它既支持统一格式,也支持包含 0 到 9 位小数的 RFC 3339 旧数据;返回值始终会规范化为 UTC。

只有在两侧均由 FormatUTC 生成字符串时,才应直接按字符串比较时间。第三方 RFC 3339 值可能使用可变小数精度或非 UTC 偏移。

7.8 - 事件字段规范

事件字段必须先统一语义,再按各层的惯例命名。相同名称不得表示不同概念。

分层规则

层 规则 示例
BPF C 名称体现内核原始语义和单位 tgid、kernel_observed_ns
Go MixedCaps;缩写保持全大写 TGID、KernelObservedNS
JSON 使用面向用户的统计语义 pid、observed_timestamp
CLI 参数 小写短横线 --pid、--cpuid
CLI 文本和表头 使用标准大写缩写 PID、CPU、COMM

CPU 和任务

概念 定义 BPF C Go JSON CLI
逻辑 CPU 编号 bpf_get_smp_processor_id() 返回的编号 cpu CPU cpu --cpu、CPU
进程 ID 用户态进程标识,即 Linux TGID tgid 原始事件用 TGID,输出用 PID pid --pid、PID
线程 ID Linux task PID tid TID tid --tid、TID
父进程 ID 父进程 TGID parent_tgid ParentTGID 或输出层 ParentPID parent_pid PPID
task comm 内核 task 的 comm,可能是线程名 comm Comm comm COMM

时间和数值单位

概念 定义 BPF C Go JSON
内核观测原始时间 CLOCK_MONOTONIC 纳秒读数,不含系统挂起时间 kernel_observed_ns KernelObservedNS 不序列化,仅用于内部关联
内核观测时间 原始单调时间转换后的 UTC 时间 不适用 KernelObservedTimestamp kernel_observed_timestamp
用户态观测时间 事件生产者在用户态观测事件的 UTC 时间 不适用 ObservedTimestamp observed_timestamp
Unix 纳秒时间 Unix 纳秒时间 timestamp_ns TimestampNS timestamp_ns
纳秒时长 两个时间点的差值 <name>_ns <Name>NS <name>_ns
纳秒阈值 触发条件对应的时长 <name>_threshold_ns <Name>ThresholdNS <name>_threshold_ns
计数 无单位的累计值 <name>_count <Name>Count <name>_count
字节数 数据大小,不是位数 <name>_bytes <Name>Bytes <name>_bytes

容器、cgroup 和网络命名空间

概念 BPF C Go JSON CLI
容器 ID container_id ContainerID container_id --container-id、CONTAINER_ID
cgroup ID cgroup_id CgroupID cgroup_id CGROUP_ID
cgroup CSS 地址 <subsystem>_css_addr <Subsystem>CSSAddr <subsystem>_css_addr <SUBSYSTEM>_CSS_ADDR
网络命名空间 inode netns_inum NetNamespaceInum net_namespace_inum NETNS_INUM
网络命名空间 cookie netns_cookie NetNamespaceCookie net_namespace_cookie NETNS_COOKIE
网络设备索引 ifindex 或 <name>_ifindex Ifindex 或 <Name>Ifindex ifindex 或 <name>_ifindex IFINDEX

内核术语 inum 和 ifindex 在 Go 中保持一个单词,不拆成 INum 或 IfIndex。面向用户的 Go 和 JSON 字段将 netns 展开为 NetNamespace 和 net_namespace。

观测时间与兼容性

kernel_observed_timestamp、observed_timestamp 和 uploaded_timestamp 分别表示内核观测、用户态观测和存储写入时刻。内核观测表示 hook 执行时刻, 不保证等同于物理硬件错误或网络故障开始的时刻。

Document 顶层保存 UTC 时间,不保存 kernel_observed_ns。 没有内核时间的生产者及旧文档省略 kernel_observed_timestamp,不得用 用户态时间或写入时间补填。历史 RAS 文档的 observed_timestamp 曾表示 内核观测时刻,不能据此推算历史用户态观测时刻。

tcpshark 和 dropwatch 的 JSON/文本输出使用 kernel_observed_timestamp 表示内核观测时间。 工具和 Agent 应一起升级;旧文档保持原样,不自动回填或重写。 单调时钟与 UTC 的转换要求相同主机、同一次启动及主机时间命名空间。 跨越校时跳变或系统挂起的历史事件,仅凭单调时间不能精确恢复 UTC。

UTC 转换缓存单调时钟到实时钟的偏移:距上次成功采样满 1 小时后,由下一次 调用刷新,普通调用不延长缓存期限。采样失败返回错误,下次调用重试。 系统校时跳变或挂起不会提前触发刷新,偏移会在缓存到期后的下一次调用更新。

8 - 常见问题

指标

  • 为什么 memory_others_*(如 directstall_time)指标没有数据?

    memory_others 采集器读取的是滴滴云定制内核提供的 memory cgroup 扩展接口(memory.directstall_stat、memory.asynreclaim_stat、memory.local_direct_reclaim_time)。主线内核及常见发行版内核不提供这些接口,也没有可加载的内核模块能提供,因此在标准内核上这些指标不会输出,属预期行为。

    在标准内核上观测容器直接回收(direct reclaim)行为,请使用基于 eBPF 实现的 memory_reclaim_container_directstall 指标,详见「核心特性 / 内核全景观测」文档中的内存系统章节。

9 - 贡献

9.1 - 源码贡献

HUATUO 贡献指南

感谢你对 HUATUO 的关注!本指南将帮助你快速上手。


贡献方式

你可以通过多种方式参与 HUATUO:

  • 代码 — 修复 Bug、添加功能、优化性能
  • 文档 — 完善文档、翻译内容、编写教程
  • 测试 — 编写单元测试、集成测试、报告 Bug
  • eBPF — 添加新的内核探针、改进内核兼容性
  • 审查 — 审查其他贡献者的 Pull Request

开发环境

前置条件

工具 要求 说明
Go 1.24+ 项目主体使用 Go 编写
Linux 内核 4.18+ eBPF 程序需要 Linux 内核
Clang/LLVM 任意较新版本 编译 eBPF C 程序所需
Kernel headers linux-headers BPF 编译所需
Docker (可选) 容器化开发环境
Git 任意较新版本 版本管理

克隆仓库

# 先在 GitHub 上 Fork 仓库,然后:
git clone https://github.com/YOUR_USERNAME/huatuo.git
cd huatuo
git remote add upstream https://github.com/ccfos/huatuo.git

构建与测试

构建

make build        # 构建 BPF 程序和 Go 二进制
make gen-build    # 重新生成 BPF 和 Go 生成代码
make docker-build # 构建 Docker 镜像

全容器联调

使用当前源码构建开发镜像,并启动采集器、API Server、Elasticsearch、 Prometheus 和 Grafana:

make compose-dev-up

停止环境并清理关联的容器、卷和本地开发镜像:

make compose-dev-down

测试

make test  # 运行全部测试
make unit  # 只运行单元测试
make check # 运行代码风格和格式化检查

注意:make test 需要 /etc/kubernetes/pki 来运行 E2E 测试。如果没有 K8s 集群,请使用 make unit。

无需本地虚拟机即可在其他发行版中测试或调试,具体步骤参见 GitHub Actions 虚拟机调试。


贡献流程

1. 找到或创建 Issue

  • 在 open issues 中查找 Bug 和功能需求
  • 若找到未分配的 Issue,留言请求认领
  • 如果有新想法,先创建 Issue 讨论

2. 创建分支

git checkout -b fix/short-description
# 或: git checkout -b feat/short-description
# 或: git checkout -b docs/short-description

分支命名前缀:

前缀 用途
fix/ Bug 修复
feat/ 新功能
docs/ 文档
refactor/ 代码重构
test/ 添加测试

3. 编写代码

  • 一次只解决一个问题
  • 编写或更新与变更对应的测试
  • 运行 make check 检查代码风格
  • 运行 make unit 验证测试通过

4. 提交代码

使用 Conventional Commits 规范:

git commit -s -m "fix(scope): 简短描述

详细说明(可选)

Closes #issue-number

Signed-off-by: Your Name <your.email@example.com>"

-s 参数会自动添加 DCO 要求的 Signed-off-by 行。

5. 推送并发起 PR

git push origin your-branch-name

然后到 ccfos/huatuo 创建 Draft Pull Request。准备就绪后点击 Ready for review 请求审阅。

6. 代码审查

  • 维护者会审查你的 PR
  • 根据审查意见修改并推送新 commit
  • 修改期间可将 PR 设为 Draft,完成后再次点击 Ready for review
  • 通过后维护者会合入你的 PR

提交信息规范

HUATUO 遵循 Conventional Commits 规范:

<type>(<scope>): <description>

[可选正文]

[可选脚注]

类型

类型 说明
fix Bug 修复
feat 新功能
docs 文档变更
test 测试相关
refactor 不改变行为的代码重构
chore 构建、依赖等
perf 性能优化

示例

fix(pod): preserve response body read errors in httpDoRequest
feat(bpf): add probe for kernel scheduling latency
docs(contributing): add development setup guide
test(request): verify response body is readable after doRequest

代码风格

语言 工具
Go gofumpt + goimports
C (eBPF) clang-format(配置见 .clang-format)
Shell shfmt
YAML/JSON 2 空格缩进

每次提交前运行 make check 确保代码符合规范。


DCO 签名

所有贡献必须包含 开发者原产地证书 (DCO) 签名。

每次提交必须以以下内容结尾:

Signed-off-by: Your Name <your.email@example.com>

使用 git commit -s 自动添加。

签名表明你是代码的作者,或有权在此项目的 Apache 2.0 许可证下贡献此代码。


社区

  • GitHub Issues — 报告 Bug 和提功能需求
  • GitHub Discussions — 提问和分享想法
  • 微信 — 扫描 README 中的二维码加入微信群

感谢你为 HUATUO 做出贡献!

10 - 变更日志