Prometheus 告警 && etcd 集群实战

Prometheus 告警 && etcd 集群实战
[TOC]
环境规划
| 实际机器 | IP | 系统 | 角色 |
|---|---|---|---|
Prom | 10.0.0.10 | Ubuntu | Prometheus 服务端 + 拷贝 etcd 证书做监控 |
node1 | 10.0.0.2 | Rocky 10 | node_exporter + etcd 成员 |
node2 | 10.0.0.11 | Rocky 10 | node_exporter + dingtalk 插件(8060) + etcd 成员 |
node3 | 10.0.0.12 | Rocky 10 | node_exporter + Alertmanager(9093) + etcd 成员 |
Docker | 10.0.0.9 | Ubuntu | etcd-workbench 容器 |
Alertmanager 告警实战
Alertmanager 接收由 Prometheus Server 等客户端发来的告警,负责对告警进行分组(Group)与去重(Deduplicate),然后通过路由(Router)将告警分发到正确的接收器(Receiver),如邮件、钉钉、企业微信、PagerDuty 等(其中钉钉/企业微信/飞书通常通过 Webhook 方式对接)
此外,Alertmanager 还支持告警的**静默(Silence)与抑制(Inhibit)**功能 静默:在维护期间临时屏蔽特定告警(通过配置规则) 抑制:当某严重告警触发时,屏蔽其衍生告警(减少噪音)

- GitHub项目地址:
https://github.com/prometheus/alertmanager
环境部署及子路由配置
1)宿主机校验包完整性jiuzhao@Ubuntu ~$ tar tf /home/jiuzhao/下载/alertmanager-0.34.0.linux-amd64.tar.gzalertmanager-0.34.0.linux-amd64/alertmanager-0.34.0.linux-amd64/alertmanageralertmanager-0.34.0.linux-amd64/amtoolalertmanager-0.34.0.linux-amd64/alertmanager.yml# 只抽二进制,不保留冗余的版本目录壳jiuzhao@Ubuntu ~$ scp /home/jiuzhao/下载/alertmanager-0.34.0.linux-amd64.tar.gz node3:/server/pkgs/
2)node3 解包并放置二进制[root@node3 ~]# tar xf /server/pkgs/alertmanager-0.34.0.linux-amd64.tar.gz -C /usr/local/bin alertmanager-0.34.0.linux-amd64/alertmanager alertmanager-0.34.0.linux-amd64/amtool --strip-components=1[root@node3 ~]# alertmanager --version | head -1alertmanager,version 0.34.0'二进制就位,配置文件接下来手写'💡 配置放 /etc/alertmanager/,数据放 /var/lib/alertmanager/
3)创建目录并写入配置[root@node3 ~]# mkdir -p /etc/alertmanager /var/lib/alertmanager[root@node3 ~]# cat > /etc/alertmanager/alertmanager.yml <<'EOF'global: resolve_timeout: 5m # 告警被视为"已解决"的等待时长, 超时仍无更新才标记 resolved # ===== 邮件发送方(发件人)配置 ===== smtp_from: 'jiuhao_linux@163.com' # 发件人邮箱 smtp_smarthost: 'smtp.163.com:465' # 邮件服务器:端口(465=SSL) smtp_auth_username: 'jiuhao_linux@163.com' # 登录账号(一般同发件人) smtp_auth_password: 'AZeQaZRg4hPWt3dK' # 邮箱授权码(不是登录密码) smtp_require_tls: false # 不校验服务端 TLS 证书(内网/测试用, 生产建议 true) smtp_hello: '163.com' # EHLO 握手用的域名
route: group_by: ['alertname'] # 分组: 同名告警合并成一条通知 group_wait: 5s # 组内首条告警到达后等 5s 再发(攒批, 防轰炸) group_interval: 10s # 同一组再次发送通知的间隔 repeat_interval: 5m # 告警持续未恢复时, 重复提醒的间隔 receiver: 'kpyun_system' # 顶层默认接收者(兜底: 子路由都没命中时走这里) # 配置子路由: 按 job 名正则匹配后分发给对应小组 routes: - receiver: 'kpyun_dba' match_re: job: kpyun_dba_exporter # continue=true: 匹配后不中断, 继续向下匹配, 让消息最终也发给系统组 continue: true - receiver: 'kpyun_k8s' match_re: job: kpyun_k8s_exporter continue: true - receiver: 'kpyun_system' match_re: job: .* # .* 匹配任意 job, 即所有告警都再发给系统组 continue: true
# dc = datacenter(数据中心/机房),它不是 Prometheus 内置的,而是我们自己在告警规则里打的一个自定义标签# 告警抑制: 同一 dc 下, 若出现 critical 级告警, 则抑制(不发)同 dc 的 warning 级告警, 减少噪音# dc 就是"机房标签,用来区分"这条告警属于哪个机房";保证"只在同一机房内才抑制",避免把北京机房的 critical 拿去压制深圳机房的 warninginhibit_rules: - source_match: # 触发抑制的"源"条件 severity: critical target_match: # 被抑制的"目标"条件 severity: warning equal: - dc # 源与目标 dc 标签值相同才抑制
receivers:- name: 'kpyun_dba' # 接收组(名字被 route 引用) email_configs: - to: 'jiuzhao996@qq.com' # 该组接收邮箱 send_resolved: true # 告警恢复后仍发一封"已恢复"邮件 headers: { Subject: "[WARN] kpyun linux 报警邮件" } # 邮件主题- name: 'kpyun_k8s' email_configs: - to: 'jiuzhao996@qq.com' send_resolved: true headers: { Subject: "[WARN] kpyun linux 报警邮件" }- name: 'kpyun_system' email_configs: - to: 'jiuzhao996@qq.com' send_resolved: true headers: { Subject: "[WARN] kpyun linux 报警邮件" }EOF
4)语法检查[root@node3 ~]# amtool check-config /etc/alertmanager/alertmanager.ymlChecking '/etc/alertmanager/alertmanager.yml' SUCCESSFound: - global config - route - 1 inhibit rules - 3 receivers - 0 templates✅️ 路由 + 子路由 + 抑制规则 校验通过
5)写 systemd 服务文件[root@node3 ~]# cat > /usr/lib/systemd/system/alertmanager.service <<'EOF'[Unit]Description=kpyun AlertmanagerAfter=network.target
[Service]Type=simpleExecStart=/usr/local/bin/alertmanager \ --config.file=/etc/alertmanager/alertmanager.yml \ --storage.path=/var/lib/alertmanager \ # Alertmanager 的 /-/reload 默认开启, 无需 --web.enable-lifecycle(那是 Prometheus 的参数) --web.listen-address=0.0.0.0:9093Restart=on-failureRestartSec=5
[Install]WantedBy=multi-user.targetEOF
6)启动并验证[root@node3 ~]# systemctl daemon-reload && systemctl enable --now alertmanager.service[root@node3 ~]# systemctl is-active alertmanager.serviceactive[root@node3 ~]# ss -lnt | grep 9093LISTEN 0 4096 *:9093 *:*✅️ Alertmanager 已在 node3:9093 监听
http://10.0.0.12:9093/#/statusAlertmanager 的 9093 和 9094 端口分工明确:9093 负责对外提供服务,9094 负责内部集群通信
| 端口 | 用途 | 核心功能 |
|---|---|---|
| 9093 | Web UI 和 API 端口 | 供你通过浏览器访问 Alertmanager 的界面,或供 Prometheus 等组件通过 API 推送告警 |
| 9094 | 集群通信端口 (Cluster Port) | 当部署多个 Alertmanager 实例以实现高可用时,它们通过这个端口互相通信,同步告警通知和静默状态 |

