Prometheus生产案例:基于Consul 大规模服务发现监控部署

1. 文档概述

1.1 适用范围

本文针对物理机/虚拟机环境下的 Linux 主机监控 + 通用服务发现场景编写,适配 100 ~ 10000 台服务器的生产环境落地。Kubernetes 场景由 Consul 控制面注入 Sidecar 代理,不适用本文的独立 Client 部署模式。

1.2 软件版本基准

所有配置与机制说明基于以下版本编写,生产部署请以对应版本官方文档为最终依据:

  • Consul 1.20.x(社区稳定版)
  • Prometheus 2.53.x LTS
  • Node Exporter 1.8.2
  • 操作系统:Ubuntu 22.04 / 24.04 LTS

1.3 分层架构

整套体系自下而上分为 5 层,各层职责严格分离,避免功能重叠与架构混乱:

                    业务 / 运维使用层
                           │
                    Grafana / Alertmanager
                           │
                    Prometheus 监控层
                           │
                    Consul 服务发现层
                           │
              Consul Client + Exporter
                           │
                       Linux 主机

1.4 核心结论

Consul 是服务目录与健康状态的唯一事实源,负责「谁存在、是否可用」;Prometheus 是指标体系的事实源,负责「采集什么、存多久、如何查询告警」。
二者为互补关系,不存在功能替代。

1.5 选型依据

选择 Consul 作为服务发现组件的核心原因:

  1. 原生支持 Agent-Local 健康检查,可自动剔除故障实例
  2. 支持服务元数据标签,便于 Prometheus 过滤与分组聚合
  3. 分布式架构,Client 节点资源占用低(单实例内存 < 100MB)
  4. 同时支持 HTTP API 与 DNS 两种查询方式,生态适配广泛
  5. 经过大规模生产环境验证,稳定性与成熟度高

2. 整体架构设计

2.1 完整拓扑

                                ┌──────────────────────┐
                                │       Grafana        │
                                │ Dashboard / Explore  │
                                └──────────┬───────────┘
                                           │
                          ┌────────────────▼────────────────┐
                          │       Prometheus A / B          │
                          │ 独立 TSDB / PromQL / Rules      │
                          └────────────────┬────────────────┘
                                           │
                                  Consul Service Discovery
                                           │
                    ┌──────────────────────▼──────────────────────┐
                    │              Consul Server Cluster          │
                    │                                              │
                    │       Server-01  Server-02  Server-03       │
                    │              Raft + Catalog + ACL            │
                    └──────────────────────┬───────────────────────┘
                                           │
                              LAN Gossip / Agent Communication
                                           │
              ┌────────────────────────────┼────────────────────────────┐
              │                            │                            │
      ┌───────▼────────┐          ┌────────▼───────┐          ┌────────▼───────┐
      │ Consul Client  │          │ Consul Client  │          │ Consul Client  │
      │ Node Exporter  │          │ Node Exporter  │          │ Node Exporter  │
      │ Other Exporter │          │ Other Exporter │          │ Other Exporter │
      └───────┬────────┘          └────────┬───────┘          └────────┬───────┘
              │                            │                            │
          Linux Host                   Linux Host                   Linux Host
              │                            │                            │
              └────────────────────────────┼────────────────────────────┘
                                           │
                                      1000+ Hosts

Prometheus A/B
      │
      ├──────────────► Alertmanager Cluster ─────► 企业微信 / Email / Webhook
      │
      └──────────────► Long-term Storage(按需)
                         Thanos / Mimir / VictoriaMetrics

2.2 核心设计原则:控制流与数据流分离

这是本架构的核心设计思想,避免控制面成为数据瓶颈:

  • 控制流:服务注册、健康状态、成员关系通过 Consul Agent → Gossip → Server 同步,仅传递状态信息。
  • 数据流:Prometheus 直接拉取 Exporter 指标,不经过 Consul 转发,指标流量不经过控制面。

2.2.1 服务注册链路(控制流)

Linux 主机 → Consul Client → Service Register → Health Check → Consul Catalog

2.2.2 指标采集链路(数据流)

Linux → Node Exporter :9100 → Prometheus → TSDB → Grafana
              ↑
        Consul SD 提供目标列表

2.2.3 故障传导链路

Exporter 故障 → Consul Health = critical → Prometheus SD 过滤 → Scrape 异常 → 告警规则触发 → Alertmanager → 通知

2.3 组件核心职责

组件 核心职责
Consul Server Raft 一致性、服务目录、集群状态、ACL 等控制面能力
Consul Client 每台业务机上的 Agent,本地服务注册、健康检查、成员通信
Node Exporter 暴露 Linux 主机操作系统与硬件指标
Prometheus 指标拉取、TSDB 存储、PromQL 查询、Recording Rule、告警规则计算
Alertmanager 告警路由、分组、抑制、静默、多渠道通知
Grafana 指标可视化与交互式查询
Thanos/Mimir/VictoriaMetrics 等 按规模提供长期存储、全局查询、高可用写入能力
CMDB 资产事实与生命周期管理,作为自动化的数据源

2.4 组件职责边界

Consul 不承担

  • 指标存储与计算
  • 可视化面板
  • 日志采集与检索
  • 全量资产管理
  • PromQL 查询

Prometheus 不承担

  • 服务生命周期管理
  • 节点故障检测与成员管理
  • 资产注册与元数据管理

三者定位

CMDB = 资产属性的真相(这台机器是什么、归谁管、生命周期)
Consul = 服务运行的真相(现在有哪些实例、健康状态如何)
Prometheus = 运行指标的真相(这些实例产生了什么指标、性能如何)

三者互补,不能互相替代。


3. 核心原理

3.1 Consul 架构原理

3.1.1 Server 与 Client

Server

配置 server = true,是集群的控制面,仅部署奇数台(3/5台)。
核心职责:

  • Raft 一致性选举与数据持久化
  • 维护全局服务目录(Catalog)
  • ACL 权限控制
  • 处理跨节点的查询请求
  • 维护 Leader/Follower 角色

生产原则:Server 仅作为控制面运行,禁止在 Server 节点注册业务服务、运行业务 Exporter

Client

配置 server = false,部署在每台业务机上,是集群的数据面。
核心职责:

  • 本地服务注册与注销
  • 本地执行健康检查
  • 参与 LAN Gossip,同步集群状态
  • 承接本机业务的 API 查询请求
  • 将本地状态同步到 Server 集群

关键特性:Client 不参与 Raft 选举,不存储一致性数据,本身无状态,单节点故障不影响全局集群。

3.1.2 Raft 一致性与法定人数

Raft 是 Consul Server 之间的强一致性协议,仅在 Server 节点间运行,用于保证集群数据一致。

  • 3 节点集群:法定人数(Quorum)= 2,最多容忍 1 台节点故障
  • 5 节点集群:法定人数 = 3,最多容忍 2 台节点故障

重要结论:3 节点集群只能同时挂 1 台;挂 2 台即失去法定人数,集群无法写入,也无法选举新 Leader。

3.1.3 Gossip 协议

基于 Serf 实现,分为 LAN Gossip(同数据中心所有节点)和 WAN Gossip(跨数据中心 Server)。
核心作用:

  • 节点自动发现与加入
  • 秒级节点故障检测
  • 成员状态全网扩散
  • 事件广播

生产强制要求:开启 Gossip 加密,防止恶意节点加入集群。

3.1.4 Agent-Local 健康检查模型

这是 Consul 与多数集中式注册中心的本质区别:

所有健康检查由注册服务的本地 Agent 执行,Server 不做远程探测。

例如本机部署了 Consul Client + Node Exporter,HTTP 健康检查由本地 Client 定时调用目标地址,仅将最终健康状态同步到集群。

核心价值:

  1. 检测结果最接近业务真实状态,无中间网络链路干扰
  2. 探测压力分布式分摊,大规模场景下 Server 无探测压力
  3. 避免集中式探测的单点瓶颈

3.1.5 Client 节点的生产必要性

千节点规模下,Client 不是冗余的转发代理,而是架构的核心支柱:

  1. 可靠的健康检查:没有 Client 则无本地健康检测能力,直连 Server 注册的服务,进程退出后集群无法感知,会产生大量僵尸实例。
  2. 节点级故障自动收敛:Client 是集群正式成员,节点宕机/断网通过 Gossip 秒级感知,自动摘除所有关联服务;远程注册无法实现该能力。
  3. 保护 Server 控制面:所有业务侧的注册、查询请求都在本机消化,Server 仅处理一致性写入,大幅降低控制面压力。
  4. 本地状态缓存:Client 持有全量服务目录副本,查询亚毫秒级响应;Server 短暂故障时,本地缓存可降级提供服务发现能力。
  5. 收敛安全边界:Client 默认可绑定 127.0.0.1,高权限 Token 无需扩散到业务机,大幅缩小攻击面。
  6. 运维解耦:服务注册配置随业务生命周期管理,升级变更不影响 Server 集群稳定性。

可省略 Client 的场景仅包括:10 台以内测试环境、纯静态服务目录且无需健康检查、容器编排平台已有原生服务发现机制。
千节点 Linux 生产环境,禁止将直连 Server API 注册作为标准方案

