Prometheus 告警 && etcd 集群实战

7276 字
36 分钟
Prometheus 告警 && etcd 集群实战
Prometheus 告警 && etcd 集群实战

Prometheus 告警 && etcd 集群实战#

[TOC]


环境规划#

实际机器IP系统角色
Prom10.0.0.10UbuntuPrometheus 服务端 + 拷贝 etcd 证书做监控
node110.0.0.2Rocky 10node_exporter + etcd 成员
node210.0.0.11Rocky 10node_exporter + dingtalk 插件(8060) + etcd 成员
node310.0.0.12Rocky 10node_exporter + Alertmanager(9093) + etcd 成员
Docker10.0.0.9Ubuntuetcd-workbench 容器

Alertmanager 告警实战#

推送告警

配置

配置

Prometheus Server

及其他客户端

Alertmanager API

① 分组 Group

按标签将告警归类

② 去重 Deduplicate

组内合并重复告警

避免轰炸

③ 路由 Router

匹配接收规则

邮件

SMTP 协议

钉钉

Webhook

企业微信

Webhook

PagerDuty

原生集成

其他 Webhook

自定义 HTTP 回调

静默 Silence

抑制 Inhibit

推送告警

配置

配置

Prometheus Server

及其他客户端

Alertmanager API

① 分组 Group

按标签将告警归类

② 去重 Deduplicate

组内合并重复告警

避免轰炸

③ 路由 Router

匹配接收规则

邮件

SMTP 协议

钉钉

Webhook

企业微信

Webhook

PagerDuty

原生集成

其他 Webhook

自定义 HTTP 回调

静默 Silence

抑制 Inhibit

Tip

Alertmanager 接收由 Prometheus Server 等客户端发来的告警,负责对告警进行分组(Group)与去重(Deduplicate),然后通过路由(Router)将告警分发到正确的接收器(Receiver),如邮件、钉钉、企业微信、PagerDuty 等(其中钉钉/企业微信/飞书通常通过 Webhook 方式对接)

此外,Alertmanager 还支持告警的**静默(Silence)与抑制(Inhibit)**功能 静默:在维护期间临时屏蔽特定告警(通过配置规则) 抑制:当某严重告警触发时,屏蔽其衍生告警(减少噪音)

  • GitHub项目地址:https://github.com/prometheus/alertmanager

环境部署及子路由配置#

Terminal window
1)宿主机校验包完整性
jiuzhao@Ubuntu ~$ tar tf /home/jiuzhao/下载/alertmanager-0.34.0.linux-amd64.tar.gz
alertmanager-0.34.0.linux-amd64/
alertmanager-0.34.0.linux-amd64/alertmanager
alertmanager-0.34.0.linux-amd64/amtool
alertmanager-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 -1
alertmanager,version 0.34.0
'二进制就位,配置文件接下来手写'
Tip

💡 配置放 /etc/alertmanager/,数据放 /var/lib/alertmanager/

Terminal window
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 拿去压制深圳机房的 warning
inhibit_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.yml
Checking '/etc/alertmanager/alertmanager.yml' SUCCESS
Found:
- 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 Alertmanager
After=network.target
[Service]
Type=simple
ExecStart=/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:9093
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
6)启动并验证
[root@node3 ~]# systemctl daemon-reload && systemctl enable --now alertmanager.service
[root@node3 ~]# systemctl is-active alertmanager.service
active
[root@node3 ~]# ss -lnt | grep 9093
LISTEN 0 4096 *:9093 *:*
✅️ Alertmanager 已在 node3:9093 监听
http://10.0.0.12:9093/#/status

Alertmanager 的 9093 和 9094 端口分工明确:9093 负责对外提供服务,9094 负责内部集群通信

端口用途核心功能
9093Web UI 和 API 端口供你通过浏览器访问 Alertmanager 的界面,或供 Prometheus 等组件通过 API 推送告警
9094集群通信端口 (Cluster Port)当部署多个 Alertmanager 实例以实现高可用时,它们通过这个端口互相通信,同步告警通知和静默状态