Prometheus 集成 Alertmanager 实现告警

`修改 Prometheus 配置(路由到 Alertmanager)`1)Prom 上开启 alerting + rule_filesroot@Prom ~# vim /etc/prometheus/prometheus.ymlroot@Prom ~# egrep -v '^$|^[[:space:]]*#' /etc/prometheus/prometheus.ymlglobal: # 每 15 秒从所有配置的目标(targets)拉取(抓取)一次监控指标数据(控制采集数据的频率) scrape_interval: 15s # 每 5 秒评估一次告警规则和记录规则(控制检查规则的频率) evaluation_interval: 5s ✅alerting: alertmanagers: - static_configs: - targets: # 告警目标地址,告诉 Prometheus "告警发到哪里" - 10.0.0.12:9093 ✅
rule_files: # 告警规则文件,告诉 Prometheus "告警规则从哪里读" - "/etc/prometheus/kpyun-linux-rules.yml" ✅ - "/etc/prometheus/kpyun-linux-rules-inhibit.yml" ✅scrape_configs: - job_name: "prometheus" scheme: 'https' tls_config: insecure_skip_verify: true basic_auth: username: kpyun password: kpyun123 static_configs: - targets: ["10.0.0.10:9090"] labels: app: "prometheus"`注释其余配置`
# 追加三个被监控 job(分别对应 dba/k8s/bigdata 分组)root@Prom ~# cat >> /etc/prometheus/prometheus.yml << 'EOF' - job_name: "kpyun_dba_exporter" static_configs: - targets: ["10.0.0.2:9100"] - job_name: "kpyun_k8s_exporter" static_configs: - targets: ["10.0.0.11:9100"] - job_name: "kpyun_bigdata_exporter" static_configs: - targets: ["10.0.0.12:9100"]EOF告警规则
1)基础规则: up==0 实例宕机即触发root@Prom ~# cat > /etc/prometheus/kpyun-linux-rules.yml <<'EOF'groups:- name: kpyun-linux-rules-alert # 规则组名, 一个文件可含多个 group rules: - alert: kpyun-dba_exporter-alert # 告警名(唯一标识, Alertmanager 用它分组) expr: up{job="kpyun_dba_exporter"} == 0 # 触发条件: 该 job 实例 up=0(宕机) for: 1s # 条件需持续为真 1s,告警才从 pending 变 firing(for 决定真正 firing 延迟) labels: # 附加标签, 供路由/分组/抑制匹配 school: kpyun apps: dba annotations: # 告警内容(支持 Go 模板变量) summary: "{{ $labels.instance }} 数据库实例已停止运行!" # $labels.instance 取实例名 value: "{{ $value }}" # $value 取触发时表达式的值(此处恒为 0) - alert: kpyun-k8s_exporter-alert expr: up{job="kpyun_k8s_exporter"} == 0 for: 1s labels: school: kpyun apps: k8s annotations: summary: "{{ $labels.instance }} K8S服务器已停止运行!" value: "{{ $value }}" - alert: kpyun-bigdata_exporter-alert expr: up{job="kpyun_bigdata_exporter"} == 0 for: 1s labels: school: kpyun apps: bigdata annotations: summary: "{{ $labels.instance }} 大数据服务器已停止运行!" value: "{{ $value }}"EOF
2)抑制配套规则: 打上 severity/critical|warning + dc 标签root@Prom ~# cat > /etc/prometheus/kpyun-linux-rules-inhibit.yml <<'EOF'groups:- name: kpyun-linux-rules-alert-inhibit # 抑制配套规则组(结构与第一套完全一致) rules: # 相比第一套: 仅额外打了 severity / dc 两个标签, 供 Alertmanager inhibit_rules 按级别+机房匹配 - alert: kpyun-dba_exporter-alert-xixi expr: up{job="kpyun_dba_exporter"} == 0 for: 3s # 比第一套稍长, 让 critical 与 warning 时间重叠, 抑制才生效 labels: apps: dba severity: critical # 严重级别: 关键(抑制的"源") dc: beijing # 机房标签: 北京 annotations: summary: "{{ $labels.instance }} 数据库实例已停止运行超过 3s!" value: "{{ $value }}" - alert: kpyun-k8s_exporter-alert-haha expr: up{job="kpyun_k8s_exporter"} == 0 for: 3s labels: apps: k8s severity: warning # 警告(被抑制的"目标") dc: beijing # 与上方 critical 同 dc → 满足 inhibit 的 equal:[dc] annotations: summary: "{{ $labels.instance }} K8S服务器已停止运行超过 3s!" value: "{{ $value }}" - alert: kpyun-bigdata_exporter-alert-hehe expr: up{job="kpyun_bigdata_exporter"} == 0 for: 5s labels: apps: bigdata severity: warning dc: shenzhen # 不同 dc → 不被上面 beijing 的 critical 抑制 annotations: summary: "{{ $labels.instance }} 大数据服务器已停止运行超过 5s!" value: "{{ $value }}"EOF
3)语法检查root@Prom ~# promtool check config /etc/prometheus/prometheus.ymlChecking prometheus.yml SUCCESS: 2 rule files found SUCCESS: prometheus.yml is valid prometheus config file syntaxChecking /etc/prometheus/kpyun-linux-rules.yml SUCCESS: 3 rules foundChecking /etc/prometheus/kpyun-linux-rules-inhibit.yml SUCCESS: 3 rules found✅️ 2 个规则文件,共 6 条规则
4)热加载(Prometheus 已带 --web.enable-lifecycle)root@Prom ~# curl -s -k -u kpyun:kpyun123 -X POST https://10.0.0.10:9090/-/reload'Prometheus 已把警报发给 Alertmanager'
https://10.0.0.10:9090/alerts
触发告警验证
1)停掉 node1(dba 组件) 的 node_exporter, 模拟实例宕机[root@node1 ~]# systemctl stop node-exporter.service
2)Prometheus 活跃告警root@Prom ~# curl -s -k -u kpyun:kpyun123 "https://10.0.0.10:9090/api/v1/alerts" \ | python3 -c "import sys,json;d=json.load(sys.stdin);print('活跃告警数:',len(d['data']['alerts']))"活跃告警数: 2# 仅 dba 的 2 条规则 firing; Alertmanager 侧再按接收组拆成 4 个通知组(见下图)💡 停一台 dba 为什么会收到 4 封邮件? node1 扮演 dba 组件, 宕机时两套规则同时命中:
- 基础规则
kpyun-dba_exporter-alert(第一套) - 抑制配套规则
kpyun-dba_exporter-alert-xixi(第二套,severity: critical、dc: beijing)
每条告警在 Alertmanager 又被发往两个接收组:
kpyun_dba组(job 正则命中,continue: true继续向下)kpyun_system组(.*兜底命中,continue: true继续)
而这两个组的 to 都填的是同一个邮箱 → 2 条规则 × 2 个接收组 = 4 封邮件



