概述
Prometheus 的常规监控是"从内向外看"——Prometheus 采集 Exporter 指标,了解系统内部状态。但用户访问你的服务时,走的是"从外向内"的路径:DNS 解析 → 网络路由 → 负载均衡 → 后端服务。一条从内部看完全健康的链路,可能因为 DNS 配置错误、CDN 缓存问题或 SSL 证书过期而导致用户完全无法访问。
Blackbox Exporter 解决的是"从外部探测"的问题。它模拟用户行为,从外部对 HTTP/TCP/ICMP/DNS 端点发起探测,让你获得"用户视角"的可用性数据。详细梳理 Blackbox Exporter 的配置、各类探测方式、SSL 证书监控、多地域拨测方案和可用性 SLO 度量。
一、为什么需要外部探测
1.1 内部监控的盲区
用户请求路径:
用户 → DNS → CDN → 负载均衡 → Ingress → Pod → 数据库
内部监控覆盖:
✓ Pod 指标
✓ 数据库指标
✓ Ingress 指标
✓ LB 指标
外部盲区:
✗ DNS 解析是否正常?
✗ CDN 缓存是否正确?
✗ SSL 证书是否过期?
✗ 网络路由是否通畅?
✗ 从用户地域访问是否可达?
内部监控无法覆盖的典型场景:
| 场景 | 内部监控 | 外部探测 |
|---|---|---|
| DNS 解析失败 | 看不到 | ✓ 能检测 |
| SSL 证书过期 | 看不到(需主动检测) | ✓ 能检测 |
| CDN 缓存错误 | 看不到 | ✓ 能检测 |
| 网络路由中断 | 看不到 | ✓ 能检测 |
| 特定地域访问慢 | 看不到 | ✓ 多地域拨测 |
| 页面内容被篡改 | 看不到 | ✓ HTTP 内容检查 |
1.2 Blackbox Exporter 的定位
┌────────────────────────────────────────────────────┐
│ 监控视角对比 │
│ │
│ 内部监控 (Prometheus + Exporter) │
│ → "系统是否健康?" │
│ → CPU、内存、磁盘、QPS、延迟 │
│ │
│ 外部探测 (Blackbox Exporter) │
│ → "用户能否访问?" │
│ → HTTP 状态码、响应时间、证书有效期 │
│ → TCP 端口可达性、DNS 解析正确性 │
│ │
│ 两者互补:内部健康 + 外部可达 = 完整可用性 │
└────────────────────────────────────────────────────┘
二、Blackbox Exporter 架构
2.1 工作原理
┌───────────────┐ ┌───────────────────────┐
│ Prometheus │ scrape │ Blackbox Exporter │
│ (拉取方) │ ──────→ │ (探测方) │
└───────────────┘ └───────────┬───────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ HTTP │ │ TCP │ │ ICMP │
│ 探测 │ │ 探测 │ │ 探测 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────┐
│ 被探测目标 (Target) │
│ web.example.com / 10.0.1.5:3306 │
└─────────────────────────────────────┘
Blackbox Exporter 的核心特点:
- 拉模式集成:Prometheus 通过 scrape Blackbox Exporter 获取探测结果
- 参数化探测:通过 URL 参数指定探测目标和模块
- 模块化配置:不同探测类型(HTTP/TCP/ICMP/DNS)使用不同模块
- 返回丰富指标:探测结果包含状态码、响应时间、证书信息等
2.2 探测 URL 格式
# HTTP 探测
http://blackbox:9115/probe?target=https://example.com&module=http_2xx
# TCP 探测
http://blackbox:9115/probe?target=10.0.1.5:3306&module=tcp_connect
# ICMP 探测
http://blackbox:9115/probe?target=10.0.1.5&module=icmp
# DNS 探测
http://blackbox:9115/probe?target=dns.example.com&module=dns_example
三、配置详解
3.1 完整配置文件
# blackbox.yml
modules:
# HTTP 探测模块
http_2xx:
prober: http
timeout: 10s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: [200, 201, 204] # 可接受的状态码
method: GET
headers:
User-Agent: "Blackbox-Exporter/0.25"
Accept-Language: "zh-CN"
follow_redirects: true
preferred_ip_protocol: ip4 # 优先 IPv4
ip_protocol_fallback: true
# HTTP 探测 - 需要认证
http_with_auth:
prober: http
timeout: 10s
http:
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer token-xxx"
body: '{"action":"health_check"}'
valid_status_codes: [200]
# HTTP 探测 - 检查页面内容
http_content_check:
prober: http
timeout: 10s
http:
fail_if_body_matches_regexp:
- "error"
- "maintenance"
fail_if_body_not_matches_regexp:
- "expected_content"
# HTTPS 探测 - 严格 SSL 验证
http_strict_ssl:
prober: http
timeout: 10s
http:
fail_if_ssl: false
tls_config:
insecure_skip_verify: false
ca_file: /etc/ssl/certs/ca-certificates.crt
# TCP 端口探测
tcp_connect:
prober: tcp
timeout: 5s
# TCP 探测 - 带协议握手
tcp_mysql:
prober: tcp
timeout: 5s
tcp:
query_response:
- send: "\x00\x00\x00\x0a" # MySQL 握手包
- expect: "^[\\x00-\\xff]" # 匹配响应
# TCP 探测 - SSH 端口
tcp_ssh:
prober: tcp
timeout: 5s
tcp:
query_response:
- expect: "^SSH-2.0-"
# ICMP 探测
icmp:
prober: icmp
timeout: 5s
icmp:
preferred_ip_protocol: ip4
# DNS 探测
dns_check:
prober: dns
timeout: 5s
dns:
query_name: "example.com"
query_type: A
valid_rcodes: [NOERROR]
validate_answer_rrs:
fail_if_matches_regexp:
- ".*127.0.0.1"
fail_if_not_matches_regexp:
- ".*10\\.0\\..*"
# DNS 探测 - 检查 SOA
dns_soa:
prober: dns
timeout: 5s
dns:
query_name: "example.com"
query_type: SOA
transport_protocol: tcp
valid_rcodes: [NOERROR]
3.2 Docker 部署
# docker-compose.yml
version: '3.8'
services:
blackbox-exporter:
image: prom/blackbox-exporter:v0.25.0
container_name: blackbox
ports:
- "9115:9115"
volumes:
- ./blackbox.yml:/etc/blackbox_exporter/config.yml
command:
- '--config.file=/etc/blackbox_exporter/config.yml'
- '--web.listen-address=:9115'
- '--log.level=info'
restart: unless-stopped
注意:ICMP 探测需要额外权限。Docker 环境下需要添加
cap_add: [NET_ADMIN]或使用 host 网络。
四、HTTP 探测
4.1 基础 HTTP 探测
# Prometheus scrape 配置
scrape_configs:
- job_name: 'blackbox-http'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://www.example.com
- https://api.example.com/health
- https://app.example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
4.2 探测返回的指标
# 手动测试探测结果
curl -s "http://blackbox:9115/probe?target=https://example.com&module=http_2xx" | grep probe_
返回的关键指标:
| 指标 | 说明 |
|---|---|
probe_success | 探测是否成功 (0/1) |
probe_duration_seconds | 探测总耗时 |
probe_http_status_code | HTTP 状态码 |
probe_http_version | HTTP 协议版本 |
probe_http_content_length | 响应内容长度 |
probe_ssl_earliest_cert_expiry | SSL 证书最早到期时间(Unix 时间戳) |
probe_ssl_last_information | SSL 证书信息 |
probe_dns_lookup_time_seconds | DNS 解析耗时 |
probe_tcp_connection_seconds | TCP 连接耗时 |
probe_tls_handshake_seconds | TLS 握手耗时 |
probe_http_duration_seconds{phase=...} | 各阶段耗时(dns/connect/tls/transfer) |
4.3 探测各阶段耗时
# 查看 HTTP 请求各阶段耗时
probe_http_duration_seconds{phase="dns"} # DNS 解析
probe_http_duration_seconds{phase="connect"} # TCP 连接
probe_http_duration_seconds{phase="tls"} # TLS 握手
probe_http_duration_seconds{phase="transfer"} # 数据传输
probe_http_duration_seconds{phase="processing"}# 服务端处理
这些阶段指标可以帮助你定位延迟瓶颈——是 DNS 慢、网络慢还是服务处理慢。
五、TCP 端口探测
5.1 基础 TCP 探测
scrape_configs:
- job_name: 'blackbox-tcp'
metrics_path: /probe
params:
module: [tcp_connect]
static_configs:
- targets:
- 'mysql:3306'
- 'redis:6379'
- 'kafka:9092'
- 'ssh:22'
- 'smtp:25'
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
5.2 协议握手探测
简单端口可达性检查不足以验证服务是否真正可用。通过 query_response 可以做协议层握手验证:
modules:
# MySQL 健康检查
tcp_mysql:
prober: tcp
tcp:
query_response:
- expect: "^.*\\x00\\x00\\x00\\x0a.*" # MySQL 握手响应
# Redis PING
tcp_redis:
prober: tcp
tcp:
query_response:
- send: "PING\r\n"
- expect: "^\\+PONG"
# SMTP 检查
tcp_smtp:
prober: tcp
tcp:
query_response:
- expect: "^220 .* SMTP"
- send: "EHLO blackbox\r\n"
- expect: "^250"
# PostgreSQL 检查
tcp_postgres:
prober: tcp
tcp:
query_response:
- send: "\x00\x00\x00\x08\x04\xd2\x16\x2f" # PgSSLRequest
- expect: "^[SN]" # S=支持SSL, N=不支持
六、ICMP 探测
6.1 ICMP 配置
scrape_configs:
- job_name: 'blackbox-icmp'
metrics_path: /probe
params:
module: [icmp]
static_configs:
- targets:
- '192.168.1.1' # 网关
- '8.8.8.8' # 公网 DNS
- '10.0.0.1' # 内网核心交换机
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
6.2 ICMP 返回指标
| 指标 | 说明 |
|---|---|
probe_success | ICMP 是否收到响应 |
probe_duration_seconds | 往返时间(RTT) |
probe_ip_protocol | 使用的 IP 协议(4/6) |
ICMP 探测适合网络设备可用性监控——交换机、路由器、防火墙等不支持 HTTP 的设备。
七、DNS 探测
7.1 DNS 配置
modules:
# 检查 A 记录解析
dns_a_record:
prober: dns
dns:
query_name: "www.example.com"
query_type: A
valid_rcodes: [NOERROR]
# 检查特定 DNS 服务器
dns_custom_server:
prober: dns
dns:
query_name: "internal.example.com"
query_type: A
transport_protocol: udp
preferred_ip_protocol: ip4
ip_protocol_fallback: false
7.2 Prometheus 配置
scrape_configs:
- job_name: 'blackbox-dns'
metrics_path: /probe
params:
module: [dns_a_record]
static_configs:
- targets:
- '8.8.8.8' # Google DNS
- '1.1.1.1' # Cloudflare DNS
- 'dns-internal:53' # 内网 DNS
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
八、SSL 证书到期监控
8.1 探测配置
SSL 证书过期是最常见的"非技术故障"——一个忘记续期的证书可以让整个网站不可访问。
scrape_configs:
- job_name: 'ssl-cert-monitor'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://www.example.com
- https://api.example.com
- https://admin.example.com
- https://grafana.example.com
- https://prometheus.example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
8.2 证书到期告警
groups:
- name: ssl-cert
rules:
# 证书 30 天内过期
- alert: SSLCertExpiringSoon
expr: |
probe_ssl_earliest_cert_expiry - time() < 30 * 24 * 3600
for: 1h
labels:
severity: warning
annotations:
summary: "SSL 证书即将过期: {{ $labels.instance }}"
description: "证书将在 {{ $value | humanizeDuration }} 后过期"
# 证书 7 天内过期
- alert: SSLCertExpiringCritical
expr: |
probe_ssl_earliest_cert_expiry - time() < 7 * 24 * 3600
for: 1h
labels:
severity: critical
annotations:
summary: "SSL 证书紧急过期: {{ $labels.instance }}"
description: "证书将在 7 天内过期,请立即续期!"
runbook: "https://wiki.internal/runbooks/ssl-renewal"
# 证书已过期
- alert: SSLCertExpired
expr: |
probe_ssl_earliest_cert_expiry - time() < 0
for: 5m
labels:
severity: critical
annotations:
summary: "SSL 证书已过期: {{ $labels.instance }}"
description: "证书已过期,网站可能无法访问!"
8.3 证书信息仪表盘
# 证书剩余天数
probe_ssl_earliest_cert_expiry - time() / 86400
# 证书签发者
probe_ssl_last_information{info="issuer"}
# 证书主题
probe_ssl_last_information{info="subject"}
九、多地域拨测
9.1 多地域部署架构
┌─── 北京机房 ────────────────┐
│ Blackbox-Beijing │
│ 探测全国用户入口 │
└──────────────┬───────────────┘
│
▼
┌──────────────┐
│ Prometheus │
│ (汇总指标) │
└──────┬───────┘
▲
┌─── 上海机房 ────────────────┐
│ Blackbox-Shanghai │
│ 探测华东用户入口 │
└──────────────────────────────┘
┌─── 广州机房 ────────────────┐
│ Blackbox-Guangzhou │
│ 探测华南用户入口 │
└──────────────────────────────┘
┌─── 海外节点 ────────────────┐
│ Blackbox-Overseas │
│ 探测海外用户入口 │
└──────────────────────────────┘
9.2 多地域 Prometheus 配置
scrape_configs:
# 北京节点拨测
- job_name: 'blackbox-beijing'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://www.example.com
labels:
region: beijing
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-beijing:9115
# 上海节点拨测
- job_name: 'blackbox-shanghai'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://www.example.com
labels:
region: shanghai
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-shanghai:9115
# 广州节点拨测
- job_name: 'blackbox-guangzhou'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://www.example.com
labels:
region: guangzhou
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-guangzhou:9115
9.3 多地域延迟对比
# 各地域访问延迟对比
probe_duration_seconds{job=~"blackbox-.*"} by (region, instance)
# 地域间延迟差异
max(probe_duration_seconds) by (instance) - min(probe_duration_seconds) by (instance)
9.4 多地域告警
groups:
- name: multi-region
rules:
# 所有地域都不可达 → critical
- alert: ServiceDownAllRegions
expr: |
count by(instance) (probe_success == 0) >= 3
for: 2m
labels:
severity: critical
annotations:
summary: "服务在所有地域不可达: {{ $labels.instance }}"
description: "3 个地域同时探测失败,服务可能完全宕机"
# 单地域不可达 → warning
- alert: ServiceDownSingleRegion
expr: |
probe_success == 0 and on(instance)
count by(instance) (probe_success == 0) < 3
for: 5m
labels:
severity: warning
annotations:
summary: "服务在单地域不可达: {{ $labels.instance }} ({{ $labels.region }})"
description: "仅 {{ $labels.region }} 探测失败,可能是网络路由问题"
# 地域间延迟差异过大
- alert: HighLatencyVariance
expr: |
(max by(instance) (probe_duration_seconds) -
min by(instance) (probe_duration_seconds)) > 2
for: 10m
labels:
severity: warning
annotations:
summary: "地域间延迟差异过大: {{ $labels.instance }}"
description: "最快与最慢地域延迟差异超过 2 秒"
十、可用性 SLO 度量
10.1 计算可用性
# 可用性 = 成功探测次数 / 总探测次数
avg_over_time(probe_success[30d]) * 100
# 按服务计算
avg by(instance) (avg_over_time(probe_success[30d])) * 100
# 对比 SLO 目标(如 99.9%)
avg_over_time(probe_success[30d]) * 100 < 99.9
10.2 SLO 告警
groups:
- name: availability-slo
rules:
# 可用性低于 99.9%(30 天窗口)
- alert: AvailabilitySLOBreach
expr: |
avg by(instance) (avg_over_time(probe_success[30d])) < 0.999
for: 5m
labels:
severity: warning
slo: availability-999
annotations:
summary: "可用性 SLO 违约: {{ $labels.instance }}"
description: "近 30 天可用性 {{ $value | humanizePercentage }},低于 99.9% 目标"
# 可用性低于 99%(严重违约)
- alert: AvailabilitySLOCritical
expr: |
avg by(instance) (avg_over_time(probe_success[30d])) < 0.99
for: 5m
labels:
severity: critical
slo: availability-999
annotations:
summary: "可用性严重违约: {{ $labels.instance }}"
description: "近 30 天可用性 {{ $value | humanizePercentage }},低于 99%"
10.3 响应时间 SLO
groups:
- name: latency-slo
rules:
# 95 分位响应时间 > 500ms
- alert: LatencySLOBreach
expr: |
histogram_quantile(0.95,
sum by(le, instance) (rate(probe_duration_seconds_bucket[5m]))
) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "响应时间 SLO 违约: {{ $labels.instance }}"
description: "P95 响应时间 {{ $value }}s,超过 500ms 目标"
十一、动态目标发现
11.1 从 Consul 发现探测目标
scrape_configs:
- job_name: 'blackbox-consul'
metrics_path: /probe
params:
module: [http_2xx]
consul_sd_configs:
- server: 'consul:8500'
services: ['web', 'api']
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
# 从 Consul metadata 获取探测模块
- source_labels: [__meta_consul_service_metadata_probe_module]
target_label: __param_module
regex: (.+)
replacement: '${1}'
11.2 从 Kubernetes Ingress 发现
scrape_configs:
- job_name: 'blackbox-k8s-ingress'
metrics_path: /probe
params:
module: [http_2xx]
kubernetes_sd_configs:
- role: ingress
relabel_configs:
# 只探测有 prometheus.io/probe 注解的 Ingress
- source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe]
action: keep
regex: true
# 使用 Ingress 的 host 作为探测目标
- source_labels: [__meta_kubernetes_ingress_scheme, __address__, __meta_kubernetes_ingress_path]
regex: (.+);(.+);(.+)
target_label: __param_target
replacement: ${1}://${2}${3}
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox:9115
十二、Grafana 仪表盘
12.1 推荐仪表盘布局
┌─────────────────────────────────────────────────────┐
│ Blackbox Exporter 仪表盘 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 总探测数 │ │ 成功率 │ │ 平均延迟 │ │
│ │ 142 │ │ 99.3% │ │ 127ms │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 探测结果时间线(成功率趋势) │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 探测延迟时间线 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌────────────────────┐ │
│ │ 失败目标列表 │ │ SSL 证书到期天数 │ │
│ │ Target | Status │ │ 域名 | 剩余天数 │ │
│ └──────────────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────┘
12.2 关键 PromQL 查询
# 总体成功率
avg(probe_success) * 100
# 按目标成功率
avg by(instance) (probe_success) * 100
# 平均探测延迟
avg(probe_duration_seconds)
# P95/P99 探测延迟
histogram_quantile(0.95, sum by(le) (rate(probe_duration_seconds_bucket[5m])))
histogram_quantile(0.99, sum by(le) (rate(probe_duration_seconds_bucket[5m])))
# SSL 证书剩余天数
(probe_ssl_earliest_cert_expiry - time()) / 86400
# 各阶段耗时分布
probe_http_duration_seconds{phase="dns"}
probe_http_duration_seconds{phase="connect"}
probe_http_duration_seconds{phase="tls"}
probe_http_duration_seconds{phase="transfer"}
总结
Blackbox Exporter 是 Prometheus 生态中唯一的外部探测工具,填补了"用户视角可用性监控"的空白:
- 多协议探测:HTTP/TCP/ICMP/DNS 四种探测方式,覆盖网络层到应用层
- SSL 证书监控:自动检测证书到期时间,提前告警避免因证书过期导致的故障
- 多地域拨测:从不同地域探测同一目标,发现地域性网络问题和 CDN 配置错误
- 可用性 SLO:基于
probe_success计算 30 天可用性,直接度量用户视角的 SLO - 动态发现:支持从 Consul/Kubernetes 动态发现探测目标,自动跟随基础设施变化
- 丰富指标:各阶段耗时(DNS/TCP/TLS/Transfer)帮助定位延迟瓶颈
Blackbox Exporter 应该是每个监控体系的标配——内部健康(Prometheus + Exporter)和外部可达(Blackbox Exporter)两者缺一不可,共同构成完整的可用性监控。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Blackbox Exporter 官方文档 — GitHub 开源社区,参考了Blackbox Exporter 官方文档相关内容