3.2 Prometheus HA 与容量原理

3.2.1 原生 Prometheus 无主备机制

原生 Prometheus 是单实例架构,不存在内置的主备复制、自动切换机制。

  • 双实例部署为冗余模式:A/B 两台独立抓取相同目标、各自维护独立的 TSDB 和 WAL,互不复制数据。
  • 查询侧需通过 Grafana 双数据源、或接入 Thanos Query 等统一查询层实现高可用。
  • 告警侧依赖 Alertmanager 集群去重,避免双实例重复发送告警。

说明:VictoriaMetrics、Thanos 等衍生方案提供集群化写入能力,不属于原生 Prometheus 范畴。

3.2.2 双实例 HA 方案

推荐拓扑:

                  Consul Catalog
                       │
           ┌───────────┴───────────┐
           │                       │
     Prometheus A            Prometheus B
           │                       │
           └───────────┬───────────┘
                       │
                Alertmanager 集群

设计要点:

  • A/B 实例部署在不同故障域(不同物理机、不同机架)
  • 使用完全一致的 scrape、rule 配置
  • 独立存储,互不依赖
  • 告警统一发送到 Alertmanager 集群,由后者完成去重、分组、路由

3.2.3 容量规划核心逻辑

“1000 台服务器需要多大配置”不是通用命题。真正决定 Prometheus 容量的核心因素:

  • Target 总数量
  • 单 Target 的活跃时序数(Active Series)
  • 抓取间隔(scrape_interval)
  • 标签基数(Cardinality)
  • Recording Rule / Alert Rule 数量与复杂度
  • 查询并发与面板复杂度
  • 数据保留时长
  • 磁盘 IOPS 与吞吐量

结论:不存在“单台 Prometheus 最多支持 X 台服务器”的通用标准,所有规格都需要基于实际场景压测确认。

3.2.4 主机监控场景容量估算(经验参考)

以下为默认配置下的经验值,仅用于前期估算,生产必须以实际压测为准:

  • 单台 Node Exporter 默认 Collector:约 200~300 个活跃时序
  • 开启 systemd/processes 等高开销 Collector:时序数增加 30%~100%
  • 1000 台主机默认配置:约 20~30 万活跃时序
  • 15s 抓取间隔下,100 万时序约对应 1.5 万 samples/sec

规格参考(15s 抓取间隔)

活跃时序规模 CPU 内存 30天保留磁盘占用估算
50 万 8核 16GB 1.5~2.25 TB
200 万 16核 64GB 6~9 TB

核心观测指标

prometheus_tsdb_head_series          # 当前活跃时序数
rate(prometheus_tsdb_head_samples_appended_total[5m])  # 每秒写入样本数
process_resident_memory_bytes        # 进程内存占用
rate(prometheus_tsdb_compactions_failed_total[5m])     # 压缩失败次数

4. 网络与系统前置准备

4.1 端口规划

4.1.1 Consul 端口

端口 协议 角色 用途 开放范围
8300 TCP Server Raft 一致性同步 / Server RPC Server 节点之间
8301 TCP+UDP Server+Client LAN Gossip 成员发现与心跳 所有集群内节点
8302 TCP+UDP Server WAN 跨数据中心 Gossip 跨 DC 时使用,单 DC 可关闭
8500 TCP Server+Client HTTP API / Web UI Server: 仅运维/监控网段;Client: 仅 127.0.0.1
8501 TCP Server HTTPS API 按需开启 TLS 后使用
8600 TCP+UDP Server+Client DNS 服务发现 按需开放

⚠️ 重要:8301 必须同时放通 TCP 和 UDP,仅放通 TCP 会导致 Gossip 通信异常,节点频繁离线。

4.1.2 其他组件端口

组件 端口 开放范围
Node Exporter 9100 仅 Prometheus 网段
Prometheus 9090 仅运维/监控网段
Alertmanager 9093 仅 Prometheus 网段 + 运维网段
Grafana 3000 运维/业务访问网段

4.2 防火墙原则

生产环境禁止通过关闭防火墙解决网络问题
正确做法:

  1. 防火墙保持开启状态
  2. 按最小权限原则,仅开放必要端口,限制来源网段
  3. 管理面(8500、9090、3000)严禁 0.0.0.0/0 开放,严禁直接暴露公网
  4. 数据面(9100)仅允许 Prometheus 所在网段访问

4.3 系统基础配置

4.3.1 时间同步

所有节点必须配置时间同步,Raft 协议对时钟偏差敏感,节点间时间差建议控制在 1s 以内,过大可能导致选举异常、日志时序混乱。

apt update && apt install -y chrony curl wget unzip jq vim
systemctl enable --now chronyd
chronyc tracking
timedatectl set-timezone Asia/Shanghai

核心要求:时间同步正常 + 时区统一 + 日志时间可关联。

4.3.2 系统参数优化

  • Client 节点:nofile = 65536 可满足需求
  • Server 节点:可根据规模上调至 655360
  • 所有参数调整需基于实际压测,并非数值越大越好
cat >> /etc/sysctl.conf << 'EOF'
net.core.somaxconn = 65535
net.ipv4.ip_local_port_range = 1024 65535
fs.file-max = 655360
vm.max_map_count = 262144
EOF
sysctl -p

4.3.3 磁盘与故障域要求

Consul Server
  • 必须使用 SSD 磁盘,Raft 日志对 fsync 延迟极度敏感,机械盘会导致频繁 Leader 选举
  • 建议 IOPS ≥ 3000,写入延迟 < 10ms
  • 3 台 Server 必须分布在不同故障域(不同物理机、不同机架、不同交换机),避免单点故障导致全集群失效
Prometheus
  • 优先 SSD,关注持续写入吞吐量与存储空间
  • 双实例部署在不同故障域,避免同时故障

5. Consul Server 集群部署

5.1 节点规划

  • 1000~3000 台主机规模:3 台 Server
  • 5000 台以上或多业务线:5 台 Server
  • Server 必须专用,不承载业务服务

5.2 目录规范(遵循 FHS 标准)

/etc/consul.d/
├── consul.hcl          # 主配置文件
├── services/           # 服务注册配置目录(Server 节点不建议存放业务服务)
└── certs/              # TLS 证书目录

/var/lib/consul/        # 数据目录(对应 data_dir)

日志统一通过 systemd 管理,使用 journalctl -u consul 查看。

5.3 安装二进制

cd /tmp
# 版本请替换为企业内部验证通过的版本
wget https://releases.hashicorp.com/consul/1.20.0/consul_1.20.0_linux_amd64.zip
unzip consul_1.20.0_linux_amd64.zip
install -m 0755 consul /usr/local/bin/consul
consul version

# 创建系统用户
useradd --system --home /var/lib/consul --shell /usr/sbin/nologin consul
mkdir -p /etc/consul.d/services /var/lib/consul
chown -R consul:consul /etc/consul.d /var/lib/consul

5.4 Server 配置文件

路径:/etc/consul.d/consul.hcl,每个节点修改 node_namebind_addradvertise_addr

# 数据中心名称,同一集群所有节点必须一致
datacenter = "dc1"

# 数据存储目录
data_dir = "/var/lib/consul"

# 服务注册配置目录
config_dir = "/etc/consul.d/services"

# 日志级别
log_level = "INFO"

# 节点名称,集群内唯一
node_name = "consul-server-01"

# 集群内部通信绑定地址,填写本机内网IP,不可写 0.0.0.0
bind_addr = "10.0.0.71"
advertise_addr = "10.0.0.71"

# HTTP API 监听地址
client_addr = "0.0.0.0"

# 开启 Server 模式
server = true

# 集群期望 Server 节点数,3节点集群设为 3,所有节点必须一致
bootstrap_expect = 3

# 集群成员发现列表,填写所有 Server 节点地址
retry_join = ["10.0.0.71:8301", "10.0.0.72:8301", "10.0.0.73:8301"]

# Web UI 配置(Consul 1.18+ ui 字段已废弃)
ui_config {
  enabled = true
}

# Raft 性能参数
performance {
  raft_multiplier = 1
}

# 节点离线超过 7 天禁止启动,防止脏数据污染集群;0 表示关闭校验
server_rejoin_age_max = "168h"

# 端口配置
ports {
  http = 8500
  https = -1
  dns = 8600
  grpc = -1
  serf_lan = 8301
  serf_wan = 8302
  server = 8300
}

5.5 systemd 服务文件

路径:/etc/systemd/system/consul.service

[Unit]
Description=Consul Server Agent
After=network-online.target
Wants=network-online.target

[Service]
User=consul
Group=consul
Type=simple
ExecStart=/usr/local/bin/consul agent -config-dir=/etc/consul.d
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=655360

[Install]
WantedBy=multi-user.target

5.6 集群启动与验收

  1. 依次启动 3 个节点
systemctl daemon-reload
systemctl enable --now consul
  1. 验收标准(全部通过才算集群健康)
# 1. 进程状态正常
systemctl status consul

# 2. 所有节点状态为 alive
consul members

# 3. Raft 集群正常,1 个 Leader,2 个 Follower
consul operator raft list-peers

