概述

Prometheus 的常规监控是"从内向外看"——Prometheus 采集 Exporter 指标,了解系统内部状态。但用户访问你的服务时,走的是"从外向内"的路径:DNS 解析 → 网络路由 → 负载均衡 → 后端服务。一条从内部看完全健康的链路,可能因为 DNS 配置错误、CDN 缓存问题或 SSL 证书过期而导致用户完全无法访问。

Blackbox Exporter 解决的是"从外部探测"的问题。它模拟用户行为,从外部对 HTTP/TCP/ICMP/DNS 端点发起探测,让你获得"用户视角"的可用性数据。详细梳理 Blackbox Exporter 的配置、各类探测方式、SSL 证书监控、多地域拨测方案和可用性 SLO 度量。

参考来源:Blackbox Exporter 官方文档

一、为什么需要外部探测

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_codeHTTP 状态码
probe_http_versionHTTP 协议版本
probe_http_content_length响应内容长度
probe_ssl_earliest_cert_expirySSL 证书最早到期时间(Unix 时间戳)
probe_ssl_last_informationSSL 证书信息
probe_dns_lookup_time_secondsDNS 解析耗时
probe_tcp_connection_secondsTCP 连接耗时
probe_tls_handshake_secondsTLS 握手耗时
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_successICMP 是否收到响应
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)两者缺一不可,共同构成完整的可用性监控。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Blackbox Exporter 官方文档 — GitHub 开源社区,参考了Blackbox Exporter 官方文档相关内容