💡 恢复后 Alertmanager 还会补发一封恢复通知(对应 send_resolved: true), 形成 firing → resolved 完整闭环

自定义告警模板
模板原理:Alertmanager 默认只发纯文本邮件, 用 Go 模板语法可渲染富邮件(卡片/配色/emoji 等), 具体挂接方式见下方 alertmanager.yml 配置
1)写模板[root@node3 ~]# mkdir -p /etc/alertmanager/tmpl[root@node3 ~]# cat > /etc/alertmanager/tmpl/kpyun.html <<'EOF'{{ define "kpyun-email" }}<!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>{{ if eq .Status "firing" }}🚨 告警触发{{ else }}✅ 告警恢复{{ end }}</title> <style> @font-face { font-family: "EmojiFont"; src: local("Apple Color Emoji"), local("Segoe UI Emoji"), local("Noto Color Emoji"); } body { font-family: 'Segoe UI', system-ui, sans-serif, "EmojiFont"; line-height: 1.6; color: #333; max-width: 800px; margin: 20px auto; padding: 0 20px; background-color: #f9f9f9; } .header { text-align: center; padding: 24px; border-radius: 12px; margin-bottom: 24px; background: {{ if eq .Status "firing" }}#fff0f0{{ else }}#f0fff4{{ end }}; border: 2px solid {{ if eq .Status "firing" }}#FF5252{{ else }}#4CAF50{{ end }}; } .header h1 { margin: 0; font-size: 22px; } .alert-card { border-radius: 8px; padding: 20px; margin-bottom: 20px; background: {{ if eq .Status "firing" }}linear-gradient(135deg, #FFF6F6 0%, #FFEBEB 100%){{ else }}linear-gradient(135deg, #F6FFF6 0%, #EBFFEB 100%){{ end }}; border-left: 5px solid {{ if eq .Status "firing" }}#FF5252{{ else }}#4CAF50{{ end }}; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .alert-field { margin-bottom: 8px; display: flex; } .field-label { font-weight: bold; min-width: 95px; color: #555; } .field-value { flex: 1; } .severity-critical { color: #FF5252; font-weight: bold; } .severity-warning { color: #FFBB33; font-weight: bold; } .divider { height: 1px; background: #eee; margin: 12px 0; } .footer { text-align: center; color: #888; font-size: 13px; margin-top: 20px; } </style></head><body> <div class="header"> <h1>{{ if eq .Status "firing" }}<span class="emoji">🚨</span> 告警触发通知{{ else }}<span class="emoji">✅</span> 告警恢复通知{{ end }}</h1> </div>
{{ range .Alerts }} <div class="alert-card"> <div class="alert-field"><span class="field-label">📣 告警名称</span><span class="field-value">{{ .Labels.alertname }}</span></div> <div class="alert-field"><span class="field-label">⚠️ 告警级别</span><span class="field-value severity-{{ .Labels.severity }}">{{ if .Labels.severity }}{{ .Labels.severity }}{{ else }}无{{ end }}</span></div> <div class="alert-field"><span class="field-label">🕚 目标机器</span><span class="field-value">{{ .Labels.instance }}</span></div> <div class="alert-field"><span class="field-label">📝 告警摘要</span><span class="field-value">{{ .Annotations.summary }}</span></div> {{ if eq .Status "firing" }} <div class="alert-field"><span class="field-label">🕑 触发时间</span><span class="field-value">{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</span></div> {{ else }} <div class="alert-field"><span class="field-label">⏳ 持续时间</span><span class="field-value">{{ .StartsAt.Format "15:04:05" }} - {{ .EndsAt.Format "15:04:05" }} ({{ .EndsAt.Sub .StartsAt }})</span></div> <div class="alert-field"><span class="field-label">✅ 恢复时间</span><span class="field-value">{{ (.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</span></div> {{ end }} {{- if .Annotations.description }} <div class="divider"></div> <div class="alert-field"><span class="field-label">📑 详细描述</span><span class="field-value">{{ .Annotations.description }}</span></div> {{- end }} </div> {{ end }}
<div class="footer">Kpyun 自动化运维 · 本次共 {{ len .Alerts }} 条告警</div></body></html>{{ end }}EOF
2)alertmanager.yml 挂接模板(两处改动)[root@node3 ~]# vim /etc/alertmanager/alertmanager.yml# 改动一: 顶层新增 templates 指向模板文件templates: - '/etc/alertmanager/tmpl/kpyun.html'
# 改动二: receivers 的 kpyun_dba 下加 html 引用- name: 'kpyun_dba' email_configs: - to: 'jiuzhao996@qq.com' send_resolved: true html: '{{ template "kpyun-email" . }}' # 用自定义 HTML 模板渲染邮件 headers: { Subject: "[WARN] kpyun linux 报警邮件" }
3)校验+热重载[root@node3 ~]# amtool check-config /etc/alertmanager/alertmanager.yml - 1 inhibit rules - 3 receivers - 1 templates✅️ 模板已加载(1 templates), 发往 dba 组的邮件会用该 HTML 渲染# 校验通过只是第一步, 配置必须重新加载才生效# Alertmanager 的 /-/reload 默认开启, 无需 --web.enable-lifecycle, 该 flag 是 Prometheus 的参数[root@node3 ~]# curl -s -X POST http://localhost:9093/-/reload公网 IP 发送频率过快会被邮件服务商限流,报错 550 Connection frequency limited / 554 IP is rejected
- 生产建议用 163 邮箱或企业邮箱
- QQ 邮箱有发送次数限制,实测踩坑较多
- 解决: 换邮箱类型,或换网络出口 IP
集成钉钉插件实现告警

`部署钉钉插件(node2)`1)解包jiuzhao@Ubuntu ~$ scp /home/jiuzhao/下载/prometheus-webhook-dingtalk-2.1.0.linux-amd64.tar.gz node2:/server/pkgs/[root@node2 ~]# tar xf /server/pkgs/prometheus-webhook-dingtalk-2.1.0.linux-amd64.tar.gz -C /usr/local/bin prometheus-webhook-dingtalk-2.1.0.linux-amd64/prometheus-webhook-dingtalk --strip-components=1# 只需要解压这个二进制工具
2)配置(占位,换成你自己的机器人)[root@node2 ~]# mkdir -p /etc/dingtalk[root@node2 ~]# cat > /etc/dingtalk/config.yml <<'EOF'targets: kpyun-linux: # 对应的是dingding的webhook url: https://oapi.dingtalk.com/robot/send?access_token=f9c739ae3516301eea13f55a2bcafa8a9397c3d82ca69c6610209ed75c6b888d # 对应的是"加签"的值,复制过来即可 secret: "SEC97c0de0400bdc90469546381f381d38c1b35a612101dcfef4ab0a7204764d554"EOF
3)systemd + 启动[root@node2 ~]# cat > /usr/lib/systemd/system/prometheus-webhook-dingtalk.service <<'EOF'[Unit]Description=kpyun Prometheus Webhook DingtalkAfter=network.target
[Service]Type=simpleExecStart=/usr/local/bin/prometheus-webhook-dingtalk --web.listen-address=0.0.0.0:8060 --config.file=/etc/dingtalk/config.ymlRestart=on-failureRestartSec=5
[Install]WantedBy=multi-user.targetEOF[root@node2 ~]# systemctl daemon-reload && systemctl enable --now prometheus-webhook-dingtalk.service
4)验证[root@node2 ~]# ss -lnt | grep 8060LISTEN 0 4096 *:8060 *:*✅️ 钉钉插件已在 node2:8060 监听[root@node2 ~]# journalctl -u prometheus-webhook-dingtalk.service | tail -8 | egrep 'Started|urls'Aug 22 15:24:53 node2 systemd[1]: Started prometheus-webhook-dingtalk.service - kpyun Prometheus Webhook Dingtalk.Aug 22 15:24:53 node2 prometheus-webhook-dingtalk[2436]: ts=2026-08-22T07:24:53.842Z caller=main.go:113 component=configuration msg="Webhook urls for prometheus alertmanager" urls=http://0.0.0.0:8060/dingtalk/kpyun-linux/send`💡 就是上面这个地址!【可以直接复制】`
5)联合 Alertmanager(真正把告警推到钉钉)[root@node3 ~]# vim /etc/alertmanager/alertmanager.yml...... # 修改Alertmanager的配置文件`这段要放在 alertmanager.yml 的某个 receiver 下`- name: 'kpyun_system'# email_configs:# - to: 'jiuzhao996@qq.com'# send_resolved: true# html: '{{ template "kpyun-email" . }}' # 用自定义 HTML 模板渲染邮件# headers: { Subject: "[WARN] kpyun linux 报警邮件" } webhook_configs: # 指向的是Prometheus的钉钉插件发送端地址 - url: 'http://10.0.0.11:8060/dingtalk/kpyun-linux/send' # 空字典=用默认 HTTP 设置(不配代理/TLS/认证); 跨网或需鉴权时在此填写 http_config: {} # 单次 webhook 最多携带多少条告警, 0 = 不限制(全部带上) max_alerts: 0 # 告警恢复后也向钉钉发一条"已恢复"消息 send_resolved: true
6)校验+热重载[root@node3 ~]# amtool check-config /etc/alertmanager/alertmanager.ymlChecking '/etc/alertmanager/alertmanager.yml' ✅️ SUCCESS[root@node3 ~]# curl -s -X POST http://localhost:9093/-/reload
7)模拟故障[root@node1 ~]# systemctl stop node-exporter.service[root@node1 ~]# systemctl start node-exporter.service

