container_fs_reads_total计数器监控原理

详细拆解如何获取宿主机上 PID 为 29139 的容器对应的 container_fs_reads_total 计数器,并计算其每秒读取速率(IOPS)的具体过程。本文展示从操作系统底层到监控指标的转化。

​核心目标:​​ 获取 PID 29139 容器每秒的磁盘读操作次数 (Read IOPS)。

​依赖项:​

  • 宿主机访问权限(能查看 /proc/sys/fs/cgroup
  • cAdvisor 运行中(通常内置于 kubelet)
  • Prometheus 配置抓取该节点上的 cAdvisor/metrics

​详细步骤流程:​

阶段 1: 确定目标容器在宿主机上的资源视图(PID -> cgroup)

  1. ​根据 PID 找到其 cgroup 路径:​

    • 查看进程的 cgroup 信息:cat /proc/29139/cgroup

    • ​输出示例 (cgroup v1):​

      复制代码
      12:memory:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      11:devices:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      10:hugetlb:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      9:perf_event:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      8:blkio:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      7:freezer:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      6:net_cls:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      5:cpuacct,cpu:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      4:cpuset:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      3:pids:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      2:rdma:/
      1:name=systemd:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
    • ​关键识别:​ 找到与 blkio 控制器相关联的行(例如上面的第8行)。记录其路径:/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>(实际路径中的 <PodUID><ContainerRuntimeID> 是具体的长ID)。这就是该进程(容器)在 blkio 子系统下的 ​cgroup 相对路径​

  2. ​定位 blkio cgroup 统计文件 (cgroup v1 示例):​

    • 将相对路径拼接到 /sys/fs/cgroup/blkio/ 下:cd /sys/fs/cgroup/blkio/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>

    • 在这个目录下,找到核心统计文件:blkio.throttle.io_serviced

      • (如果系统使用 ​cgroup v2​blkio 控制器被合并,关键文件通常是 io.stat,路径类似 /sys/fs/cgroup/<SAME_RELATIVE_PATH>/io.stat
    • ​查看实时统计:​ cat blkio.throttle.io_serviced

    • ​输出示例:​

      复制代码
      8:0 Read 123456
      8:0 Write 78901
      8:0 Sync 0
      8:0 Async 202357
      ...(其他设备号)
      Total 0
    • ​关键提取:​ 这里 Read 123456 表示设备 8:0 (major:minor) 上发生的 ​累计读操作总次数​ 。这个值直接对应于我们将要获取的 container_fs_reads_total ​计数器的瞬时值​

阶段 2: cAdvisor 的视角

  1. ​cAdvisor 周期性扫描:​

    • cAdvisor 运行在节点上(通常由 kubelet 托管)。
    • 每隔 10 秒,cAdvisor 会执行一次采集循环。
    • cAdvisor 发现节点上的所有容器,包括 PID 29139 所属的容器。它知道该容器的:
      • ​cgroup 路径:​ /sys/fs/cgroup/blkio/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>
      • 容器名称(从容器运行时或 cgroup 路径解析)。
      • 所属 Pod 名称(解析自 cgroup 路径或 Kubernetes API)。
      • Namespace。
      • 容器 ID (<ContainerRuntimeID>)。
      • 镜像名称。
      • 节点名称(hostname)。
  2. ​cAdvisor 读取统计文件:​

    • cAdvisor 在每次采集循环中,打开文件 /sys/fs/cgroup/blkio/kubepods/burstable/pod<PodUID>/<ContainerRuntimeID>/blkio.throttle.io_serviced
    • 它解析文件内容。
    • 对于每一行包含 Read 的操作(忽略 Sync/Async/Total),提取设备号和计数值(如示例中的 123456)。
    • ​聚合/选择:​
      • 如果按设备统计(且 device 标签被启用),则为每个设备生成一个时间序列点。
      • ​通常情况下,如果未指定设备过滤,cAdvisor 会将同一 cgroup 下所有设备的 Read 计数 相加 得到该容器总的读操作数。​ 这就是我们通常关注的 container_fs_reads_total 值,不区分设备。
  3. ​生成并暴露指标:​

    • cAdvisor 使用当前采集时间戳(例如 T1=1680000000)和计算得到的总读计数 123456
    • 它构造一个 ​Prometheus Counter​ 指标样本:
      • container_fs_reads_total{container="<ContainerName>", id="<ContainerRuntimeID>", image="<ImageName>", name="<K8sContainerName>", namespace="<PodNamespace>", node="<NodeHostname>", pod="<PodName>"} 123456
    • 经过约 10 秒后(在时间戳 T2 = T1 + 10),cAdvisor 再次采集:
      • 读取同一个文件,得到新的累计值,例如 135678
      • 构造新样本:... reads_total ... 135678
    • cAdvisor 将所有容器的指标合并,通过其 HTTP 服务(例如:http://<NodeIP>:10250/metrics/cadvisor)暴露 /metrics/cadvisor 端点。

阶段 3: Prometheus 抓取与存储

  1. ​Prometheus 周期性抓取:​
    • Prometheus 配置了 scrape_config,定期(例如每 15s)向 http://<NodeIP>:10250/metrics/cadvisor 发送 HTTP GET 请求。
    • 假设在时间 T_p1 (在 T1T2 之间)抓取一次,它获取到的目标容器的 container_fs_reads_total 值是 123456。Prometheus 将这个样本存储下来,记录抓取时间 T_p1 和值 123456
    • 大约 15s 后,在时间 T_p2 (在 T2T3 之间)抓取第二次,获取到值 135678。同样存储 (T_p2, 135678)

阶段 4: 计算每秒读取速率 (Read IOPS)

  1. ​使用 PromQL 查询速率:​
    • 用户在 Prometheus UI 或 Grafana 等工具中编写查询:
      rate(container_fs_reads_total{id="<ContainerRuntimeID>"}[5m])
    • ​解读:​
      • container_fs_reads_total{id="<ContainerRuntimeID>"}:精确过滤出我们要查询的容器(用 id 标签是最准确的)。也可以组合 namespace, pod, container 等标签。
      • [5m]:定义一个回溯的时间窗口(此处是 5 分钟)。​这是计算速率的关键​
      • rate():PromQL 函数,用于计算计数器 (counter) 在指定时间窗口 [5m] 内的 ​平均每秒增长率​
    • ​计算过程 (简化逻辑,非精确算法):​
      • Prometheus 在评估此查询时(例如在时间 T_now),会在 [T_now - 5m, T_now] 的时间窗口内,找到所有符合条件的 container_fs_reads_total 样本点 (假设抓取了多个点:(T_p1, V1=123456), (T_p2, V2=135678), (T_p3, V3=147890), ...)。
      • 它识别出窗口中最早的样本点 ((T_first, V_first)) 和最晚的样本点 ((T_last, V_last))。
      • 计算计数器增量:Delta = V_last - V_first
      • 计算时间增量(秒):TimeDelta_seconds = T_last - T_first
      • ​计算平均每秒速率:Rate = Delta / TimeDelta_seconds
    • ​结果:​
      • Rate 值就是目标容器在最近 5 分钟窗口内的 ​平均每秒磁盘读操作次数 (Read IOPS)​。这直观地反映了该容器的磁盘读负载强度。

​总结流程图:​

复制代码
宿主机 PID 29139 -> /proc/29139/cgroup -> blkio cgroup路径 (e.g., /sys/fs/cgroup/blkio/kubepods/.../<ID>)
         |
         V
定期读取 /sys/fs/cgroup/blkio/.../<ID>/blkio.throttle.io_serviced (cAdvisor, e.g. 10s)
         | (解析 Read 总计数)
         V
构造指标 container_fs_reads_total{..., id="<ID>", ...} <CounterValue> (cAdvisor)
         |
         V
暴露于 http://<NodeIP>:10250/metrics/cadvisor (cAdvisor)
         |
         V
抓取 (Prometheus, e.g. 15s) -> 存储 (Timestamp, <CounterValue>)
         |
         V
查询: rate(container_fs_reads_total{id="<ID>"}[5m]) (PromQL)
         |
         V
结果:目标容器最近 5 分钟的平均每秒读操作次数 (Read IOPS)

​关键点强调:​

  1. /proc/<pid>/cgroup 是关联 PID 到 cgroup 的桥梁。​
  2. blkio.throttle.io_serviced (v1) 或 io.stat (v2) 是核心数据源文件,提供累计读计数。​
  3. ​cAdvisor 是数据采集、标签附加和指标暴露的关键组件。​
  4. ​Prometheus 抓取并存储时间序列数据。​
  5. rate(...[window]) 是计算平均每秒速率(IOPS)的标准方法,必须指定一个合理的评估窗口(如 1m, 5m)。​
  6. id 标签是利用容器运行时 ID 精确过滤特定容器指标的最可靠方式。​