# 4. 确认 Leader 信息
consul info | grep leader

注意:HTTP API 可访问 ≠ Raft 集群健康,必须验证 raft list-peers。


6. Consul Client 节点部署

说明:Consul Client 和 Consul Server 使用同一个二进制程序,只是配置文件里 server=false 就变成客户端,不需要单独安装客户端版本。

安装参考Consul Server

6.1 Client 配置文件

路径:/etc/consul.d/consul.hcl

datacenter = "dc1"
data_dir = "/data/consul"  # 注意权限 chown -R consul:consul
log_level = "WARN"

# 节点名称,全局唯一,建议使用主机名
node_name = "prometheus"

bind_addr = "10.0.0.42"
advertise_addr = "10.0.0.42"

# 安全加固:仅本机可调用 API
client_addr = "127.0.0.1"

# 关闭 Server 模式,纯客户端
server = false

# 集群 Server 节点列表
retry_join = ["10.0.0.71:8301", "10.0.0.72:8301", "10.0.0.73:8301"]

# 优雅退出时主动通知集群
leave_on_terminate = true
# 重启后自动重连集群
rejoin_after_leave = true

6.2 systemd 服务文件

路径:/etc/systemd/system/consul.service

[Unit]
[Unit]
Description=Consul Client Agent
After=network-online.target
Wants=network-online.target

[Service]
User=consul
Group=consul
Type=simple
ExecStart=/usr/local/bin/consul agent -config-dir=/etc/consul.d -config-dir=/etc/consul.d/services
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

6.3 加入集群与验证

systemctl daemon-reload
systemctl enable --now consul

# 验证节点已加入集群,状态为 alive
[root@Ans-prometheus /etc/consul.d]# consul members
Node        Address         Status  Type    Build  Protocol  DC   Partition  Segment
consul1     10.0.0.71:8301  alive   server  2.0.0  2         dc1  default    <all>
consul2     10.0.0.72:8301  alive   server  2.0.0  2         dc1  default    <all>
consul3     10.0.0.73:8301  alive   server  2.0.0  2         dc1  default    <all>
prometheus  10.0.0.42:8301  alive   client  2.0.0  2         dc1  default    <default>

6.4 生命周期说明

  • systemctl stop consul 会触发优雅退出,Client 主动发送 leave 消息,集群实时收敛状态
  • 异常断电、强制终止等场景不会触发 leave,依赖 Gossip 协议检测节点故障
  • 节点异常离线后,默认 reconnect_timeout(72h)超时后,自动从 Catalog 中清理

7. Node Exporter 部署与服务注册

7.1 Node Exporter 部署

cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar zxf node_exporter-1.8.2.linux-amd64.tar.gz
install -m 0755 node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/

useradd --system --no-create-home --shell /usr/sbin/nologin node_exporter

服务文件 /etc/systemd/system/node_exporter.service

[Unit]
Description=Prometheus Node Exporter
After=network-online.target
Wants=network-online.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
  --web.listen-address=:9100 \
  --collector.systemd

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

启动服务:

systemctl daemon-reload
systemctl enable --now node_exporter

7.2 验证

必须分两步验证,二者不等价:

# 1. 本机验证,确认 Exporter 正常运行
curl http://127.0.0.1:9100/metrics

# 2. 在 Prometheus 节点执行,确认网络链路可达
curl http://10.0.10.20:9100/metrics

7.3 生产参数原则

  1. 默认 Collector 优先:默认开启的采集器已覆盖绝大多数主机指标,无需全开
  2. 高开销采集器按需开启systemdprocessesinterrupts 等会显著增加时序数,确认业务需要再开启
  3. 安全加固:生产环境建议通过 --web.config.file 配置 Basic Auth 或 TLS,避免未授权访问指标
  4. 高危采集器管控textfile 采集器可自定义指标,需严格管控写入权限,防止恶意注入高基数指标

7.4 服务注册配置

  • 配置文件方式,生产推荐
    服务定义放入 /etc/consul.d/services/ 目录,支持 .hcl.json 格式。
    示例:/etc/consul.d/services/node_exporter.hcl
service {
  id      = "node-exporter-10.0.10.20"
  name    = "node-exporter"
  address = "10.0.10.20"
  port    = 9100

  meta = {
    environment = "prod"
    team        = "ops"
    os          = "ubuntu2204"
  }

  check {
    id       = "node-exporter-http"
    name     = "node-exporter metrics check"
    http     = "http://10.0.10.20:9100/metrics"
    interval = "15s"
    timeout  = "5s"
    # 持续故障 24 小时后自动注销服务
    deregister_critical_service_after = "24h"
  }
}

生效与验证:

# 校验配置语法
consul validate /etc/consul.d

# 热加载配置
consul reload

# 验证服务注册
# 列出集群中所有注册的服务名称
[root@Ans-prometheus /etc/consul.d/services]# consul catalog services
consul
node-exporter


# 查询 node-exporter 服务对应的所有实例节点
[root@Ans-prometheus /etc/consul.d/services]# consul catalog nodes -service=node-exporter
Node        ID        Address    DC
consul2     402f1e7d  10.0.0.72  dc1
prometheus  de77a92c  10.0.0.42  dc1

  • 手动注册
curl -X PUT \
  -d '{
    "id": "node-exporter-10.0.0.71",
    "name": "node-exporter",
    "address": "10.0.0.71",
    "port": 9100,
    "meta": {
      "environment": "prod",
      "team": "ops",
      "os": "ubuntu2204"
    },
    "checks": [
      {
        "http": "http://10.0.0.71:9100/metrics",
        "interval": "15s",
        "timeout": "5s",
        "deregister_critical_service_after": "24h"
      }
    ]
  }' \
  http://127.0.0.1:8500/v1/agent/service/register

字段 含义 说明
id 服务实例唯一 ID 实例级别全局唯一;重复相同 id 注册会直接覆盖旧实例;推荐规范:node-exporter-10.0.0.71;注销接口也依靠该 id 定位实例。
name 服务名称 服务分组标识,同一服务下全部实例name保持一致;Prometheus consul_sd_configs.services 匹配的就是该字段,用于筛选一类服务实例。
address 服务实例 IP 业务服务真实监听 IP;Consul 服务目录对外暴露的实例地址,Prometheus 服务发现读取该地址作为采集目标。
port 服务端口 业务服务监听端口,和 address 组合构成实例访问地址。
meta 实例元数据 key‑value 键值对;会生成__meta_consul_service_metadata_元标签,配合action:labelmap自动转换成 Prometheus 标签;用于存放 env、team、os 等业务维度。
tags 服务标签数组 字符串数组;仅用于 Consul API/UI 过滤;仅产出单个合并元标签__meta_consul_tags,不会被 labelmap 自动拆分;exporter 场景一般不使用。
checks 健康检查配置 数组,支持配置多条健康检查;由接收注册请求的本地 Consul Agent 执行探测,Server 不直接做检查。
checks[].id 检查项 ID 单条检查的唯一标识,可选;建议填写,便于区分多条不同检查。
checks[].name 检查项名称 可读描述名称,Consul UI 展示使用,纯展示用途。
checks[].http HTTP 检查地址 Agent 周期性 GET 请求;返回 2xx‑3xx 判定为passing,其余状态为critical。
checks[].interval 检查间隔 健康检查执行周期,例15s;不要设置过小,避免大量实例带来 Agent CPU 压力。
checks[].timeout 检查超时时间 HTTP 探测超时阈值,超时直接判定检查失败,应小于 interval。
checks[].deregister_critical_service_after 故障自动注销时间 实例持续 critical 状态达到该时长,Agent 自动注销该服务;生产建议配置24h,规避主机临时抖动产生无效注册残留。
  • 手动注销
    和上面的注册命令一一对应,通过服务 ID(Service ID)注销,命令如下:
    curl -X PUT \
      http://127.0.0.1:8500/v1/agent/service/deregister/node-exporter-10.0.0.71
    
    • 命令拆解
      • 接口路径:/v1/agent/service/deregister/{service_id}
        路径最后一段就是注册时 id 字段的值,必须和注册时完全一致(大小写敏感)
        示例中注册 ID 为 node-exporter-10.0.0.71,因此注销路径对应写该值

      • 请求方法:PUT(Consul Agent 接口统一要求)

      • 作用范围:Agent 级别接口,在哪个节点的 Agent 上注册的,就要在哪个节点上注销;跨节点无法直接注销其他节点的服务

7.5 注册方式对比

方式 推荐场景 优点 缺点
本地配置文件 固定基础设施、常驻 Exporter 持久化稳定、随 Agent 自动注册、可审计 静态,修改需 reload
本地 HTTP API 动态应用、自动化发布 实时生效、无需重启 Agent 需配套注销逻辑,否则遗留僵尸服务
CLI 命令 测试调试、排障 操作简单 不适合大规模生产
远程 Server API 仅临时测试 无需部署 Client 无本地健康检查、无法自动感知故障、不推荐生产

7.6 Service ID 设计规范

  • 必须全局唯一,推荐格式:服务名-IP服务名-主机名
  • 同一 Agent 上 Service ID 重复会导致后注册的覆盖先注册的
  • 禁止只用服务名作为 ID,否则多实例会互相覆盖