告警静默(Silence)
静默一般用于系统维护期: 预期要做的操作(比如 50 台内核升级 8h), 这期间没必要告警
💡 创建静默有两种方式, 效果完全等价: ① Alertmanager WebUI 的 New Silence 表单; ② amtool silence add 命令行(API), 下面分别演示
WebUI 新建静默(New Silence)
在 Alertmanager UI(http://10.0.0.12:9093/#/silences/new)点 New Silence, 表单字段含义:
| 字段 | 说明 |
|---|---|
| Start / Duration / End | 静默生效起止时间, 均为 UTC; 想对应北京时间要 +8h |
| Matchers | 匹配条件, 格式 标签名="标签值"(如 alertname="kpyun-dba_exporter-alert"); 不填则匹配不到任何告警 |
| Creator | 创建人(必填), 留空会提示 Should not be empty |
| Comment | 静默原因(必填), 如 “kpyun 维护窗口” |
| Annotations | 可选键值对注释, 仅记录用, 不参与匹配 |
| Preview Alerts / Create / Reset | 预览命中的告警 / 创建 / 重置表单 |
WebUI 创建的本质就是填 Matchers + 时间 + Comment, 后端同样生成一条 silence, 和命令行方式等价

amtool 命令行创建(等价于上面的表单)
1)用 amtool 创建静默(匹配 dba 告警, 6 小时)[root@node3 ~]# amtool --alertmanager.url=http://localhost:9093 silence add alertname=kpyun-dba_exporter-alert --duration=6h --comment="kpyun 维护窗口-静默dba告警"304b7c9b-4e98-4f55-9234-6cc748e15a34
2)查看静默[root@node3 ~]# amtool --alertmanager.url=http://localhost:9093 silence queryID Matchers Ends At Created By Comment304b7c9b-... alertname="kpyun-dba_exporter-alert" ... root kpyun 维护窗口...
3)触发告警[root@node1 ~]# systemctl stop node-exporter.service
告警抑制(Inhibit)
抑制用于抑制符合条件的告警: 比如一个数据中心断电,4w 条告警没意义,只需把”数据中心断电”这条发出来,其余被抑制
1)规则已在 alertmanager.yml 的 inhibit_rules 中(同一 dc 下 critical 抑制 warning)[root@node1 ~]# systemctl stop node-exporter.service[root@node2 ~]# systemctl stop node-exporter.service
2)实测: 同时触发 dba(critical,dc=beijing) 与 k8s(warning,dc=beijing)[root@node3 ~]# /usr/local/bin/amtool --alertmanager.url=http://localhost:9093 alert query# 注意: kpyun-k8s_exporter-alert-haha(warning,dc=beijing) 不在 active 列表# 因为它被同 dc 的 critical(dba) 抑制了✅️ 抑制生效: warning 被 critical 压制,避免告警泛滥💡 对比: 基础版 kpyun-k8s_exporter-alert(没打 severity 标签)不受影响,仍在 active,说明抑制只作用于带 severity: warning 且同 dc 的告警