Alertmanager WebUI 状态页
Alertmanager WebUI 状态页

Prometheus 集成 Alertmanager 实现告警#

Prometheus调用Alertmanager图解
Prometheus调用Alertmanager图解

Terminal window
`修改 Prometheus 配置(路由到 Alertmanager)`
1)Prom 上开启 alerting + rule_files
root@Prom ~# vim /etc/prometheus/prometheus.yml
root@Prom ~# egrep -v '^$|^[[:space:]]*#' /etc/prometheus/prometheus.yml
global:
# 每 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

告警规则#

Terminal window
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.yml
Checking prometheus.yml
SUCCESS: 2 rule files found
SUCCESS: prometheus.yml is valid prometheus config file syntax
Checking /etc/prometheus/kpyun-linux-rules.yml
SUCCESS: 3 rules found
Checking /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

触发告警验证#

Terminal window
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 个通知组(见下图)
Tip

💡 停一台 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 封邮件

Prometheus Alerts 页面
Prometheus Alerts 页面

Alertmanager WebUI Alerts 页面(4 个分组: dba/system 各出现 2 次)
Alertmanager WebUI Alerts 页面(4 个分组: dba/system 各出现 2 次)

告警通知邮件
告警通知邮件

Tip

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

恢复通知邮件(重新启动 node1 的 node_exporter 后触发)
恢复通知邮件(重新启动 node1 的 node_exporter 后触发)

自定义告警模板#

Tip

模板原理:Alertmanager 默认只发纯文本邮件, 用 Go 模板语法可渲染富邮件(卡片/配色/emoji 等), 具体挂接方式见下方 alertmanager.yml 配置