7.7 健康检查机制说明

常用检查类型

类型 适用场景 判断标准
HTTP 检查 Web 服务、Exporter 返回 2xx~3xx 状态码为健康
TCP 检查 数据库、中间件端口 端口可建立连接为健康
TTL 检查 核心业务进程 服务主动上报心跳,超时未报为异常
Script 检查 自定义业务逻辑 脚本退出码 0 为健康(慎用,有安全风险)

Node Exporter 场景优先使用 HTTP /metrics 检查,比 TCP 检查更准确。

自动注销机制

deregister_critical_service_after 机制说明:

  1. 注册该服务的本地 Agent 维护,并非 Server 全局处理
  2. 服务持续处于 critical 状态超过阈值,且 Agent 在线时,自动注销服务
  3. 如果 Agent 本身离线,该机制不生效,依赖节点故障的 Gossip 收敛
  4. 阈值不建议设置过短(如 < 10min),避免网络抖动导致服务频繁被摘除

节点故障与服务故障的区别

场景 Consul Node 状态 服务健康状态 处理机制
Exporter 挂了,主机正常 alive critical 健康检查检测,超阈值自动注销服务
主机断电/断网 failed 随节点标记不可用 Gossip 检测节点故障,超时后自动清理节点
主机正常下线 left 随节点注销 Agent 主动 leave,状态实时收敛

8. Prometheus Consul 服务发现集成

8.1 SD 工作原理

完整链路:

Consul Catalog + Health → Prometheus Consul SD → Meta Labels → Relabel → 最终 Target → Scrape

Prometheus 定期从 Consul 获取服务列表与健康状态,通过 Relabel 规则过滤生成最终抓取目标,全程无需重启 Prometheus。

8.2 生产级配置示例

以下提供两套生产可用的配置方案,均基于 Consul 分布式架构原则设计,可根据实际规模与运维成本选型。


8.2.1 本机 Consul Client 方案(生产标准推荐)

8.2.1.1 设计思路与角色边界

本方案是千节点以上生产环境的标准落地方式,核心思想是将 Consul 数据面前置到 Prometheus 本机

  • 三台 Consul Server 仍独立部署,作为集群控制面,保持专用、无业务负载
  • 在 Prometheus 所在服务器上,额外部署一个 纯 Client 模式 的 Consul 进程(server = false),作为集群的数据面节点加入集群
  • 该 Client 节点通过 Gossip 协议与三台 Server 同步全量服务目录与健康状态,在本地维护完整缓存
  • Prometheus 仅需访问本机 127.0.0.1:8500 即可获取服务列表,无需直连任何 Server

高可用原理:Client 自动感知集群存活节点,只要 Server 集群仍满足法定人数(3 节点集群挂 1 台仍正常),本地数据就能持续同步,Prometheus 侧完全无感知,真正实现服务发现层高可用。

⚠️ 注意:这不是「把其中一台 Server 和 Prometheus 混部」。Client 是无状态的数据面角色,资源占用极低(<100MB 内存),不参与 Raft 选举,故障不影响集群一致性;与 Server 混部的方案完全不同,也不推荐 Server 与 Prometheus 混部。

8.2.1.2 前置条件

Prometheus 所在机器已部署 Consul Client 并成功加入集群,Client API 仅监听 127.0.0.1

8.2.1.3 配套 Consul Client 配置

路径:/etc/consul.d/consul.hcl

datacenter = "dc1"
data_dir = "/var/lib/consul"
config_dir = "/etc/consul.d/services"

node_name = "prometheus-sd-client"
bind_addr = "Prometheus本机内网IP"
advertise_addr = "Prometheus本机内网IP"
client_addr = "127.0.0.1"

# 核心:纯客户端模式,不参与选举
server = false

# 三台 Server 地址,用于加入集群
retry_join = ["10.0.0.71:8301", "10.0.0.72:8301", "10.0.0.73:8301"]

leave_on_terminate = true
rejoin_after_leave = true
log_level = "WARN"

启动并验证:

systemctl enable --now consul
# 正常应看到 3 台 Server + 本机 Client,状态均为 alive
consul members
8.2.1.4 Prometheus 配置
scrape_configs:
  - job_name: "node-exporter"
    # 抓取全局配置
    scrape_interval: 15s        # 抓取间隔,主机监控推荐 15s
    scrape_timeout: 10s         # 抓取超时,预留充足冗余
    metrics_path: "/metrics"    # 显式声明指标路径,避免歧义

    # Consul 服务发现配置
    consul_sd_configs:
      - server: "127.0.0.1:8500"   # 仅对接本机 Consul Client,无单点故障
        datacenter: "dc1"          # 必须与集群实际 DC 名称严格一致
        refresh_interval: "30s"    # 服务列表刷新间隔,平衡实时性与压力
        services: ["node-exporter"] # 源头仅拉取指定服务,减少无效开销
        allow_stale: true          # 允许非 Leader 返回数据,降低控制面压力
        # 开启 ACL 时使用,凭证文件需严格限制权限
        # token_file: "/etc/prometheus/secrets/consul-token"

    # 标签重写规则
    relabel_configs:
      # 1. 核心过滤:仅保留健康状态为 passing 的实例,故障自动摘流
      - source_labels: [__meta_consul_health]
        regex: "passing"
        action: keep

      # 2. 拼接最终抓取地址:服务IP + 服务端口
      - source_labels: [__meta_consul_service_address, __meta_consul_service_port]
        separator: ":"
        target_label: __address__

      # 3. 映射业务标签:节点名称、数据中心、环境元数据
      - source_labels: [__meta_consul_node]
        target_label: node
      - source_labels: [__meta_consul_datacenter]
        target_label: datacenter
      - source_labels: [__meta_consul_service_metadata_environment]
        target_label: environment
8.2.1.5 方案核心优势
  1. 真正的高可用:基于 Consul 原生分布式协议,集群只要不失去法定人数,服务发现就不受影响,远优于多地址轮询
  2. 查询性能最优:全量服务目录本地缓存,亚毫秒级返回,无跨网络请求开销
  3. 保护控制面:Prometheus 不直接请求 Server,大幅降低控制面读压力
  4. 安全边界清晰:Server 的 8500 端口无需对 Prometheus 网段开放,仅需开放 8301 Gossip 端口,攻击面更小
  5. 配置简洁:Prometheus 侧仅需一行本地地址,无需重复配置多台 Server

8.2.2 多 Server 直连冗余方案(无额外进程)

8.2.2.1 适用场景

中小规模场景,不想额外部署 Consul Client 进程,通过配置多台 Server 地址实现服务发现层的基本冗余。
Prometheus 会并行从所有 Server 拉取服务列表,基于实例标签自动去重;单台 Server 故障时,其余节点仍可正常返回目标。

8.2.2.2 Prometheus 配置
scrape_configs:
  - job_name: "node-exporter"
    scrape_interval: 15s
    scrape_timeout: 10s
    metrics_path: "/metrics"

    consul_sd_configs:
      # 第 1 台 Server
      - server: "10.0.0.71:8500"
        datacenter: "dc1"
        refresh_interval: "30s"
        services: ["node-exporter"]
        allow_stale: true
      # 第 2 台 Server
      - server: "10.0.0.72:8500"
        datacenter: "dc1"
        refresh_interval: "30s"
        services: ["node-exporter"]
        allow_stale: true
      # 第 3 台 Server
      - server: "10.0.0.73:8500"
        datacenter: "dc1"
        refresh_interval: "30s"
        services: ["node-exporter"]
        allow_stale: true

    relabel_configs:
      - source_labels: [__meta_consul_health]
        regex: "passing"
        action: keep
      - source_labels: [__meta_consul_service_address, __meta_consul_service_port]
        separator: ":"
        target_label: __address__
      - source_labels: [__meta_consul_node]
        target_label: node
      - source_labels: [__meta_consul_datacenter]
        target_label: datacenter
      - source_labels: [__meta_consul_service_metadata_environment]
        target_label: environment
8.2.2.3 注意事项
  1. 三台配置的 servicesdatacenter 必须完全一致,否则失去冗余意义
  2. 会产生重复查询请求,对 Server 有轻微额外压力
  3. 仅适合中小规模场景,千节点以上强烈推荐方案一

8.2.3 关键参数详解

参数 作用与生产建议
server 对接的 Consul Agent 地址,单条配置仅支持单个 host:port,不支持数组;多实例冗余需写多条 consul_sd_configs
datacenter 必须与 Consul 集群实际数据中心名称严格一致;不一致会返回空结果且无明确报错,是常见隐形坑
services 从源头过滤服务,仅拉取指定名称的服务,大幅减少无效数据传输与后续 Relabel 开销;支持多服务数组;不支持通配符
refresh_interval 服务列表刷新间隔,默认 60s;主机监控场景 30s 足够,过频会增加 Consul 压力
allow_stale 允许非 Leader 节点返回读请求,大幅降低 Leader 压力,同时提升多节点可用性;服务发现场景对秒级一致性无要求,强烈建议开启
token_file 开启 ACL 时使用,建议用文件存储凭证,禁止明文写入配置;需严格限制文件权限为 600