etcd 集群实战

- etcd 是分布式,高可用的键值存储,用 Raft 共识算法保证一致性,Go 编写
- 端口: 2379(客户端 http|https) / 2380(集群内部 peer)
- API v3 是主流(3.5+ 已移除 v2),
etcdctl默认即 v3- 3.7 起无需再设
ETCDCTL_API=3
- 3.7 起无需再设
- GitHub项目地址:https://github.com/etcd-io/etcd

- 官方文档地址:https://etcd.io/docs/
部署 etcd 集群
1)拷贝安装jiuzhao@Ubuntu 下载$ scp /home/jiuzhao/下载/etcd-v3.7.1-linux-amd64.tar.gz node1:/server/pkgs/
[root@node1 ~]# tar -xf /server/pkgs/etcd-v3.7.1-linux-amd64.tar.gz -C /usr/local/bin etcd-v3.7.1-linux-amd64/etcd{,utl,ctl} --strip-components=1[root@node1 ~]# ll /usr/local/bin/etcd*-rwxr-xr-x 1 1000 1000 24801442 Jul 24 03:32 /usr/local/bin/etcd-rwxr-xr-x 1 1000 1000 16789666 Jul 24 03:32 /usr/local/bin/etcdctl-rwxr-xr-x 1 1000 1000 16478370 Jul 24 03:32 /usr/local/bin/etcdutl`只有这三个工具对我们有用!`[root@node1 ~]# etcdctl versionetcdctl version: 3.7.1API version: 3.7
2)分发其他节点[root@node1 ~]# scp /usr/local/bin/etcd* 10.0.0.11:/usr/local/bin[root@node1 ~]# scp /usr/local/bin/etcd* 10.0.0.12:/usr/local/bin[root@node3 ~]# etcdctl versionetcdctl version: 3.7.1API version: 3.7📌 关键优化: 直接复用已有的 kpyun Root CA + Intermediate CA,只在 Prom 上用 Intermediate CA 新签一张 etcd server 证书,其 SAN(证书白名单) 覆盖三个节点主机名 + IP,即”哪些名字/IP 连我算合法”,三节点共用同一张证
3)在 Prom 的既有 CA 目录下签 etcd 证书root@Prom ~# cd /etc/prometheus/certsroot@Prom ~# openssl genrsa -out private/etcd.kpyun.com.key 4096root@Prom ~# openssl req -new -key private/etcd.kpyun.com.key -out etcd.kpyun.com.csr \ -subj "/C=CN/ST=Beijing/L=Beijing/O=kpyun/OU=Etcd/CN=etcd.kpyun.com"root@Prom ~# cat > etcd-server.ext <<'EOF'authorityKeyIdentifier=keyid,issuerbasicConstraints=CA:FALSEkeyUsage=critical,digitalSignature,keyEnciphermentextendedKeyUsage=serverAuth,clientAuthsubjectAltName=@alt_names[alt_names]DNS.1=node1DNS.2=node2DNS.3=node3IP.1=10.0.0.2IP.2=10.0.0.11IP.3=10.0.0.12IP.4=127.0.0.1EOFroot@Prom ~# openssl x509 -req -in etcd.kpyun.com.csr -CA certs/intermediate.crt \ -CAkey private/intermediate.key -CAcreateserial -out certs/etcd.kpyun.com.crt \ -days 365 -sha256 -extfile etcd-server.extroot@Prom ~# cat certs/rootCA.crt certs/intermediate.crt > certs/etcd-ca-bundle.pemroot@Prom ~# openssl verify -CAfile certs/rootCA.crt -untrusted certs/intermediate.crt certs/etcd.kpyun.com.crtcerts/etcd.kpyun.com.crt: OK✅️ Root CA → Intermediate CA → etcd server 证书 验证通过
4)分发三件套到三节点 /etc/etcd/certs 与 Prom(监控用)# 三件套 = 证书(etcd.kpyun.com.crt→etcd-server.pem) / 私钥(etcd.kpyun.com.key→etcd-server-key.pem) / CA 链(etcd-ca-bundle.pem→etcd-ca.pem)# /etc/etcd 与 /etc/etcd/certs 必须提前建# /var/lib/etcd 由 etcd 首次启动自动创建, 无需手动建(本例 etcd 以 root 运行, 有创建权限)# 前置(Prom 一次性): 装 sshpass 免交互输密码; 全机密码统一为 1root@Prom ~# apt-get install -y sshpass# sshpass -p '1' 自动填密码; -o StrictHostKeyChecking=accept-new 首次自动接受主机密钥并写入 known_hosts(跳过 yes, 重跑不再提示; 实验室环境专用, 生产改用 SSH key)root@Prom ~# for N in 10.0.0.2 10.0.0.11 10.0.0.12; do sshpass -p '1' ssh -o StrictHostKeyChecking=accept-new root@$N 'mkdir -p /etc/etcd/certs' sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /etc/prometheus/certs/certs/etcd.kpyun.com.crt root@$N:/etc/etcd/certs/etcd-server.pem sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /etc/prometheus/certs/private/etcd.kpyun.com.key root@$N:/etc/etcd/certs/etcd-server-key.pem sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /etc/prometheus/certs/certs/etcd-ca-bundle.pem root@$N:/etc/etcd/certs/etcd-ca.pemdone# Prom 自身也要一份(给 Prometheus 抓 etcd /metrics 走 mTLS)root@Prom ~# mkdir -p /etc/prometheus/certs/etcdroot@Prom ~# cp /etc/prometheus/certs/certs/etcd.kpyun.com.crt /etc/prometheus/certs/etcd/etcd-server.pemroot@Prom ~# cp /etc/prometheus/certs/private/etcd.kpyun.com.key /etc/prometheus/certs/etcd/etcd-server-key.pemroot@Prom ~# cp /etc/prometheus/certs/certs/etcd-ca-bundle.pem /etc/prometheus/certs/etcd/etcd-ca.pem✅️ 三节点 + Prom 各就位三件套(证书/私钥/CA)5)node1 的配置文件(其余节点改 name / IP 即可)[root@node1 ~]# cat > /etc/etcd/etcd.config.yml <<'EOF'name: 'node1'data-dir: /var/lib/etcdsnapshot-count: 5000heartbeat-interval: 100election-timeout: 1000quota-backend-bytes: 0listen-peer-urls: 'https://10.0.0.2:2380'listen-client-urls: 'https://10.0.0.2:2379,http://127.0.0.1:2379'max-snapshots: 3max-wals: 5initial-advertise-peer-urls: 'https://10.0.0.2:2380'advertise-client-urls: 'https://10.0.0.2:2379'initial-cluster: 'node1=https://10.0.0.2:2380,node2=https://10.0.0.11:2380,node3=https://10.0.0.12:2380'initial-cluster-token: 'etcd-kpyun-cluster'initial-cluster-state: 'new'strict-reconfig-check: falseenable-pprof: trueproxy: 'off'client-transport-security: cert-file: '/etc/etcd/certs/etcd-server.pem' key-file: '/etc/etcd/certs/etcd-server-key.pem' client-cert-auth: true trusted-ca-file: '/etc/etcd/certs/etcd-ca.pem' auto-tls: truepeer-transport-security: cert-file: '/etc/etcd/certs/etcd-server.pem' key-file: '/etc/etcd/certs/etcd-server-key.pem' peer-client-cert-auth: true trusted-ca-file: '/etc/etcd/certs/etcd-ca.pem' auto-tls: truedebug: falselog-outputs: [default]EOF# 三节点 mutual TLS: client-cert-auth + peer-client-cert-auth 都用同一张 etcd 证书======================================================================# 把 node1 配置分发到 node2 / node3: 用 sed 改 name 与本机 IP, 省去每台手敲# initial-cluster: 它列举【全部三节点】, 每个节点必须一字不差, 绝不能改, 所以要跳过该行# 只改本机相关的 4 处 URL(listen-peer / listen-client / initial-advertise-peer / advertise-client)# node1 也装 sshpass(它要主动 scp 给 node2/node3); 同样跳过 yes[root@node1 ~]# dnf install -y sshpass[root@node1 ~]# sed -e '/initial-cluster:/!s#https://10.0.0.2:#https://10.0.0.11:#g' \ -e "s/^name: 'node1'/name: 'node2'/" \ /etc/etcd/etcd.config.yml > /tmp/etcd.config.node2.yml[root@node1 ~]# echo -e '10.0.0.11 node2\n10.0.0.12 node3' >> /etc/hosts`hosts解析`[root@node1 ~]# sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /tmp/etcd.config.node2.yml root@node2:/etc/etcd/etcd.config.yml
[root@node1 ~]# sed -e '/initial-cluster:/!s#https://10.0.0.2:#https://10.0.0.12:#g' \ -e "s/^name: 'node1'/name: 'node3'/" \ /etc/etcd/etcd.config.yml > /tmp/etcd.config.node3.yml[root@node1 ~]# sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /tmp/etcd.config.node3.yml root@node3:/etc/etcd/etcd.config.yml✅️ node2 / node3 配置就位(name 与 IP 已对应替换, initial-cluster 三节点保持一致)6)写 unit(三节点相同)[root@node1 ~]# cat > /usr/lib/systemd/system/etcd.service <<'EOF'[Unit]Description=kpyun EtcdAfter=network.target
[Service]# simple = 进程起了就算活;notify = 服务真能干活了才算活Type=notifyExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.config.ymlRestart=on-failureRestartSec=10LimitNOFILE=65536
[Install]WantedBy=multi-user.targetEOF[root@node1 ~]# systemctl daemon-reload && systemctl enable --now etcd.service# node2 / node3 同样操作,然后三台一起启动[root@node3 ~]# systemctl is-active etcd.serviceactive
7)查看集群状态[root@node1 ~]# etcdctl --endpoints="https://10.0.0.2:2379,https://10.0.0.11:2379,https://10.0.0.12:2379" \ --cacert=/etc/etcd/certs/etcd-ca.pem --cert=/etc/etcd/certs/etcd-server.pem \ --key=/etc/etcd/certs/etcd-server-key.pem endpoint status --write-out=table# 实际输出列较多,下面只抽取关键列:✅️ 三节点 3.7.1 集群成立,node1(10.0.0.2) 为 Leader(RAFT TERM 2)| 端点地址 | 成员 ID | etcd 版本 | 是否为当前 Leader | Raft 任期(每次重新选主 +1) |
|---|---|---|---|---|
| https://10.0.0.2:2379 | 217112283068c8a | 3.7.1 | true ✅ | 2 |
| https://10.0.0.11:2379 | 787c4515ec48fd16 | 3.7.1 | false ❌ | 2 |
| https://10.0.0.12:2379 | 9c5a59fd7ae10a75 | 3.7.1 | false ❌ | 2 |
💡 以上是第 7 步建库时的快照,彼时 node1(10.0.0.2) 为 Leader(Raft Term=2)
到了下方「增删改查」章节的 endpoint status 输出,因中间跑过停 Leader 选主演练,Leader 已变为 node2(见该处字段说明)——属不同时间点的两次运行,并非文档写错
高可用验证
`停 Leader 看新 Leader 诞生`1)停掉 Leader(node1)[root@node1 ~]# systemctl stop etcd.service[root@node1 ~]# etcdctl --endpoints=... endpoint status --write-out=table# 停掉 1 台(Leader)后仍剩 2 台 ≥ 多数派(⌊3/2⌋+1=2),因此能选出新 Leader ✅'触发了一次新选举,RAFT TERM(Raft任期)+1 --> 递增到 3'
2)再把 node1 拉起[root@node1 ~]# systemctl start etcd.service[root@node1 ~]# etcdctl --endpoints=... endpoint status --write-out=table# 把 node1 重新拉起时它只是作为 follower 回归,没有再选举,所以 TERM 保持 3 不变'RAFT TERM 只有在"发生新选举"时才 +1'💡 Raft 的核心价值在于“高可用”与“自动容灾”
Leader 挂了,剩余节点重新选主,集群继续可用,对客户端几乎无感知(集群继续提供读写服务)
Raft 多数派机制:etcd 遵循「超过半数(quorum = ⌊N/2⌋+1)存活才能对外服务、才能选出新 Leader」 一旦存活节点数跌破半数,集群将丧失选举能力,陷入“脑裂防护”状态,此时读写操作将全部不可用,集群整体瘫痪
基础操作
1)添加别名(免敲长证书参数)[root@node1 ~]# cat >> /root/.bashrc <<'EOF'
# kpyun etcd 集群快捷别名alias etcdctl='etcdctl --endpoints="https://10.0.0.2:2379,https://10.0.0.11:2379,https://10.0.0.12:2379" --cacert=/etc/etcd/certs/etcd-ca.pem --cert=/etc/etcd/certs/etcd-server.pem --key=/etc/etcd/certs/etcd-server-key.pem'EOF[root@node1 ~]# source /root/.bashrc
2)查看集群成员列表[root@node1 ~]# etcdctl member listID 状态 名称 Peer URL(节点间通信) Client URL(客户端访问)217112283068c8a, started, node1, https://10.0.0.2:2380, https://10.0.0.2:2379, false787c4515ec48fd16, started, node2, https://10.0.0.11:2380, https://10.0.0.11:2379, false9c5a59fd7ae10a75, started, node3, https://10.0.0.12:2380, https://10.0.0.12:2379, false# 最后一列 `false` 是「是否为 learner 成员」(学习者节点)标记,不是是否 Leader!'三行都为 `false` 表示三个节点都是正式投票成员(非 learner)'# 想看谁是主节点,依靠 `member list` 是看不出来的
3)查看各节点健康状态和详细信息[root@node1 ~]# etcdctl endpoint statushttps://10.0.0.2:2379, 217112283068c8a, 3.7.1, 3.7.0, 20 kB, 16 kB, 20%, 2.1 GB, false, false, 3, 16, 16, , , falsehttps://10.0.0.11:2379, 787c4515ec48fd16, 3.7.1, 3.7.0, 20 kB, 16 kB, 20%, 2.1 GB, true, false, 3, 16, 16, , , falsehttps://10.0.0.12:2379, 9c5a59fd7ae10a75, 3.7.1, 3.7.0, 20 kB, 16 kB, 20%, 2.1 GB, false, false, 3, 16, 16, , , false
4)写入键值对(增 / 改)[root@node1 ~]# etcdctl put school kpyunOK[root@node1 ~]# etcdctl put /class linuxOK# "/" 只是普通字符,etcd 本身不分子目录,靠 --prefix 做"伪目录"范围查询# 改 = 同一个键再 put 一次即覆盖旧值(etcd 按 revision 记录每次写,旧值仍能在历史里读到)[root@node1 ~]# etcdctl put school kpyun-v2OK[root@node1 ~]# etcdctl get school# 默认 键key和值value 都会打印schoolkpyun-v2
5)查询键值(查)[root@node1 ~]# etcdctl get school --print-value-only--print-value-only # 只打印值kpyun-v2
[root@node1 ~]# etcdctl get "" --prefix --keys-only--prefix # 前缀匹配(空串=匹配全部键)--keys-only # 只打印键,不打印值(和 --print-value-only 正好相反)/class
school
6)查看某键当前 revision[root@node1 ~]# etcdctl get school -w json | jq{ "header": { "revision": 6, # 是当前集群总版本号 "raft_term": 3 # Raft 任期(每次重新选主 +1) }, "kvs": [ { "key": "c2Nob29s", # 解码= school "create_revision": 3, # 首次写入时的 revision(= 第一次 put school) "mod_revision": 5, # 最近一次修改的 revision(= 改成 kpyun-v2 那次) "version": 2, # 该键被改过几次(2 次 put) "value": "a3B5dW4tdjI=" # 解码= kpyun-v2 } ], "count": 1}`注意 key/value 是 base64 编码,需解码才可读`[root@node1 ~]# echo 'c2Nob29s' | base64 -d ;echoschool[root@node1 ~]# echo 'a3B5dW4tdjI=' | base64 -d ;echokpyun-v2`etcd 是 revision 模型,每次写都产生新 revision`[root@node1 ~]# etcdctl put school kpyun-v3OK# get -w json 只给首尾 revision(创建时和最近一次修改)
7)重放历史事件`etcdctl watch <key> --rev=<create_revision> -w json | jq`[root@node1 ~]# etcdctl watch school --rev=3 -w json | jq..."Events": [ { "kv": { "key": "c2Nob29s", "create_revision": 3, "mod_revision": 3, "version": 1, "value": "a3B5dW4=" } }, { "kv": { "key": "c2Nob29s", "create_revision": 3, "mod_revision": 5, "version": 2, "value": "a3B5dW4tdjI=" } }, { "kv": { "key": "c2Nob29s", "create_revision": 3, "mod_revision": 7, "version": 3, "value": "a3B5dW4tdjM=" } }... # 每条事件的 mod_revision 就是各版本的落点# key 被覆盖后旧值不立即消失,可用 --rev 回看历史[root@node1 ~]# etcdctl get school --rev=3schoolkpyun # 覆盖前:school 当时还是 kpyun[root@node1 ~]# etcdctl get school --rev=5schoolkpyun-v2[root@node1 ~]# etcdctl get school --rev=7schoolkpyun-v3
8)删除键值(删)[root@node1 ~]# etcdctl del /class1# 删除同样支持范围删除,但极危险!下面这条等于清空整个 keyspace,生产千万别手滑:# etcdctl del "" --prefix[root@node1 ~]# etcdctl get "" --prefixschoolkpyun-v3# ✅️ 增删改查与 Redis/Zookeeper 类似,都是键值对;# 区别:etcd 是 revision 模型(每次写都留历史,可 --rev 回看)集群数据备份和恢复
1)先写点数据便于验证[root@node1 ~]# etcdctl put /kpyun/backup/demo "restore-demo"OK
2)对单节点(10.0.0.2)做快照# 相当于备份数据`注意快照只能对 单节点 发起,不能一次指定多 endpoint`# 我们之前有别名alias,别名中指定的是多服务端点地址,\etcdctl 取消别名[root@node1 ~]# \etcdctl snapshot save /tmp/kpyun-etcd-`date +%F`.backupSnapshot saved at /tmp/kpyun-etcd-2026-08-23.backupServer version 3.7.0
3)查看快照信息`这里用的是 etcdutl`[root@node1 ~]# etcdutl snapshot --help用法: etcdutl snapshot [命令]
可用命令: restore 将 etcd 成员快照恢复到 etcd 目录 status 获取给定文件的后端快照状态
[root@node1 ~]# etcdutl snapshot status /tmp/kpyun-etcd-`date +%F`.backup -w table┌──────────┬──────────┬────────────┬────────────┬─────────┐│ HASH │ REVISION │ TOTAL KEYS │ TOTAL SIZE │ VERSION │├──────────┼──────────┼────────────┼────────────┼─────────┤│ 8c95d9f6 │ 11 │ 1 │ 20 kB │ 3.7.0 │└──────────┴──────────┴────────────┴────────────┴─────────┘✅️ 1 个 key,revision 11,来自 3.7.0 集群
4)拷贝备份数据[root@node1 ~]# sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /tmp/kpyun-etcd-`date +%F`.backup node2:/tmp[root@node1 ~]# sshpass -p '1' scp -o StrictHostKeyChecking=accept-new /tmp/kpyun-etcd-`date +%F`.backup node3:/tmp快照恢复必须三节点停服后一起重新 bootstrap(restart),不能先起两台再让第三台后加入,否则报错
✅ 正确姿势: 停三台 → 各自 etcdutl snapshot restore(带 --name 与完整 --initial-cluster)→ 三台一起 systemctl rstart
5)三台停服并清空旧 restore 目录[root@node1 ~]# systemctl stop etcd.service# node2/node3 同理[root@node1 ~]# rm -rf /var/lib/etcd# 之前的数据都在 /var/lib/etcd(etcd 首次启动自动创建)
6)每台用自己 --name 恢复(以 node1 为例)`重新指定了一个存储目录 /var/lib/etcd-restore`[root@node1 ~]# etcdutl snapshot restore /tmp/kpyun-etcd-`date +%F`.backup \ --data-dir=/var/lib/etcd-restore --name=node1 \ --initial-cluster='node1=https://10.0.0.2:2380,node2=https://10.0.0.11:2380,node3=https://10.0.0.12:2380' \ --initial-advertise-peer-urls=https://10.0.0.2:2380# node2/node3 同理,改 --name / --initial-advertise-peer-urls 即可successfully ✅ "path": "/tmp/kpyun-etcd-2026-08-23.backup", "wal-dir": "/var/lib/etcd-restore/member/wal", "data-dir": "/var/lib/etcd-restore", "snap-dir": "/var/lib/etcd-restore/member/snap",[root@node1 ~]# tree /var/lib/etcd-restore/var/lib/etcd-restore└── member ├── snap │ ├── 0000000000000001-0000000000000003.snap │ └── db └── wal └── 0000000000000000-0000000000000000.wal
7)把配置的 数据目录 指向 恢复目录,三台一起启动data-dir: /var/lib/etcd[root@node1 ~]# sed -i 's#/var/lib/etcd#/var/lib/etcd-restore#g' /etc/etcd/etcd.config.yml[root@node1 ~]# systemctl restart etcd.service# node2/node3 同时[root@node1 ~]# etcdctl endpoint status --write-out=table# 三节点重新 healthy,node1 再次成为 Leader ✅
8)验证数据恢复[root@node1 ~]# etcdctl get /kpyun/backup/demo/kpyun/backup/demorestore-demo✅️ 数据从快照完整恢复图形化管理 etcd 集群

