先看最短路径有没有断
整条链能跑通,依赖四个组件依次正常:MongoDB 本身能起、mongodb_exporter 能连上 MongoDB 并把指标吐出来、Prometheus 能抓到 Exporter 的指标并解析告警规则、Alertmanager 能把 firing 的告警发给 Receiver。任何一个环节断掉,下游的”告警没响”都只是表象。最快的判断顺序是先跑 docker compose config 看 Compose 文件本身能不能解析,再看 docker compose ps 确认每个服务处于预期状态(Up / running 而不是 restarting / unhealthy),最后按顺序看 docker compose logs mongodb_exporter、prometheus、alertmanager 的输出,从第一个出现 ERROR 或 WARN 的层开始修,而不是同时动四个服务的配置。
最容易踩的坑是照搬 Kubernetes 场景的写法。参考资料里用 Prometheus Operator + ServiceMonitor 抓 noenv/mongo-exporter 的示例,那些对象(Deployment、Service、ServiceMonitor、NodePort、namespace=monitoring)只会在 Kubernetes 里由 Operator 自动识别,放进普通 Compose 后不会被任何进程处理。同样,9104 端口、--web.listen-address=:9104、--mongodb.uri 这些参数在那个镜像里能跑,不等于 Percona 官方 mongodb_exporter 也支持这些参数。Percona 版本对监听参数、环境变量和 URI 解析逻辑都做过重写,参数名和默认端口都要按当前版本 README 重新核对。
部署边界先选清楚
Compose 编排里通常有两条路:监控一个已经在外面的 MongoDB(生产最常见),或者在 Compose 里起一个 MongoDB 做本地验收(测试和 CI 方便)。前者只需要 Exporter 暴露端口给 Prometheus 抓,MongoDB 容器不存在也没关系;后者用 depends_on 把 MongoDB 排在 Exporter 前面,让 Exporter 等 MongoDB 进入 healthy 后再启动,可以省掉重试逻辑。两条路对 Prometheus 抓取配置没有本质区别,区别只在 Exporter 连接字符串填的是服务名还是外部 IP。
Kubernetes 那条路用的是 Prometheus Operator 的 ServiceMonitor 自动发现 scrape target,而不是手写的 scrape_configs。ServiceMonitor 资源里写的 selector、namespace、endpoints 会被 Operator 翻译成 Prometheus 的 job,示例里加 release: prometheus 这种 label selector 是 kube-prometheus 项目的约定。搬到 Compose,Prometheus 是用静态配置文件启动的,没有 Controller 帮你做这件事,必须自己写 scrape_configs 并把配置文件挂进容器。对比下来大致是这样:
| 取舍项 | Kubernetes 路径 | Compose 路径 |
|---|---|---|
| 目标发现 | ServiceMonitor + Operator | scrape_configs 静态列表 |
| 命名空间 | 通过 label selector / namespace 区分 | 通过 service name + Docker network |
| Exporter 暴露 | Service / NodePort 之类对象 | ports: 字段 |
| 配置生效方式 | Operator 监听 CRD 变更 | 改完配置重启 Prometheus |
Operator 风格的 label 选择、ServiceMonitor CRD 默认间隔这些隐式能力,搬到 Compose 后都需要显式配置,迁移时容易漏掉。
把版本和参数一次固定
先选定各组件的镜像 tag,再按对应版本的官方文档核对参数名和默认值。MongoDB 容器自身用社区版 tag(如 :7.0),mongodb_exporter 用 percona/mongodb_exporter,建议把版本锁到具体 patch 版本(具体某个 v0.X 系列);Prometheus 用 v2.5x.x,Alertmanager 用 v0.27.x。具体哪一版以官方下载页为准,原则是不用 :latest,因为 Exporter 在大版本之间会改指标名和 URI 参数。Compose、MongoDB、Exporter、Prometheus、Alertmanager 的实际版本号也要以官方文档为准;上线前按对应版本核对文中的指标名、监听端口和配置语法,避免升级后失效。
Percona mongodb_exporter 的运行参数和旧版(noenv/mongo-exporter、早期 fork 的 mongodb_exporter tag)不同。常见启动参数是 --mongodb.uri、--web.listen-address、--collect.database、--collect.collection 等;默认监听端口在较新版本里是 9216,但具体哪个版本对应哪一档,最好以当版 README 为准,不能直接照搬 noenv 示例里的 9104,也不要把社区复刻版本的 URI 模板当成官方格式。认证方面,URI 里可以带用户名密码或 authSource=admin,但 TLS、x509、角色权限等高级选项必须看当版文档。直接照抄旧示例的 URI,会触发 connection refused 或 auth failed 之外更难定位的错误。
配置文件保持一条链
Compose 文件大致结构是:先写一个网络让所有服务互通;然后是 MongoDB、mongodb_exporter、Prometheus、Alertmanager 四个服务,每个挂好配置和数据目录;密码、URI 这类信息用环境变量注入,避免明文写死在 YAML 里。下面是一份常见骨架,参数和镜像版本以实际查到的官方文档为准:
services:
mongo:
image: mongo:7.0
volumes:
- mongo-data:/data/db
networks: [stack]
mongodb-exporter:
image: percona/mongodb_exporter:vX.Y.Z
environment:
MONGODB_URI: mongodb://user:pass@mongo:27017/admin
depends_on:
- mongo
networks: [stack]
ports:
- "9216:9216"
prometheus:
image: prom/prometheus:vX.Y.Z
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./rules:/etc/prometheus/rules:ro
networks: [stack]
ports:
- "9090:9090"
alertmanager:
image: prom/alertmanager:vX.Y.Z
volumes:
- ./alertmanager.yml:/etc/alertmanager.yml:ro
networks: [stack]
ports:
- "9093:9093"
networks:
stack:
volumes:
mongo-data:
prometheus.yml 关键是把 Exporter 列为一个 job,指标路径默认是 /metrics:
global:
scrape_interval: 15s
rule_files:
- /etc/prometheus/rules/*.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
scrape_configs:
- job_name: mongodb
static_configs:
- targets: ['mongodb-exporter:9216']
Alertmanager 配置用于接入 webhook / Slack / 邮件 Receiver,并通过路由树决定哪些 label 发往哪个 Receiver。下面是一个 webhook 配置示意:
route:
receiver: default
routes:
- matchers: [severity="critical"]
receiver: critical
receivers:
- name: default
- name: critical
webhook_configs:
- url: http://example.invalid/hook
骨架里只固定服务名、网络和挂载路径;密码、URL、Receiver 凭据在实际部署时通过环境变量注入,避免敏感信息以明文形式进入版本控制。
MongoDB 自身的报错先排干净
Exporter 抓不到指标时,最常见的原因是 MongoDB 自己起不来,先排这一层。参考资料里列过的几种典型报错,在用官方 mongo 镜像跑 mongod 时仍然会见到,处理方式也基本不变。
ERROR: child process failed, exited with error number 1 通常是容器内用户没有读取挂载目录的权限。镜像以 mongodb 用户运行(不是 root),它对 /data/db、/data/configdb 这些默认目录有写权限,但宿主机的自定义目录如果 chown 给了 root 或别的用户,mongodb 进程就启不来。解决办法是让挂载的目标路径落在容器已授权目录下面,或者用 user: 显式指定与宿主目录匹配的 UID/GID。
error (at /tmp/docker-entrypoint-config.json:1): Cannot index string with string "systemLog" 多半是 --config 引用的配置文件本身格式不对。官方 mongo 镜像的入口脚本已经在跑 mongod,Compose 的 command 里不应该再写一遍 mongod --config ...,否则会重复启动 mongod,配置被当成 mongod 参数或第二份 JSON 再解析一次,报错落在 /tmp/docker-entrypoint-config.json 之类临时文件上,根因其实是参数重复。fork=true 是另一个常见错误源:容器里没有 init system,fork 之后父进程退出,入口脚本判断子进程”启动成功但父进程已死”会报 child process successful,parent failed,需要把配置里的 fork 关掉。
同一网络里用服务名,不要写 localhost
Exporter 连不上 MongoDB 的几个常见原因都是同一类:地址写错。Compose 默认网络里,服务之间用服务名(比如 mongo)而不是 localhost,因为每个容器有自己的网络命名空间。同一个 Compose 文件里 mongodb_exporter 起在另一个容器里,写 mongodb://localhost:27017 会连到 Exporter 自己而不是 MongoDB。MongoDB 的端口是 27017,但这是容器内端口;如果在宿主机上另外起了 MongoDB,再让宿主机 Prometheus 抓 Exporter,需要把 Exporter 的指标端口映射到宿主机,再让 Prometheus 抓 host.docker.internal 或宿主 IP——这些细节与 MongoDB 是否在 Compose 里无关,区分清楚即可。
连接 MongoDB 的 root 账号时通常使用 authSource=admin,SCRAM-SHA 模式在 URI 里直接带用户名密码就可以;运维权限和监控权限最好分开,Exporter 不需要读写业务集合,只要能跑 serverStatus、replSetGetStatus 等几条诊断命令。TLS / x509、Kerberos 这些场景的参数以 Exporter 当前版本 README 为准,不要抄旧版本的 URI 模板。常见的情况是照搬示例 URI 后遇到 auth failed 或 Server selection timeout,十有八九是 authSource、replicaSet、directConnection 这几个参数没填对。
Exporter 先返回指标
判断 Exporter 是否健康的最直接方式不是看 Prometheus,而是直接 curl 它的指标端点:
curl -s http://localhost:9216/metrics | head
只要能看到 mongodb_up 之类的指标,连接就是通的。如果端口连不上,先看容器日志里有没有 connection refused、auth failed、no reachable servers、server selection timeout 这几类报错,对应的解决分别是端口没监听、密码或 authSource 错、副本集名称写错、DNS 没解析到 MongoDB 容器。如果端点能访问但报 metrics not enabled,是 --collect.* 开关没打开,不是 Exporter 故障——具体哪些采集项默认开启、哪些要手动开,得翻当前版本的 README 看默认值。
Prometheus 完成抓取
判断抓取是否成功,先看 Targets 页面:在 /api/v1/targets 里检查 job=mongodb 的 health 是不是 up,并查看 last scrape error。connection refused 通常是端口写错(Percona 默认是 9216,而不是 noenv 那种示例里的 9104);no such host 是 Prometheus 容器里 DNS 找不到服务名,多半是两者不在同一个 Compose 网络,或者 links 用得不全;context deadline exceeded 是 Exporter 启动慢,Prometheus 抓超时,调大 scrape_timeout 是临时处理,正确处理是让 Exporter 真正起来或者加 initial_delay。prometheus.yml 没挂载或路径写错时,Prometheus 日志会报 error loading config 或读到一份空配置,需要重新挂载并让 Prometheus 重读。
Compose 文件改动后,docker compose up -d prometheus 重启 Prometheus 让它重读配置;规则文件路径注意用容器内路径(/etc/prometheus/rules/*.yml 而不是宿主机路径),并且目录里的文件要有可读权限,否则加载阶段就会报错。
指标对了规则才有可能触发
PromQL 写错是告警不响的另一个主要原因。指标名以 Exporter 实际输出为准:Percona mongodb_exporter 在不同版本里输出 mongodb_up、mongodb_ss_repl_health、mongodb_ss_mem_resident 等,标签也有变化。先在 Prometheus 的 Graph 页用 {job="mongodb"} 查询,确认实际输出的指标 name 和 label 跟规则里写的一致,再写 PromQL,否则规则写得再细致也是 0 命中。
规则文件基本结构是这样:
groups:
- name: mongodb
rules:
- alert: MongoDBDown
expr: mongodb_up == 0
for: 2m
labels:
severity: critical
annotations:
summary: MongoDB 实例不可达
写完用 promtool check rules rules/*.yml 跑一遍语法,配置加载后看 /rules 页面里这条规则的 state 是 inactive 还是没有出现。规则不出现一般是文件没有挂载成功;state 一直 inactive 是条件没有满足;pending 是在等 for;变成 firing 后再看 Alertmanager 有没有收到。规则里 for 时间别写太短,scrape 抖动一下就触发纯属给自己找麻烦;测试时想跳过等待可以把 for 临时设成 0s,验证通过再调回去。
Alertmanager 收得到才谈路由
告警 firing 但没人收到,问题大多数在 Alertmanager 这一头。Prometheus 到 Alertmanager 的连接靠 alerting.alertmanagers 段,地址写错(端口不是 9093、服务名不在同一个网络、TLS 配错)时 Prometheus 会报 error sending alert 或一直 try again。Alertmanager 自己的日志里则要看类似 WARN" Notify x failed 这种——Receiver 的 URL 写错、认证 token 失效、Slack / 飞书 webhook 不可达,都会在这里冒出来。
路由匹配靠 label matcher,旧版本 Alertmanager 用 match / match_re,新版改成统一 matchers,旧配置搬过来不报错但也不生效,需要按当前版本文档改写。分组(group_by、group_wait、group_interval)和抑制(inhibit_rules)写得过严会把告警静默掉;测试时建议先关掉抑制、缩短 group_wait,让每条 firing 先实际发出一条,再启用完整规则。
Receiver 凭据建议用环境变量注入或挂载 Secret 文件。Slack / 企业微信 / 飞书 webhook URL 轮换后未及时同步的情况也常见,会表现为告警上线首日能够发送,之后却不再送达。另外,webhook URL 一般需要开放网络出口权限。Compose 默认网络本身不限制出口,但宿主机层面的 iptables 或云厂商安全组经常被漏配,导致容器内能 curl,外部通知却发不出去。
上线之前再多看一眼副作用
镜像使用 :latest 是常见风险。Exporter 在大版本之间指标名变化较多,PromQL 和规则需要跟着升级;锁定版本能避免大半夜升级 0.x 时,顺带把整条告警链路一起改掉。密码直接写进 Compose 文件并提交到 Git 也是常见事故,用 .env 文件并加入 .gitignore,或者使用 Docker secrets、外部 Secret 管理,风险更低。端口暴露(27017、9216、9090、9093)只暴露必要的,MongoDB 容器不要暴露给公网。
测试通知不要发到群里,重复触发一次即可。示例跑完后清理 Receiver 里遗留的测试告警,避免监控值班同学第二天醒来仍被昨晚的测试通知打扰。
按这条验收顺序走一遍
按最短闭环验收:先用 docker exec 进入 MongoDB 容器,执行 mongosh --eval 'db.runCommand({ping:1})';ping 通后 curl Exporter 的 /metrics;拿到指标后确认 Prometheus Targets 为 up;PromQL 能查出结果后,确认 promtool check rules 通过且 /rules 显示活跃;Alertmanager 收到 firing 后,再确认 Receiver 是否真的发出。链路哪个环节报错,就锁定那一层修,不要把四层配置一起改。
如果哪一步失败,先回到 docker compose logs <service> 看真实报错再动手。常见的情况是服务还没有完全就绪,docker compose up 启动后还要等 Exporter 第一次成功连上 MongoDB 才能输出指标。在等待期间,Prometheus 看到的 Target 是 Down,这并不一定是配置错;让 Exporter 真正 healthy,再观察几分钟,问题往往自动消失。
上线前,以下项目必须在实际环境中验证:当前 Exporter 版本号与官方 README 是否对应,包括指标名、监听端口和采集开关;URI 在 TLS / x509 / 副本集下的写法,不同 MongoDB 版本的语法略有差异;Alertmanager 新旧 matcher 的语法差异,按所安装的具体版本改写;企业微信 / Slack / 飞书 webhook 的网络出口策略,云厂商安全组经常拦截。这些项目受环境影响最大,应按官方文档核对,并结合本机或当前云厂商的实际安全组策略验证后再上线。












暂无评论内容