8.2.4 Relabel 规则设计说明

  1. 健康状态过滤action: keep 仅保留 passing 状态的实例,是 Consul SD 最核心的规则,实现「故障实例自动摘流」的核心能力
  2. 抓取地址拼接:使用 service_address + service_port 生成最终抓取地址,确保抓取的是服务真实端口,而非 Consul 节点默认端口
  3. 业务标签映射:将 Consul 中的节点、元数据映射为 Prometheus 永久标签,用于后续聚合、过滤、告警路由
  4. 标签设计原则:保留默认 instance = IP:Port,额外新增 node 标签存储主机名;禁止用主机名覆盖 instance,避免丢失精确定位信息

8.2.5 常见配置陷阱

  1. 服务名不匹配services 填写的名称必须与 Consul 注册的 service.name 完全一致(大小写敏感),否则发现不到任何目标
  2. datacenter 不一致:Consul 默认值为 dc1,若自定义了 DC 名称此处必须同步修改;该错误无明确报错,排查难度高
  3. 健康过滤导致无目标:90% 的「Consul 正常但 Prometheus 无 Target」问题,都是服务健康状态非 passing 被规则过滤;排查优先查看 Prometheus UI → Status → Service Discovery 中的 Dropped Targets
  4. 单地址单点故障:仅配置一台 Server 地址时,该节点故障会导致服务发现完全停滞,生产环境必须做高可用
  5. 明文 Token 风险:禁止在配置文件中明文写入 ACL Token,必须使用 token_file 并严格限制文件权限与访问范围

8.2.3 验证

# 查看 Consul 中服务实例数
[root@consul2 ~]# curl -s http://10.0.0.71:8500/v1/catalog/service/node-exporter | jq length
2
[root@consul2 ~]# curl -s http://10.0.0.71:8500/v1/catalog/service/node-exporter | jq '.[].Node'
"consul2"
"prometheus"

# 查看 Prometheus 生效的目标数
[root@consul2 ~]# curl -s http://10.0.0.42:9090/api/v1/targets | jq '.data.activeTargets | length'
3
[root@consul2 ~]# curl -s http://10.0.0.42:9090/api/v1/targets | jq -r '.data.activeTargets[].labels.instance'
10.0.0.72:9100
10.0.0.42:9100
localhost:9090

9. 标签治理与数据规范

9.1 推荐标签体系

适合聚合、过滤、告警路由的维度标签:

environment=prod
region=cn-east-1
az=az01
team=ops
os=ubuntu2204
service=node-exporter

9.2 高基数陷阱

禁止作为 Label 的内容

  • request_id、user_id、订单号、UUID
  • 时间戳、随机字符串
  • 自由文本内容

核心原则

Label 是用于聚合的维度,不是日志字段。高基数标签会导致时序数爆炸,撑爆 TSDB。

错误示例:

http_requests_total{request_id="a7f8..."}  # 完全不可聚合,基数爆炸

正确方向:

http_requests_total{method="GET", route="/api/orders", status="200"}

9.3 系统边界

推荐数据流:

CMDB(资产事实)→ 自动化平台 → Consul 注册(服务发现事实)→ Prometheus SD → 指标(运行事实)

三者各司其职,不要用 Consul 存储全量资产信息,也不要用 Prometheus 做资产管理。


10. 自动化批量部署

10.1 项目目录规范

monitoring/
├── inventory/
│   ├── production
│   └── staging
├── group_vars/
├── roles/
│   ├── consul_server/
│   ├── consul_client/
│   ├── node_exporter/
│   └── prometheus/
├── templates/
└── site.yml

10.2 Role 职责划分

Role 负责内容
consul_server Server 安装、配置、systemd、TLS、ACL、验证
consul_client Client 安装、配置、systemd、Gossip、Agent Token
node_exporter Exporter 安装、systemd、Consul 服务注册配置
prometheus Prometheus 部署、配置、规则、SD 配置

10.3 幂等性要求

重复执行 Playbook 不能产生重复用户、重复配置、重复服务注册。

  • 全部使用 Ansible 官方幂等模块
  • 配置文件使用 template 管理
  • 服务注册配置随 Role 统一管理

10.4 分批发布原则

禁止一次性推送 1000 台节点,避免打爆 Server 集群。
推荐批次:10 → 50 → 100 → 500 → 全量
每批次观察指标:

  • Server CPU、内存、磁盘 IO
  • Gossip 状态、Raft 延迟
  • Catalog 增长速度
  • Prometheus Target 与时序增长
# 参考
- name: Deploy Consul Client + Node Exporter
  hosts: consul_clients
  gather_facts: yes
  vars:
    consul_encrypt_key: "<Gossip 加密密钥>"
    consul_agent_token: "<Client Agent Token>"
  tasks:
    - name: Create directories
      file:
        path: "{{ item }}"
        state: directory
        mode: '0755'
      loop:
        - /data/consul/data
        - /data/consul/conf.d
        - /data/node_exporter

    - name: Install Consul binary
      unarchive:
        src: https://releases.hashicorp.com/consul/1.20.0/consul_1.20.0_linux_amd64.zip
        dest: /usr/local/bin/
        remote_src: yes
        creates: /usr/local/bin/consul

    - name: Create consul user
      user:
        name: consul
        shell: /sbin/nologin
        create_home: no

    - name: Set consul dir permissions
      file:
        path: /data/consul
        owner: consul
        group: consul
        recurse: yes

    - name: Generate client config
      copy:
        content: |
          datacenter = "dc1"
          data_dir = "/data/consul/data"
          config_dir = "/data/consul/conf.d"
          node_name = "{{ ansible_hostname }}"
          bind_addr = "{{ ansible_default_ipv4.address }}"
          advertise_addr = "{{ ansible_default_ipv4.address }}"
          client_addr = "127.0.0.1"
          server = false
          retry_join = ["10.0.0.71", "10.0.0.72", "10.0.0.73"]
          log_level = "WARN"
          leave_on_terminate = true
          rejoin_after_leave = true

          encrypt = "{{ consul_encrypt_key }}"

          acl {
            enabled = true
            default_policy = "deny"
            tokens {
              agent = "{{ consul_agent_token }}"
            }
          }
        dest: /data/consul/consul.hcl

    - name: Deploy consul systemd service
      copy:
        content: |
          [Unit]
          Description=Consul Client Agent
          After=network-online.target
          Wants=network-online.target
          [Service]
          User=consul
          Group=consul
          Type=simple
          ExecStart=/usr/local/bin/consul agent -config-file=/data/consul/consul.hcl
          ExecReload=/bin/kill -HUP $MAINPID
          Restart=on-failure
          RestartSec=5
          LimitNOFILE=655360
          [Install]
          WantedBy=multi-user.target
        dest: /etc/systemd/system/consul.service

    - name: Install Node Exporter
      unarchive:
        src: https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
        dest: /data/node_exporter/
        remote_src: yes
        extra_opts: [--strip-components=1]
        creates: /data/node_exporter/node_exporter

    - name: Deploy node_exporter service
      copy:
        content: |
          [Unit]
          Description=Prometheus Node Exporter
          After=network-online.target
          [Service]
          Type=simple
          ExecStart=/data/node_exporter/node_exporter --web.listen-address=:9100 --collector.systemd
          Restart=on-failure
          RestartSec=5
          [Install]
          WantedBy=multi-user.target
        dest: /etc/systemd/system/node_exporter.service

    - name: Register node_exporter to Consul
      copy:
        content: |
          {
            "service": {
              "id": "node_exporter-{{ ansible_default_ipv4.address }}",
              "name": "node_exporter",
              "address": "{{ ansible_default_ipv4.address }}",
              "port": 9100,
              "checks": [
                {
                  "http": "http://127.0.0.1:9100/metrics",
                  "interval": "15s",
                  "timeout": "5s",
                  "deregister_critical_service_after": "72h"
                }
              ]
            }
          }
        dest: /data/consul/conf.d/node_exporter.json

    - name: Start all services
      systemd:
        name: "{{ item }}"
        daemon_reload: yes
        state: started
        enabled: yes
      loop:
        - consul
        - node_exporter

11. 全生命周期管理

11.1 新增服务器标准流程

1. OS 初始化与安全加固
2. 时间同步与基础配置
3. 部署 Consul Client
4. 加入 Consul 集群,验证节点状态
5. 部署 Node Exporter / 业务 Exporter
6. 配置服务注册与健康检查
7. 验证健康状态为 passing
8. Prometheus SD 自动发现 Target
9. 验证指标采集正常、up == 1
10. Grafana 可查询、告警正常生效

11.2 正常下线流程

1. 业务流量摘除
2. 注销服务注册(consul services deregister / API 注销)
3. 停止 Exporter
4. 执行 consul leave 优雅退出 Client
5. 停止 consul 服务
6. 服务器下电/回收
7. 验证节点从 Consul 成员列表消失、Prometheus Target 消失

11.3 节点替换

  • 新旧节点主机名、IP 均变化时,确保清理旧节点的 Node ID、Service ID
  • 禁止只修改 IP 不清理旧数据,避免产生残留条目