1)Docker 虚拟机拉镜像(xuanyuan 镜像源,不用配代理)root@Docker ~# docker pull docker.xuanyuan.run/tzfun/etcd-workbench:1.1.4root@Docker ~# docker tag docker.xuanyuan.run/tzfun/etcd-workbench:1.1.4 etcd-workbench:1.1.4root@Docker ~# docker rmi docker.xuanyuan.run/tzfun/etcd-workbench:1.1.4root@Docker ~# docker images | grep etcdetcd-workbench:1.1.4 c58de0e1b96e 479MB 137MB
2)准备配置(账号 admin:kpyun123)并运行root@Docker ~# cat > /root/etcd-workbench.conf <<'EOF'[server]# 服务监听端口port = 8002# etcd 操作超时时间(毫秒)etcdExecuteTimeoutMillis = 3000# 数据存储目录dataDir = ./data
[auth]# 是否启用认证enable = true# 登录用户名和密码(格式:用户名:密码)user = admin:kpyun123
[log]# 日志级别:DEBUG / INFO / WARN / ERRORlevel = INFO# 日志文件存储目录file = ./logs# 日志文件基础名称(实际文件会追加时间戳或序号)fileName = etcd-workbench# 单个日志文件大小上限(MB),超过后自动滚动切割fileLimitSize = 100# 日志输出位置:std(终端)和 file(文件),逗号分隔可同时启用printers = std,fileEOF
root@Docker ~# docker run -d \ -v /root/etcd-workbench.conf:/usr/tzfun/etcd-workbench/etcd-workbench.conf \ -p 8002:8002 \ --name etcd-workbench \ etcd-workbench:1.1.4root@Docker ~# ss -ntl | grep 8002LISTEN 0 4096 0.0.0.0:8002 0.0.0.0:*✅️ workbench 已在 Docker(10.0.0.9):8002 监听
3)浏览访问http://10.0.0.9:8002/账号:admin密码:kpyun123# 登录后连接集群在 UI 里上传 etcd 的 CA 链 + 客户端证书(即 /etc/etcd/certs/ 下的三件套)即可连接 TLS 集群做图形化增删改查
jiuzhao@Ubuntu 下载$ scp -r node1:/etc/etcd/certs/ .jiuzhao@Ubuntu 下载$ tree ./certs/./certs/├── etcd-ca.pem├── etcd-server-key.pem└── etcd-server.pem