Terminal window
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" }}&#128680; 告警触发{{ else }}&#9989; 告警恢复{{ 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">&#128680;</span> 告警触发通知{{ else }}<span class="emoji">&#9989;</span> 告警恢复通知{{ end }}</h1>
</div>
{{ range .Alerts }}
<div class="alert-card">
<div class="alert-field"><span class="field-label">&#128227; 告警名称</span><span class="field-value">{{ .Labels.alertname }}</span></div>
<div class="alert-field"><span class="field-label">&#9888;&#65039; 告警级别</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">&#128346; 目标机器</span><span class="field-value">{{ .Labels.instance }}</span></div>
<div class="alert-field"><span class="field-label">&#128221; 告警摘要</span><span class="field-value">{{ .Annotations.summary }}</span></div>
{{ if eq .Status "firing" }}
<div class="alert-field"><span class="field-label">&#128337; 触发时间</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">&#9203; 持续时间</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">&#9989; 恢复时间</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">&#128209; 详细描述</span><span class="field-value">{{ .Annotations.description }}</span></div>
{{- end }}
</div>
{{ end }}
<div class="footer">Kpyun 自动化运维 &middot; 本次共 {{ 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
Warning

公网 IP 发送频率过快会被邮件服务商限流,报错 550 Connection frequency limited / 554 IP is rejected

  • 生产建议用 163 邮箱或企业邮箱
  • QQ 邮箱有发送次数限制,实测踩坑较多
  • 解决: 换邮箱类型,或换网络出口 IP

集成钉钉插件实现告警#

Terminal window
`部署钉钉插件(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

钉钉机器人
钉钉机器人

Terminal window
3)systemd + 启动
[root@node2 ~]# cat > /usr/lib/systemd/system/prometheus-webhook-dingtalk.service <<'EOF'
[Unit]
Description=kpyun Prometheus Webhook Dingtalk
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/prometheus-webhook-dingtalk --web.listen-address=0.0.0.0:8060 --config.file=/etc/dingtalk/config.yml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
[root@node2 ~]# systemctl daemon-reload && systemctl enable --now prometheus-webhook-dingtalk.service
4)验证
[root@node2 ~]# ss -lnt | grep 8060
LISTEN 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.yml
Checking '/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)#

Note

静默一般用于系统维护期: 预期要做的操作(比如 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预览命中的告警 / 创建 / 重置表单
Note

WebUI 创建的本质就是填 Matchers + 时间 + Comment, 后端同样生成一条 silence, 和命令行方式等价

New Silence)
New Silence)


amtool 命令行创建(等价于上面的表单)

Terminal window
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 query
ID Matchers Ends At Created By Comment
304b7c9b-... alertname="kpyun-dba_exporter-alert" ... root kpyun 维护窗口...
3)触发告警
[root@node1 ~]# systemctl stop node-exporter.service

告警抑制(Inhibit)#

Note

抑制用于抑制符合条件的告警: 比如一个数据中心断电,4w 条告警没意义,只需把”数据中心断电”这条发出来,其余被抑制

Terminal window
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 压制,避免告警泛滥
Tip

💡 对比: 基础版 kpyun-k8s_exporter-alert(没打 severity 标签)不受影响,仍在 active,说明抑制只作用于带 severity: warning 且同 dc 的告警


etcd 集群实战#

etcd reliability is important
etcd reliability is important

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

部署 etcd 集群#

Terminal window
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 version
etcdctl version: 3.7.1
API 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 version
etcdctl version: 3.7.1
API version: 3.7
Important

📌 关键优化: 直接复用已有的 kpyun Root CA + Intermediate CA,只在 Prom 上用 Intermediate CA 新签一张 etcd server 证书,其 SAN(证书白名单) 覆盖三个节点主机名 + IP,即”哪些名字/IP 连我算合法”,三节点共用同一张证

Terminal window
3)在 Prom 的既有 CA 目录下签 etcd 证书
root@Prom ~# cd /etc/prometheus/certs
root@Prom ~# openssl genrsa -out private/etcd.kpyun.com.key 4096
root@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,issuer
basicConstraints=CA:FALSE
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
[alt_names]
DNS.1=node1
DNS.2=node2
DNS.3=node3
IP.1=10.0.0.2
IP.2=10.0.0.11
IP.3=10.0.0.12
IP.4=127.0.0.1
EOF
root@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.ext
root@Prom ~# cat certs/rootCA.crt certs/intermediate.crt > certs/etcd-ca-bundle.pem
root@Prom ~# openssl verify -CAfile certs/rootCA.crt -untrusted certs/intermediate.crt certs/etcd.kpyun.com.crt
certs/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 免交互输密码; 全机密码统一为 1
root@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.pem
done
# Prom 自身也要一份(给 Prometheus 抓 etcd /metrics 走 mTLS)
root@Prom ~# mkdir -p /etc/prometheus/certs/etcd
root@Prom ~# cp /etc/prometheus/certs/certs/etcd.kpyun.com.crt /etc/prometheus/certs/etcd/etcd-server.pem
root@Prom ~# cp /etc/prometheus/certs/private/etcd.kpyun.com.key /etc/prometheus/certs/etcd/etcd-server-key.pem
root@Prom ~# cp /etc/prometheus/certs/certs/etcd-ca-bundle.pem /etc/prometheus/certs/etcd/etcd-ca.pem
✅️ 三节点 + Prom 各就位三件套(证书/私钥/CA)
Terminal window
5)node1 的配置文件(其余节点改 name / IP 即可)
[root@node1 ~]# cat > /etc/etcd/etcd.config.yml <<'EOF'
name: 'node1'
data-dir: /var/lib/etcd
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-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: 3
max-wals: 5
initial-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: false
enable-pprof: true
proxy: '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: true
peer-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: true
debug: false
log-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 三节点保持一致)
Terminal window
6)写 unit(三节点相同)
[root@node1 ~]# cat > /usr/lib/systemd/system/etcd.service <<'EOF'
[Unit]
Description=kpyun Etcd
After=network.target
[Service]
# simple = 进程起了就算活;notify = 服务真能干活了才算活
Type=notify
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.config.yml
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
[root@node1 ~]# systemctl daemon-reload && systemctl enable --now etcd.service
# node2 / node3 同样操作,然后三台一起启动
[root@node3 ~]# systemctl is-active etcd.service
active
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)
端点地址成员 IDetcd 版本是否为当前 LeaderRaft 任期(每次重新选主 +1)
https://10.0.0.2:2379217112283068c8a3.7.1true ✅2
https://10.0.0.11:2379787c4515ec48fd163.7.1false ❌2
https://10.0.0.12:23799c5a59fd7ae10a753.7.1false ❌2
Note

💡 以上是第 7 步建库时的快照,彼时 node1(10.0.0.2) 为 Leader(Raft Term=2) 到了下方「增删改查」章节的 endpoint status 输出,因中间跑过停 Leader 选主演练,Leader 已变为 node2(见该处字段说明)——属不同时间点的两次运行,并非文档写错

高可用验证#

Terminal window
`停 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'
Tip

💡 Raft 的核心价值在于“高可用”与“自动容灾”

Leader 挂了,剩余节点重新选主,集群继续可用,对客户端几乎无感知(集群继续提供读写服务)

Raft 多数派机制:etcd 遵循「超过半数(quorum = ⌊N/2⌋+1)存活才能对外服务、才能选出新 Leader」 一旦存活节点数跌破半数,集群将丧失选举能力,陷入“脑裂防护”状态,此时读写操作将全部不可用,集群整体瘫痪

基础操作#

Terminal window
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 list
ID 状态 名称 Peer URL(节点间通信) Client URL(客户端访问)
217112283068c8a, started, node1, https://10.0.0.2:2380, https://10.0.0.2:2379, false
787c4515ec48fd16, started, node2, https://10.0.0.11:2380, https://10.0.0.11:2379, false
9c5a59fd7ae10a75, 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 status
https://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, , , false
https://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, , , false
https://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 kpyun
OK
[root@node1 ~]# etcdctl put /class linux
OK
# "/" 只是普通字符,etcd 本身不分子目录,靠 --prefix 做"伪目录"范围查询
# 改 = 同一个键再 put 一次即覆盖旧值(etcd 按 revision 记录每次写,旧值仍能在历史里读到)
[root@node1 ~]# etcdctl put school kpyun-v2
OK
[root@node1 ~]# etcdctl get school
# 默认 键key和值value 都会打印
school
kpyun-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 ;echo
school
[root@node1 ~]# echo 'a3B5dW4tdjI=' | base64 -d ;echo
kpyun-v2
`etcd 是 revision 模型,每次写都产生新 revision`
[root@node1 ~]# etcdctl put school kpyun-v3
OK
# 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=3
school
kpyun # 覆盖前:school 当时还是 kpyun
[root@node1 ~]# etcdctl get school --rev=5
school
kpyun-v2
[root@node1 ~]# etcdctl get school --rev=7
school
kpyun-v3
8)删除键值(删)
[root@node1 ~]# etcdctl del /class
1
# 删除同样支持范围删除,但极危险!下面这条等于清空整个 keyspace,生产千万别手滑:
# etcdctl del "" --prefix
[root@node1 ~]# etcdctl get "" --prefix
school
kpyun-v3
# ✅️ 增删改查与 Redis/Zookeeper 类似,都是键值对;
# 区别:etcd 是 revision 模型(每次写都留历史,可 --rev 回看)

集群数据备份和恢复#

Terminal window
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`.backup
Snapshot saved at /tmp/kpyun-etcd-2026-08-23.backup
Server 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
Caution

快照恢复必须三节点停服后一起重新 bootstrap(restart),不能先起两台再让第三台后加入,否则报错

✅ 正确姿势: 停三台 → 各自 etcdutl snapshot restore(带 --name 与完整 --initial-cluster)→ 三台一起 systemctl rstart

Terminal window
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/demo
restore-demo
✅️ 数据从快照完整恢复

图形化管理 etcd 集群#

image-20251121172554551
image-20251121172554551

Terminal window
1)Docker 虚拟机拉镜像(xuanyuan 镜像源,不用配代理)
root@Docker ~# docker pull docker.xuanyuan.run/tzfun/etcd-workbench:1.1.4
root@Docker ~# docker tag docker.xuanyuan.run/tzfun/etcd-workbench:1.1.4 etcd-workbench:1.1.4
root@Docker ~# docker rmi docker.xuanyuan.run/tzfun/etcd-workbench:1.1.4
root@Docker ~# docker images | grep etcd
etcd-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 / ERROR
level = INFO
# 日志文件存储目录
file = ./logs
# 日志文件基础名称(实际文件会追加时间戳或序号)
fileName = etcd-workbench
# 单个日志文件大小上限(MB),超过后自动滚动切割
fileLimitSize = 100
# 日志输出位置:std(终端)和 file(文件),逗号分隔可同时启用
printers = std,file
EOF
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.4
root@Docker ~# ss -ntl | grep 8002
LISTEN 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
# 登录后连接集群
Tip

在 UI 里上传 etcd 的 CA 链 + 客户端证书(即 /etc/etcd/certs/ 下的三件套)即可连接 TLS 集群做图形化增删改查

Terminal window
jiuzhao@Ubuntu 下载$ scp -r node1:/etc/etcd/certs/ .
jiuzhao@Ubuntu 下载$ tree ./certs/
./certs/
├── etcd-ca.pem
├── etcd-server-key.pem
└── etcd-server.pem

etcd-workbench WebUI
etcd-workbench WebUI

监控 etcd 服务实战#

Terminal window
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:2379
EOF
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/-/reload
reload 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 -l
1941
✅️ 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 | up
10.0.0.2:2379 | up
10.0.0.11:2379 | up
✅️ 三节点全部被 Prometheus 采集
# WebUI
https://10.0.0.10:9090/targets?search=kpyun-etcd-cluster

Terminal window
5)Grafana 导入 etcd 仪表盘模板ID
21473 / 10323

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

相关文章智能推荐
1
Prometheus存储实战 && HTTPS认证 && 黑盒监控
Prometheus监控VictoriaMetrics单机部署+remote_write远端存储,Prometheus启用HTTPS+Basic Auth认证,node-exporter黑白名单,标签管理(relabel/metric_relabel),blackbox-exporter HTTP/ICMP/TCP黑盒探测,Grafana存储迁移到MySQL
2
Prometheus监控 & Grafana可视化
Prometheus监控二进制部署 Prometheus 3.13.2(mv 方案 + systemd 托管),编写 install-prometheus-server.sh / install-node-exporter.sh 一键安装卸载脚本,node-exporter 监控 Linux 主机,PromQL 四大数据类型与函数实战(stress 压测 CPU),Grafana 可视化展示,监控 ZK/Kafka/ES/Docker 主流中间件
3
Prometheus监控中间件 & Grafana进阶
Prometheus监控使用各中间件 exporter(mysqld/mongodb/redis/nginx-vts/tomcat)监控 MySQL、MongoDB、Redis、Nginx、Tomcat,Grafana 导入模板 ID(14057/17320/16504/11835/2949/9785),进阶掌握 Grafana 插件安装、自定义 Dashboard、变量、表格制作、备份与恢复
4
Prometheus自定义监控 && 服务发现 && 联邦模式
Prometheus监控部署 pushgateway 实现短期任务/自定义指标的推送式监控(API 推送删除、honor_labels、TCP 12 状态、丢包率脚本),用 Go + client_golang 开发自定义 exporter(静态编译零依赖),落地 file_sd 与 consul 服务发现(consul 2.0.3 集群 + consul_exporter),最后用 3 台 Prometheus server 搭建联邦模式实现分布式采集汇总
5
Zabbix监控开篇
Web服务Zabbix监控系统入门,涵盖Server/Agent部署、自定义监控项、值映射与主动被动模式
Profile Image of the Author
久棹
不是先学好了再干,而是先干起来再学习,干中学!
分类
站点统计
文章
115
分类
15
标签
272
总字数
306,562
运行时长
0 天
最后活动
0 天前
文章目录