12.4 批量扩容

从 1000 台扩容到 3000 台时,同步观察:

  • Consul:Catalog 规模、Gossip 延迟、Server 资源、Raft 延迟
  • Prometheus:Target 数量、Active Series、Samples/sec、WAL/TSDB 状态、查询延迟
  • 瓶颈通常先出现在 Prometheus 侧,而非 Consul 侧

12. 自监控与告警体系

12.1 Consul 自监控关键指标

  • 集群是否有 Leader
  • Raft 对等节点数量是否符合预期
  • Raft 提交延迟
  • 节点离线数量
  • 健康检查 critical 数量
  • ACL 错误次数
  • Server 磁盘使用率

12.2 Prometheus 自监控关键指标

  • Target Up 率
  • Scrape 失败率
  • Active Series 增长率
  • Samples 写入速率
  • WAL 写入失败、TSDB 压缩失败
  • 规则评估延迟
  • Remote Write 延迟与失败率
  • 磁盘空间使用率

12.3 最低告警集合(必配)

  1. Consul 无集群 Leader
  2. Consul Server 节点离线
  3. Consul Client 大量离线
  4. Node Exporter 实例故障
  5. Prometheus Scrape 失败率过高
  6. Prometheus TSDB / WAL 异常
  7. Active Series 突增(基数爆炸预警)
  8. 监控组件磁盘空间不足
  9. 规则评估异常
  10. Alertmanager 集群异常

原则:监控系统自身的故障告警,优先级高于业务告警。


13. 备份、升级与容灾

13.1 Snapshot 快照备份

社区版 Consul 无内置自动快照功能,需通过脚本定时执行。

备份命令

consul snapshot save /backup/consul-$(date +%Y%m%d-%H%M%S).snap
  • 可在任意 Server 节点执行,建议在 Follower 节点执行,避免影响 Leader 性能
  • 快照包含全量集群数据:KV、服务注册、ACL、会话、配置条目
  • 备份文件属于敏感数据,需加密存储、严格控制访问权限

定时备份脚本

#!/bin/bash
BACKUP_DIR="/data/backup/consul"
mkdir -p $BACKUP_DIR
consul snapshot save ${BACKUP_DIR}/consul-$(date +%Y%m%d).snap
# 保留 30 天
find $BACKUP_DIR -name "*.snap" -mtime +30 -delete

加入 crontab 每日凌晨执行。

13.2 恢复演练

有备份 ≠ 能恢复,必须定期做恢复演练。

恢复流程:

搭建新集群(同版本) → 初始化单节点 → 执行 snapshot restore → 验证 Raft 状态 → 验证服务目录 → 验证 ACL → 接入 Prometheus 验证 SD
  • 恢复操作会覆盖目标集群所有数据,严禁在正常运行的生产集群上直接执行
  • 建议至少每季度做一次恢复演练,验证备份有效性

13.3 版本升级

生产升级流程:

  1. 阅读对应版本 Release Notes,确认兼容性与废弃变更
  2. 测试环境完整验证功能、配置兼容性
  3. 生产集群执行 Snapshot 备份
  4. 先升级 Follower 节点,滚动重启,验证正常
  5. 最后升级 Leader 节点(会触发一次重新选举)
  6. 全量验证集群状态、服务注册、Prometheus SD
  7. 观察 24 小时无异常后完成升级

禁止通过 apt upgrade 无脑升级,Consul 大版本常有配置不兼容变更。


14. 企业级安全加固

参考架构:

                        Consul Datacenter
                              │
             ┌────────────────┴────────────────┐
             │                                 │
       Control Plane                       Data Plane
             │                                 │
       3/5 Server Agents             1000+ Client Agents
             │                                 │
             ├── Raft                     ├── Service
             ├── ACL                      ├── Health Check
             ├── Catalog                  └── Workload
             └── CA / Config

14.1 ACL 访问控制

14.1.1 什么是 ACL

ACL(Access Control List,访问控制列表)是 Consul 内置的权限管理系统。它通过 Token(令牌)Policy(策略) 两个核心概念,实现对集群资源的细粒度访问控制。

  • Token:一串唯一的密钥(形如 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),代表一个身份。当客户端向 Consul 发起 API 请求或执行 CLI 命令时,需要携带 Token 来证明自己的身份。
  • Policy:一组权限规则的集合,定义了“允许对哪些资源执行哪些操作”。Policy 可以绑定到一个或多个 Token 上,Token 的最终权限是其关联的所有 Policy 的并集。

Consul 的资源按前缀划分,主要包括:

资源前缀 说明
agent 本地 Agent 的操作,如注册/注销服务、更新健康检查
node 节点信息,如节点注册、注销、健康状态更新
service 服务目录信息,如服务注册、查询、注销
key KV 存储中的键值
session 会话管理
acl ACL 自身的策略和 Token 管理
operator 集群运维操作,如 Raft 快照、节点强制离线

ACL 的核心目标是 最小权限原则:每个 Token 只拥有完成其职责所必需的最小权限,避免权限过大造成安全风险。

14.1.2 核心目标

  • 默认拒绝所有未授权操作:开启 ACL 后,default_policy = "deny" 表示任何未携带有效 Token 的请求都会被拒绝。这类似于“默认关上门”,只有持有钥匙的人才能进入。
  • 按最小权限原则分配 Token:不为图省事给所有组件同一个高权限 Token,而是为不同角色创建专用 Token。
  • 高权限 Management Token 仅管理员持有:Bootstrap 生成的最高权限 Token(相当于 root)只能由管理员保存在安全位置,严禁写入任何业务节点的配置文件。
  • Agent 仅配置 Agent Token:所有 Consul Agent(Server 和 Client)的配置中只使用 Agent Token,该 Token 仅允许操作本节点的服务和节点信息,不能修改全局策略或其他节点。
  • 监控组件仅授予只读 Token:Prometheus 等监控系统只需要查询服务目录,因此授予只读权限,避免监控系统被攻破后成为攻击入口。

14.1.3 完整实施步骤

以下步骤假设集群当前未启用 ACL,所有节点处于正常运行状态。

步骤 1:备份集群数据

ACL 的启用属于高风险操作,一旦配置错误可能导致集群不可用。因此,务必先执行快照备份。

consul snapshot save /data/consul/backup/pre-acl-$(date +%Y%m%d).snap

备份文件应妥善保存,确保可恢复。

步骤 2:所有 Server 节点开启 ACL(平滑过渡方案)

直接在所有节点上启用 default_policy = "deny" 会导致所有未携带 Token 的请求立即被拒绝,可能造成集群功能中断(例如 Client 尚未配置 Token)。因此,推荐采用 “先 allow 后 deny” 的滚动收紧策略。

第一阶段: 在所有 Server 节点的配置文件 /data/consul/consul.hcl 中添加以下内容:

acl = {
  enabled        = true
  default_policy = "allow"          # 先保持宽松,保证现有功能不受影响
  enable_token_persistence = true
}
  • enabled = true:开启 ACL 功能。
  • default_policy = "allow":暂时允许所有未携带 Token 的请求,确保现有业务不受影响。
  • enable_token_persistence = true:将 Token 信息持久化到磁盘,避免重启丢失。

然后滚动重启所有 Server 节点:

systemctl restart consul

确保每个节点重启后集群正常(consul members 显示所有节点 alive,Raft 有 Leader)。

步骤 3:Bootstrap 生成最高权限 Token

在任一 Server 节点执行:

consul acl bootstrap

此命令仅在集群首次启用 ACL 时有效,会生成一个全局管理 Token(Management Token),拥有不受限的权限。返回内容类似:

{
  "ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "AccessorID": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
  "SecretID": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz",
  "Description": "Bootstrap Token (Global Management)"
}

务必妥善保存 SecretID。这是集群最高权限 Token,一旦丢失无法找回,只能重建集群并从备份恢复。

步骤 4:创建 Agent 专用 Policy 和 Token

Agent Token 供 Consul Server 和 Client 节点自身使用,权限应严格限制在“管理本节点的节点信息和本节点注册的服务”,不得包含全局管理权限。

创建 Policy 文件 /data/consul/acl/agent-policy.hcl

agent_prefix "" {
  policy = "write"
}
node_prefix "" {
  policy = "write"
}
service_prefix "" {
  policy = "write"
}
  • agent_prefix "":允许操作本地 Agent 的 API(如注册服务、更新健康检查)。
  • node_prefix "":允许注册、更新本节点信息。
  • service_prefix "":允许注册、注销本节点提供的服务。

注意:此 Policy 权限较大(写权限覆盖所有 service 前缀),但配合 Agent Token 的绑定机制,实际只能操作本节点 Agent 所管理的服务,因为 Agent 会将请求限制在本地节点。仍建议为每个节点创建独立 Token,避免一个 Token 泄露影响所有节点。

将 Policy 写入 Consul(需使用 Bootstrap Token 授权):

consul acl policy create \
  -name "agent-policy" \
  -description "Minimal policy for Consul agents" \
  -rules @/data/consul/acl/agent-policy.hcl \
  -token "<Bootstrap Token SecretID>"

