Prometheus与cAdvisor容器监控告警实战:从部署到PromQL规则

发布时间:2026/9/20 1:24:08
Prometheus与cAdvisor容器监控告警实战:从部署到PromQL规则 简介这份文档面向运维工程师与Linux监控初学者聚焦Prometheus 2.35.0对Docker容器的监控与告警实践解决容器化环境下指标采集、服务发现与报警链路搭建的问题。内容围绕cadvisor采集宿主机容器数据、静态配置与基于文件或consul的动态服务发现、alertmanager二进制部署及QQ邮箱告警配置等核心环节展开并给出多节点IP规划与rules告警规则示例适合具备一定Linux与Docker基础、希望落地容器监控体系的中高级读者。资源包共1个docx文件约6.42MB以图文与命令记录形式呈现便于按章节查阅与对照实操。目前已有1084人学习下载。读者可从中获得完整的部署流程、cadvisor挂载与metrics接口验证方法、alertmanager路由与接收人配置模板以及告警规则编写与排错思路快速搭建可复用的容器监控与报警方案。1. 为什么容器监控不能只靠docker statsdocker stats能看 CPU、内存、网络 IO但它有两个硬伤一是数据不落地关掉终端就没了二是没法做阈值告警和长期趋势分析。生产环境里真正要回答的问题是「哪个容器在凌晨三点把宿主机内存吃满了」「某个容器是不是已经消失但没人发现」这些都需要一套带时序存储和告警链路的方案。Prometheus 2.35.0 配合 cAdvisor 就是这套方案里最经典的组合cAdvisor 负责从宿主机内核和 Docker daemon 采集容器维度的指标暴露成/metrics文本接口Prometheus 按scrape_interval定时拉取并写入 TSDBAlertmanager 接收 Prometheus 推来的告警做分组、抑制、静默后投递到邮箱。本文基于两台机器192.168.10.92 跑 Prometheus Alertmanager cAdvisor192.168.10.90 只跑 cAdvisor的实际部署过程把配置、告警规则表达式和踩坑点讲清楚适合正在做容器监控选型或第一次搭 Prometheus 的运维和开发。2. cAdvisor 采集容器指标与 Prometheus 抓取配置2.1 cAdvisor 为什么必须挂载宿主机目录cAdvisor 本身跑在容器里但它要读的是宿主机的 cgroup、Docker 数据目录和磁盘信息。如果不挂载它只能看到自己容器的数据监控就失去意义。所以docker run时那几个--volume参数不是可选项而是核心配置。先在被监控宿主机上安装 Docker 并调整数据目录因为 cAdvisor 要挂载/data/docker作为/var/lib/docker的只读映射# 安装 docker yum -y install docker # 修改 docker 数据目录cAdvisor 挂载路径要与此一致 cat /etc/docker/daemon.json { graph: /data/docker, insecure-registries: [https://b9pmyelo.mirror.aliyuncs.com] } systemctl restart dockergraph指定 Docker 镜像和容器层的存储根目录默认是/var/lib/docker。这里改成/data/docker通常是因为系统盘空间小数据盘大。改完之后 cAdvisor 挂载时就要写/data/docker/:/var/lib/docker:ro两边必须对应否则 cAdvisor 读不到容器层信息container_fs_*系列指标会缺失。2.2 启动 cAdvisor 并验证指标接口拉取镜像并运行注意所有挂载都加:ro只读避免 cAdvisor 误写宿主机文件docker pull google/cadvisor:latest docker run -d \ --volume/:/rootfs:ro \ --volume/var/run:/var/run:ro \ --volume/sys:/sys:ro \ --volume/data/docker/:/var/lib/docker:ro \ --volume/dev/disk/:/dev/disk:ro \ --publish8080:8080 \ --detachtrue \ --namecadvisor \ google/cadvisor:latest各挂载点作用如下挂载参数作用/:/rootfs:ro读取宿主机文件系统用于container_fs_*指标/var/run:/var/run:ro访问 Docker socket获取容器元数据/sys:/sys:ro读取 cgroup获取 CPU、内存、网络指标/data/docker/:/var/lib/docker:ro读取容器层获取镜像和容器文件系统信息/dev/disk/:/dev/disk:ro读取磁盘设备信息启动后访问http://192.168.10.90:8080/metrics和http://192.168.10.92:8080/metrics能看到大量以container_开头的指标。如果页面打不开先docker ps | grep cadvisor确认容器状态再看docker logs cadvisor是否有权限报错。常见问题是宿主机启用了 SELinux需要加--privilegedtrue或调整策略。2.3 Prometheus 静态配置抓取 docker jobPrometheus 2.35.0 用二进制方式部署解压后修改prometheus.yml。核心是三块全局采集间隔、Alertmanager 地址、抓取目标。global: scrape_interval: 15s evaluation_interval: 15s alerting: alertmanagers: - static_configs: - targets: - 192.168.10.92:9093 rule_files: - rules/*.yml scrape_configs: - job_name: docker static_configs: - targets: [192.168.10.92:8080, 192.168.10.90:8080]scrape_interval: 15s表示每 15 秒拉一次指标这个值决定了告警的灵敏度下限。evaluation_interval是告警规则评估周期两者可以不同但通常设成一样。job_name是逻辑分组后面告警规则里up{jobdocker}就靠它筛选。targets里每个地址是一个 instance对应一台宿主机上的 cAdvisor。改完配置先用promtool校验避免语法错误导致启动失败./promtool check config ./prometheus.yml # 输出 SUCCESS 才说明配置合法启动后访问http://192.168.10.92:9090/targets两个 docker job 的 target 应该都是 UP 状态。如果显示 DOWN点进去看 error 信息通常是网络不通或 cAdvisor 没起来。3. Alertmanager 告警路由与邮箱投递配置3.1 路由树和分组参数怎么设Alertmanager 的核心是route块它决定告警怎么分组、等多久、发给谁。配置里几个时间参数容易设错global: resolve_timeout: 1m smtp_smarthost: smtp.qq.com:25 smtp_from: 1036981484qq.com smtp_auth_username: 1036981484qq.com smtp_auth_password: fwbxnmbfnrpvbedi smtp_require_tls: false route: group_by: [alertname] group_wait: 10s group_interval: 10s repeat_interval: 10s receiver: mail receivers: - name: mail email_configs: - to: 1441107787qq.com send_resolved: truegroup_by: [alertname]表示同名告警合并成一条邮件避免几十个容器同时触发时邮箱被轰炸。group_wait是分组内第一条告警等待多久再发给后续同类告警留合并窗口。group_interval是同一分组发送新告警的间隔。repeat_interval是重复提醒间隔生产环境一般设 1h 以上测试时设 10s 方便验证。smtp_auth_password填的是邮箱授权码不是登录密码。QQ 邮箱需要在设置里开启 SMTP 服务后生成授权码。send_resolved: true让告警恢复时也发一封邮件否则只会在触发时收到通知恢复状态无从得知。3.2 systemd 托管与端口验证二进制方式启动的服务建议交给 systemd 管理避免终端关闭后进程退出vim /usr/lib/systemd/system/alertmanager.service[Unit] Descriptionalertmanager daemon [Service] Restarton-failure ExecStart/data/alertmanager/alertmanager --config.file/data/alertmanager/alertmanager.yml [Install] WantedBymulti-user.targetRestarton-failure保证进程异常退出后自动拉起。ExecStart必须用绝对路径systemd 不认相对路径。加载并启动systemctl daemon-reload systemctl enable alertmanager systemctl start alertmanager systemctl status alertmanager netstat -anput | grep 9093看到 9093 端口处于 LISTEN 状态访问http://192.168.10.92:9093能打开 Alertmanager 的 Web UI说明服务正常。如果端口没起来用journalctl -u alertmanager -n 50看日志常见错误是 YAML 缩进不对或授权码错误。4. 容器告警规则表达式与 PromQL 调试4.1 从 up 指标到容器消失检测告警规则文件放在rules/目录下Prometheus 启动时按rule_files加载。最基础的规则是检测 cAdvisor 本身是否存活groups: - name: docker.rules rules: - alert: DockerInstanceDown expr: up{jobdocker} 0 for: 0m labels: severity: critical annotations: summary: Docker Instance down description: 容器实例: 【{{ $labels.instance }}】has been down for more than 1 minuteup{jobdocker} 0表示 Prometheus 拉不到该 target 的指标通常是 cAdvisor 挂了或网络断了。for: 0m表示条件一满足就告警不加等待。{{ $labels.instance }}会替换成具体实例地址让邮件里能看出是哪台机器。容器消失检测用container_last_seen它记录容器最后一次被看到的时间戳- alert: ContainerKilled expr: time() - container_last_seen{name!} 60 for: 1m labels: severity: critical annotations: summary: A Container has disappeared description: Container Name 【{{ $labels.name }}】 on 主机【{{ $labels.instance }}】 has disappearedtime()是当前时间戳减去container_last_seen得到容器消失的秒数超过 60 秒就认为容器没了。name!过滤掉没有名字的容器避免误报。4.2 CPU、内存、网络、磁盘四类阈值规则CPU 使用率用rate计算每秒增长率再乘以 100 转成百分比- alert: ContainerCpuUsage expr: (sum by(instance, name) (rate(container_cpu_usage_seconds_total{name!}[1m])) * 100) 80 for: 1m labels: severity: warning annotations: summary: Container CPU usaged above 80% description: Container Name 【{{ $labels.name }}】 on 主机【{{ $labels.instance }}】 CPU usage is above 80%, Current Value: {{ $value }}rate(...[1m])取 1 分钟内的每秒增长率sum by(instance, name)按实例和容器名聚合去掉 cpu 维度。 80是阈值for: 1m表示持续 1 分钟才告警避免瞬时尖峰误报。内存规则分两种口径。一种是容器占用自身 limit 的比例- alert: ContainerMemoryUsage expr: (sum(container_memory_working_set_bytes{name!}) by (instance,name) / sum(container_spec_memory_limit_bytes{name!}) by (instance,name) * 100 ! Inf) 80 for: 1m labels: severity: warning annotations: summary: Container Memory usaged 占用限制容器最大内存值 above 80% description: Container Name 【{{ $labels.name }}】 on 主机【{{ $labels.instance }}】 Memory usage 占用限制容器最大内存比例 is above 80%,Current Value:{{ $value }}container_memory_working_set_bytes是容器实际使用内存也是 OOM killer 的判断依据比container_memory_usage_bytes更准确。container_spec_memory_limit_bytes是docker run -m设置的限制值。! Inf过滤掉没设 limit 的容器因为分母为 0 时比值是正无穷。另一种是容器内存总和占宿主机总内存的比例- alert: ContainerMemoryUsageAll expr: sum(container_memory_working_set_bytes{name!}) by (instance) / sum(machine_memory_bytes) by (instance) * 100 80 for: 1m labels: severity: warning annotations: summary: all 容器内存总和 占用宿主机总内存 above 80% description: 所有容器内存总和 占用宿主机【{{ $labels.instance }}】总内存比例 is above 80%, Current Value: {{ $value }}machine_memory_bytes是宿主机总内存。这条规则看的是整机压力和上一条看单容器 limit 是互补关系。网络和磁盘规则结构类似都是rate加by聚合再除 1024 转 M 单位- alert: NetworkReceiveRate expr: sum(rate(container_network_receive_bytes_total{image!}[1m])) by (instance,name,interface)/1024/1024 100 for: 1m labels: severity: warning annotations: summary: 容器入口(接收)流量过高 description: 容器名{{ $labels.name }} 的网卡接口{{ $labels.interface}} 在实例主机{{ $labels.instance }} 每秒入口流量过高,大于 100M,当前值为:{{ $value }}M - alert: DiskReadRate expr: sum(rate(container_fs_reads_bytes_total{image!}[1m])) by (instance,name,device)/1024/1024 50 for: 1m labels: severity: warning annotations: summary: 容器文件系统磁盘读取速率过高 description: 容器名{{ $labels.name }} 的磁盘分区{{ $labels.device}} 在实例主机{{ $labels.instance }} 每秒读取速率过高,大于 50M,当前值为:{{ $value }}Mby (instance,name,interface)里的标签必须和后面{{ $labels.interface }}对应否则模板渲染出来是空值。without (interface)是另一种写法表示聚合时去掉 interface 维度效果类似但语义相反调试时在 Prometheus 的 Graph 页面里试出正确结果再写进规则。4.3 用 promtool 和 Graph 页面验证规则规则写完先校验语法./promtool check rules /data/prometheus/rules/docker.yml输出SUCCESS说明 YAML 和 PromQL 语法都没问题。但语法对不代表逻辑对还要在 Prometheus Web UI 的 Graph 页面里逐条粘贴expr表达式看返回的数据是否符合预期。比如up{jobdocker}应该返回两条值为 1 的时间序列container_last_seen{name!}应该返回所有运行中容器的记录。调试时把for临时改成0m阈值调低手动docker stop一个容器看 Alertmanager 是否收到告警、邮箱是否收到邮件。验证完再改回生产值。这个流程比直接上生产然后等故障触发要可靠得多。5. 告警降噪与规则维护的实用技巧告警规则上线后最大的问题不是漏报而是误报和重复。ContainerKilled这条规则在容器正常重启时也会触发因为container_last_seen会短暂中断。解决办法是加for: 2m给容器重启留出窗口或者用absent(container_last_seen{nametomcat,instance192.168.10.92:8080})只针对特定容器做消失检测。absent()函数的语义是如果给定的时间序列不存在返回 1存在则返回空。它适合监控「必须有但可能消失」的容器比如核心业务容器。写法上支持精确匹配和正则匹配- alert: ContainerAbsent expr: absent(container_last_seen{nametomcat,instance192.168.10.92:8080}) for: 1m labels: severity: critical annotations: summary: 各容器如果挂掉或不存在了则报警 description: 容器名:{{ $labels.name }} 在宿主机{{ $labels.instance }}上挂掉或不存在了如果要匹配一组容器用name~tomcat.正则。注意absent()返回的结果不带原始标签所以{{ $labels.name }}在告警描述里可能是空的需要在表达式里用label_replace补标签或者直接在 description 里写死容器名。另一个降噪手段是 Alertmanager 的inhibit_rules当DockerInstanceDown触发时抑制该实例上所有容器级别的告警避免一台机器挂了收到几十封邮件。配置写在alertmanager.yml里inhibit_rules: - source_match: alertname: DockerInstanceDown target_match_re: alertname: Container.* equal: [instance]equal: [instance]表示只有 source 和 target 的 instance 标签相同才抑制。这样 cAdvisor 挂了之后同一台机器上的容器告警就不会再发出来等 cAdvisor 恢复后容器告警自然消失。规则文件建议按业务或主机分组比如docker.yml放容器规则node.yml放主机规则general.yml放通用规则。每次改完规则用promtool check rules校验然后systemctl reload prometheus热加载不用重启进程。Prometheus 2.35.0 支持 SIGHUP 信号触发配置重载systemctl reload底层就是发这个信号。本文还有配套的精品资源点击获取