监控 etcd 服务实战
1)Prometheus 准备证书root@Prom ~# tree /etc/prometheus/certs/etcd//etc/prometheus/certs/etcd/├── etcd-ca.pem├── etcd-server-key.pem└── etcd-server.pem
2)prometheus.yml 追加 https 抓取任务root@Prom ~# cat >> /etc/prometheus/prometheus.yml <<'EOF'
- job_name: "kpyun-etcd-cluster" # 使用https协议 scheme: https # 配置https证书相关信息 tls_config: # 指定CA的证书链 ca_file: /etc/prometheus/certs/etcd/etcd-ca.pem # 指定etcd服务的公钥文件 cert_file: /etc/prometheus/certs/etcd/etcd-server.pem # 指定etcd服务的私钥文件 key_file: /etc/prometheus/certs/etcd/etcd-server-key.pem static_configs: - targets: - 10.0.0.2:2379 - 10.0.0.11:2379 - 10.0.0.12:2379EOF
3)校验并热加载root@Prom ~# promtool check config /etc/prometheus/prometheus.yml | tail -3 SUCCESS ✅root@Prom ~# curl -s -k -u kpyun:kpyun123 -X POST https://10.0.0.10:9090/-/reloadreload OK ✅
4)验证抓取# 直接 curl etcd 的 /metrics(走 mTLS)root@Prom ~# curl -s -k --cacert /etc/prometheus/certs/etcd/etcd-ca.pem \ --cert /etc/prometheus/certs/etcd/etcd-server.pem \ --key /etc/prometheus/certs/etcd/etcd-server-key.pem \ https://10.0.0.2:2379/metrics | wc -l1941✅️ etcd 暴露 1941 条指标root@Prom ~# curl -s -k -u kpyun:kpyun123 "https://localhost:9090/api/v1/targets" | jq -r ".data.activeTargets[] | select(.labels.job==\"kpyun-etcd-cluster\") | \"\(.labels.instance) | \(.health)\""10.0.0.12:2379 | up10.0.0.2:2379 | up10.0.0.11:2379 | up✅️ 三节点全部被 Prometheus 采集
# WebUIhttps://10.0.0.10:9090/targets?search=kpyun-etcd-cluster
5)Grafana 导入 etcd 仪表盘模板ID21473 / 10323
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!