为每个节点创建独立 Token(推荐):

# 为 consul-01 节点创建 token
consul acl token create \
  -description "Agent token for consul-01" \
  -policy-name agent-policy \
  -token "<Bootstrap Token SecretID>"

记录返回的 SecretID,稍后配置到对应节点的 acl.tokens.agent 字段。

步骤 5:创建 Prometheus 只读 Policy 和 Token

Prometheus 仅需要查询服务目录来发现监控目标,因此授予只读权限即可。

创建只读策略文件 /data/consul/acl/read-only-policy.hcl

node_prefix "" {
  policy = "read"
}
service_prefix "" {
  policy = "read"
}

创建 Policy 和 Token:

consul acl policy create \
  -name "read-only" \
  -description "Read-only policy for monitoring" \
  -rules @/data/consul/acl/read-only-policy.hcl \
  -token "<Bootstrap Token SecretID>"

consul acl token create \
  -description "Prometheus read token" \
  -policy-name read-only \
  -token "<Bootstrap Token SecretID>"
步骤 6:配置 Agent Token 并收紧默认策略
  1. 将所有 Server 和 Client 节点的配置文件 /data/consul/consul.hcl 中的 ACL 部分修改为:
acl = {
  enabled        = true
  default_policy = "deny"           # 收紧为默认拒绝
  enable_token_persistence = true
  tokens = {
    agent = "<对应节点的 Agent Token SecretID>"
  }
}
  1. 按“先 Server 后 Client”的顺序滚动重启所有节点:
systemctl restart consul

重启后,所有未携带 Token 的请求将被拒绝,而配置了 Agent Token 的节点能够正常注册服务、上报健康状态。

  1. 验证集群状态:
consul members
consul operator raft list-peers

确保所有节点均处于 alive 状态,Raft 有稳定的 Leader。

步骤 7:验证权限
  • 无 Token 访问应被拒绝
curl -s http://10.0.0.71:8500/v1/catalog/services
# 预期返回 403 Forbidden
  • 使用只读 Token 查询应成功
curl -s -H "X-Consul-Token: <read-only-token>" http://10.0.0.71:8500/v1/catalog/services
# 预期返回服务列表
  • 使用 Agent Token 尝试创建全局策略应被拒绝
consul acl policy create -name test -rules 'node_prefix "" { policy = "write" }' -token "<agent-token>"
# 预期返回 Permission denied

14.1.4 ACL 常见故障与处理

故障现象 可能原因 解决方案
Agent 启动后日志报 ACL not found Agent Token 配置错误或未创建 检查 token 是否正确,重新创建 token 并配置
consul members 显示节点状态异常 Agent Token 缺少 node_prefix write 权限 修正 agent-policy,添加相应权限
Prometheus 目标发现为空 只读 Token 无效或权限不足 检查 token,确保 service_prefix readnode_prefix read
忘记 Bootstrap Token 管理 Token 丢失 无法找回,只能重建集群并从备份恢复(如有快照)

14.2 Gossip 加密

14.2.1 目的

Consul 使用基于 Serf 的 Gossip 协议进行节点成员管理和故障检测。Gossip 通信默认是明文的,同一网络中的恶意节点可以:

  • 伪造成员加入集群,窃取服务目录或进行拒绝服务攻击。
  • 嗅探通信内容,获取敏感信息。

通过 Gossip 加密,使用对称密钥对 Gossip 通信进行加密,只有持有相同密钥的节点才能加入集群并正常通信,有效防止未授权节点接入。

14.2.2 配置步骤

生成密钥

在任意节点执行一次:

consul keygen

输出形如 a1b2c3d4-... 的 32 字节密钥(Base64 编码)。该密钥必须安全保存,并分发到所有节点。

所有节点配置

/data/consul/consul.hcl 中添加:

encrypt = "生成的密钥字符串"
encrypt_verify_incoming = true
encrypt_verify_outgoing = true
  • encrypt:设置对称加密密钥。
  • encrypt_verify_incoming:要求所有传入的 Gossip 消息必须经过加密验证,否则拒绝。
  • encrypt_verify_outgoing:要求所有发出的 Gossip 消息必须加密并携带验证信息。

注意encrypt_verify_incoming/outgoing 需要 Consul 1.8 及以上版本,请确保所有节点版本一致。

滚动上线

禁止一次性全量修改,否则节点间因密钥不一致导致 Gossip 通信中断,集群成员关系混乱。

推荐分两个阶段:

  1. 阶段一:所有节点添加 encrypt 密钥,但先设置 encrypt_verify_incoming = falseencrypt_verify_outgoing = false,滚动重启。此时通信已加密,但尚未强制验证。
  2. 阶段二:待全部节点稳定运行后,再开启 encrypt_verify_incoming = trueencrypt_verify_outgoing = true,滚动重启。

14.2.3 验证

consul info | grep -A 5 "serf_lan"
# 确认 encryption 字段为 true

也可以检查日志,确认没有 Gossip 解密失败的错误。


14.3 TLS 加密

14.3.1 适用链路

生产环境建议对以下链路启用 TLS:

  • Prometheus → Consul Server API:保护服务发现查询的数据机密性和完整性,防止中间人攻击。
  • Consul Server 之间的 RPC:保护 Raft 一致性协议传输,防止数据篡改和窃听。
  • Consul Agent 之间的通信(可选但推荐):保护 Client 与 Server 之间的 RPC。

14.3.2 证书准备

使用自建 CA 或 HashiCorp Vault 签发证书。证书必须包含以下 SAN(Subject Alternative Name):

  • 节点的主机名
  • 节点的 IP 地址
  • server.dc1.consul(用于 Server 间通信的统一名称)
  • localhost(可选,便于本地调试)

如果使用自建 CA,需在所有节点上信任该 CA 证书。

14.3.3 配置示例

/data/consul/consul.hcl 中添加:

verify_incoming        = true
verify_outgoing        = true
verify_server_hostname = true
ca_file                = "/data/consul/tls/consul-agent-ca.pem"
cert_file              = "/data/consul/tls/dc1-server-consul-01.pem"
key_file               = "/data/consul/tls/dc1-server-consul-01-key.pem"

auto_encrypt {
  allow_tls = true
}
  • verify_incoming:验证所有传入连接的客户端证书。
  • verify_outgoing:验证所有出站连接的服务端证书。
  • verify_server_hostname:验证服务器证书的主机名是否匹配。
  • ca_file:CA 证书路径。
  • cert_file / key_file:节点自己的证书和私钥路径。
  • auto_encrypt.allow_tls:允许 Client 通过 auto_encrypt 自动获取客户端证书(Server 需开启此选项)。

对于 Client 节点,可使用 auto_encrypt 自动获取客户端证书,减少证书分发工作量。Client 配置中需包含:

auto_encrypt {
  tls = true
}

14.3.4 常见故障

故障现象 原因 处理
证书验证失败:certificate signed by unknown authority CA 证书未正确配置 确保所有节点 ca_file 指向同一 CA
x509: cannot validate certificate for IP ... 证书 SAN 不包含该 IP 重新签发证书,添加正确 SAN
证书过期 未建立轮换机制 使用 Vault 自动轮换或设置告警提前续期

14.4 密钥管理最佳实践

14.4.1 敏感信息范围

  • Gossip 加密密钥
  • ACL Token(Bootstrap、Agent、只读等)
  • TLS 私钥
  • 云厂商访问凭证(如使用 Vault)

14.4.2 管理原则

  1. 禁止明文存储:所有 Token、密钥不得写入 Git、Ansible 明文变量或文档中。
  2. 使用加密存储
    • Ansible Vault:适用于 Ansible 自动化部署场景,加密变量文件。
    • HashiCorp Vault:统一秘密管理,支持动态生成 Consul Token 和证书轮换。
    • 云 KMS:如 AWS KMS、阿里云 KMS,用于加密本地配置文件。
  3. 最小化分发:高权限 Management Token 仅存储在管理员的安全介质中(如密码管理器),禁止分发到业务节点。
  4. 定期轮换:建立密钥轮换机制,尤其是 TLS 证书和 Agent Token。
  5. 审计与追踪:记录 Token 的创建、使用、删除操作,定期审计权限。

14.4.3 Ansible Vault 示例

# 加密变量文件
ansible-vault encrypt secrets.yml

# 在 Playbook 中引用
vars_files:
  - secrets.yml

secrets.yml 内容(加密后):

consul_gossip_key: "a1b2c3d4-..."
consul_agent_token: "xxxxxxxx-..."
prometheus_read_token: "yyyyyyyy-..."

运行时提供 Vault 密码:

ansible-playbook deploy.yml --vault-password-file .vault_pass

14.5 安全加固检查清单

检查项 要求 验证命令
Gossip 加密 所有节点 encrypt 一致,验证开启 consul info | grep encryption
ACL 启用 default_policy=deny,所有操作需 Token 无 Token 请求 API 返回 403
Agent Token 最小权限 acl 写权限 尝试创建策略应被拒绝
Prometheus 只读 Token 仅能查询服务目录 尝试修改服务应被拒绝
TLS 配置 API 和 RPC 使用 HTTPS curl https://... 成功,curl http://... 拒绝
敏感信息加密存储 无明文密钥出现在配置文件中 检查 Git 仓库和服务器文件

14.6 总结

企业级安全加固是 Consul 生产部署的 必选项 而非可选项。通过 ACL 最小权限Gossip 加密TLS 传输加密严格的密钥管理,可有效防止未授权访问、数据泄露和中间人攻击。实施时务必遵循滚动升级、先备份后操作、先验证后收紧的原则,确保业务连续性。安全无小事,任何一个薄弱环节都可能成为攻击突破口,因此上述每一项措施都应严格落实并定期审计。

15. 企业级故障排查手册

1 故障定位总链路

遇到 Grafana 无数据,按从下到上的顺序排查,禁止上来就重启所有服务:

Linux 主机 → Node Exporter → Consul Client → 服务注册 → 健康状态 → Consul Catalog → Prometheus SD → Target → PromQL → Grafana

2 Server 启动失败:server_rejoin_age_max

现象
服务启动立即退出,8500 端口无监听
典型日志

startup error: error="refusing to rejoin cluster because server has been offline for more than the configured server_rejoin_age_max (168h0m0s)"

根因:节点离线超过配置阈值(默认 7 天),本地 Raft 数据过期,集群保护机制禁止启动,防止脏数据污染。

解决方案

  • 场景 A:集群其他节点正常,仅单节点故障
    1. 停止 consul 服务
    2. 仅删除 Raft 数据,保留 node-id:rm -rf /var/lib/consul/raft
    3. 启动服务,节点以全新 Follower 身份从 Leader 同步全量数据
  • 场景 B:整个集群全部宕机,无可用 Leader
    1. 停止所有节点
    2. 所有节点清空数据目录:rm -rf /var/lib/consul/*
    3. 重新启动集群,全新初始化

3 No Cluster Leader

现象:大量报错 No cluster leader,无法写入服务、无法注册。
分步排查

  1. 检查所有 Server 节点进程是否存活
  2. 检查所有 Server 节点 8300 TCP、8301 TCP+UDP 是否互通
  3. 检查节点间时间差是否超过 1s
  4. 检查磁盘是否打满、IO 是否阻塞
  5. 检查所有节点 bootstrap_expect 配置是否一致
  6. 检查是否有过半 Server 节点故障,失去法定人数

禁止上来就删除 data 目录,可能造成不可逆的数据丢失。

4 Node ID 冲突

典型日志

Failed push/pull merge: Member 'xxx' has conflicting node ID

根因:虚拟机克隆、拷贝 data 目录,导致多台机器 node-id 文件相同。
解决方案

  1. 停止冲突节点的 consul 服务
  2. 删除 node-id 文件:rm -f /var/lib/consul/node-id
  3. 启动服务,自动生成全新唯一 ID
  4. 验证 consul members 状态正常

5 Leader 频繁选举

可能原因

  • Server 间网络延迟高、丢包
  • Server 使用机械硬盘,Raft fsync 慢
  • 节点 CPU/内存资源打满
  • 时间同步异常

优化方向

  • Server 部署在同机房低延迟网络
  • 必须使用 SSD 磁盘
  • 排查资源瓶颈,不要只靠调大 raft_multiplier 掩盖问题

6 Consul 正常但 Prometheus 无 Target

排查三步法:

  1. Consul 侧验证:服务是否存在?健康状态是否为 passing?
  2. SD 层验证:Prometheus UI → Service Discovery 中是否能发现该服务?是否被 Dropped?
  3. Relabel 验证:检查 keep/drop 规则,确认健康状态、标签匹配是否符合预期

90% 以上的该类问题,都是健康检查非 passing 被 relabel 过滤导致。

7 Target 存在但 Scrape 失败

  1. 查看 Prometheus UI → Targets 页的具体错误信息
  2. 在 Prometheus 节点手动 curl 目标地址,验证网络连通性
  3. 检查防火墙、安全组、目标端口是否监听
  4. 检查 Relabel 后的地址是否正确
  5. 检查是否有 TLS、认证配置不匹配

8 服务注册与健康检查故障

故障1:配置了服务注册文件,但 Consul 中看不到

排查顺序:

  1. 主配置是否声明了 config_dir
  2. 文件后缀是否为 .json / .hcl
  3. 配置语法是否正确:consul validate 文件路径
  4. 是否执行了 consul reload

故障2:服务注册成功,但健康检查一直 critical

排查顺序:

  1. 在 Client 节点本地 curl 健康检查地址,验证可用性
  2. 检查端口、路径是否正确
  3. 检查本地防火墙是否放行
  4. 检查 address 字段是否为 Client 可访问的真实地址

9 服务启动异常:

error="refusing to rejoin cluster because server has been offline for more than the configured server_rejoin_age_max (168h0m0s) - consider wiping your data dir

错误原因

  • server_rejoin_age_max 默认值 168h(7天)。

  • Consul Server 离线超过 7 天(默认 168 小时)后重新启动时的保护机制。Consul 认为本地数据可能已经过期,拒绝自动重新加入集群,以防止脏数据污染整个 Raft 集群。

场景 1:其他 2 个 Server 正常,仅 consul1 故障

目的:让 consul1 放弃过期数据,以全新节点身份从 Leader 同步。

# 停止 consul1 上的服务
systemctl stop consul

# 备份数据目录(可选,防止误操作)
mv /data/consul/data /data/consul/data_backup_$(date +%F)

# 重新创建空数据目录
mkdir -p /data/consul/data
chown consul:consul /data/consul/data

# 启动 consul
systemctl start consul

# 验证
consul members
consul operator raft list-peers

注意:不需要删除 node-id 文件(在新创建的空目录中会自动生成),所以直接清空 data 目录即可。

场景 2:整个集群全部宕机,无 Leader

如果 3 个节点都处于离线超过 7 天状态,且没有可用 Leader,需要清空所有节点数据并重新初始化,否则集群无法选主。

# 在所有 Server 节点执行
systemctl stop consul
rm -rf /data/consul/data/*
systemctl start consul

# 验证
consul members
consul operator raft list-peers

后果:会丢失所有未备份的 Consul 状态(KV、服务目录、ACL 等)。如果有 snapshot,可以在集群重新建立后恢复。

场景 3:只有 1 个 Server,但配置了 bootstrap_expect=3

如果实际上你只打算运行单节点,需要修改配置:

bootstrap_expect = 1

然后清空数据重启:

systemctl stop consul
rm -rf /data/consul/data/*
# 修改配置 bootstrap_expect = 1
systemctl start consul

场景 4:希望保留本地数据(不推荐)

可以通过调大 server_rejoin_age_max 绕过检查,例如:

server_rejoin_age_max = "720h"

但这样做可能引入数据不一致,仅建议在测试环境或确定集群状态无变化时使用。生产环境优先采用场景 1 的方式从 Leader 同步。

预防措施

  • 确保 Server 节点稳定运行,长时间停机前应执行 consul leave 或提前备份 snapshot。
  • 定期备份:consul snapshot save backup.snap
  • 监控节点离线时长,及时处理。

附录 A:生产排障命令链

# 1. 确认服务是否在 Consul 目录中
consul catalog services | grep node-exporter

# 2. 确认服务健康状态
consul health service node-exporter

# 3. 确认节点成员状态
consul members

# 4. 确认 Raft 集群状态
consul operator raft list-peers

# 5. 本地验证 Exporter
curl -s http://10.0.10.20:9100/metrics | head

# 6. 验证 Prometheus Target 状态
curl -s http://prometheus:9090/api/v1/targets | jq '.data.activeTargets | length'

# 7. 验证指标是否存在
curl -G http://prometheus:9090/api/v1/query \
  --data-urlencode 'query=up{job="node-exporter", instance="10.0.10.20:9100"}'

附录 B:排障优先级口诀

先看主机与网络
再看 Exporter
再看 Consul Client
再看服务注册
再看健康状态
再看 Catalog
再看 Prometheus SD
再看 Target
最后看 PromQL 与 Grafana

禁止上来就重启所有服务,必须逐层定位、最小范围修改。

附录 C:生产设计原则速记

  1. Consul Server = 控制面,Consul Client = 数据面 Agent
  2. 原生 Prometheus 双实例是冗余,不是 Master/Slave
  3. 健康检查只回答「是否应该被发现/采集」,不是「主机是否完全健康」
  4. 固定基础设施优先 Client + 配置文件注册
  5. 动态应用使用本地 Agent API 注册,必须配套注销
  6. CMDB、Consul、Prometheus 三者职责不能混淆
  7. 不能通过关闭防火墙解决生产网络问题
  8. 不能把高权限 Token 分发到上千台业务服务器
  9. 不能把 UUID、用户 ID 等高基数内容作为 Prometheus Label
  10. 生产级 = 正常运行 + 故障可感知 + 故障可恢复 + 灾备可验证

上线验收 Checklist

Consul

Node Exporter

Prometheus

故障演练

posted @ 2026-06-08 23:10  kyle_7Qc  阅读(19)  评论(0)    收藏  举报