Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • OpenClaw

  • Hermes-Agent

    • Hermes Agent 概述
    • Hermes Agent 实战 01|架构总览:用一个 Agent 管一整个机房
    • Hermes Agent 实战 02|多 Profile 与超管模型:一个 Agent 安全地管十几台机器
    • Hermes Agent 实战 03|Gateway 运维:systemd、裸进程,和一个 Telegram token 撞车
    • Hermes Agent 实战 04|模型路由实战:config 全解、thinking 注入与 401/503 源码级根因
    • Hermes Agent 实战 05|技能工程:写、去重、pin,与每周自我审计
    • Hermes Agent 实战 06|让 Agent 自己上班:cron 驱动的无人值守巡检
    • Hermes Agent 实战 07|数据库实战:可直接抄走的 SQL Server 巡检脚本
    • Hermes Agent 实战 08|量化交易助手:持仓盈亏、网格减仓,与「没开单」的真相
    • Hermes Agent 实战 09|接入 OpenWebUI:把每个 Profile 暴露成一个「模型」
    • Hermes Agent 实战 10|升级不翻车,与给上游提 PR:一个被冲掉三次的修复
    • Hermes Agent 实战 11|踩坑合集:当「手动 rm」从来不是真正的修复
    • Hermes Agent 实战 12|工具链外延:用 AI 运维 AI,与这个系列的诞生
    • Hermes Agent 实战 13|旗舰篇:让 Agent 从零部署并灾难恢复一个 7 节点生产集群
      • §1 它管的是什么规模
        • 1.1 物理拓扑(7 台裸金属,同构)
        • 1.2 组件版本
        • 1.3 MinIO 上的 8 个 bucket
      • §2 SOUL:超级管理员的人格
        • 2.1 五条核心原则(原文照搬)
        • 2.2 工作节奏
      • §3 机构记忆:一个 Profile 怎么承载整个集群
        • 3.1 完整的 Profile 目录(13 项)
        • 3.2 24 类 skill 全景
        • 3.3 devops 类下 10 个 skill 一览(每个对应一类操作)
        • 3.4 AGENTS.md 与 RESTORE.md 的分工
      • §4 从零部署:7 段关键配置(几乎都来自一份真实备份)
        • 4.1 CephCluster CR(节选,实际从生产集群 dump 出来)
        • 4.2 CephBlockPool CR(2 pool:.mgr + replicapool)
        • 4.3 StorageClass ceph-rbd(默认)
        • 4.4 kubeasz 部署的两个必修补丁
        • 4.5 containerd 镜像代理(/etc/containerd/certs.d/docker.io/hosts.toml)
        • 4.6 TidbCluster CR(节选:tidb-jpg-core)
        • 4.7 GreatSQL Service + 快捷连接脚本
      • §5 17 条 CRITICAL PITFALLS(合并 RESTORE.md §12 + AGENTS.md §4)
        • 5.1 节点层(2 条)
        • 5.2 容器运行时(3 条)
        • 5.3 Ceph 层(5 条)
        • 5.4 数据库层(3 条)
        • 5.5 Ingress / 镜像层(3 条)
        • 5.6 共性规律
      • §6 Harbor over MinIO:几个致命细节
        • 6.1 Ingress 注解(websecure 必加,见 §5 #14)
        • 6.2 后端 MinIO S3 配置
        • 6.3 架构
      • §7 不只是恢复:日常自动化(cron + 镜像同步)
        • 7.1 cron/jobs.json 完整字段
      • §8 灾难恢复验证:16 项 checklist(全部要绿)
      • §9 NodePort 全景(集群对外暴露的服务)
      • §10 备份档案本身(归档的归档)
      • §11 这篇想说的
        • 11.1 给读这篇的你
        • 11.2 安全提醒
      • §12 Agent 元数据块(供 AI agent 自动解析)
  • Claude-Code

  • AI-Agent
  • Hermes-Agent
Carry の Blog
2026-06-22
目录

Hermes Agent 实战 13|旗舰篇:让 Agent 从零部署并灾难恢复一个 7 节点生产集群原创

# 旗舰篇:让 Agent 从零部署并灾难恢复一个 7 节点生产集群

系列旗舰篇。前面 12 篇是「agent 帮我管机器」,这篇是「agent 自己建了一整个机房」——一个 Hermes Profile 从裸金属开始,部署并能完整灾难恢复一套 7 节点生产集群:K8s 1.34、Rook-Ceph(42 OSD / 449 TiB)、3×TiDB、3×GreatSQL、Harbor、Kuboard。它甚至给「下一个自己」写好了恢复手册。这篇几乎全是可照抄的配置和真踩过的坑。(所有 IP / 凭证 / 内网域名已脱敏。)


# §1 它管的是什么规模

# 1.1 物理拓扑(7 台裸金属,同构)

节点 角色 CPU 内存
master-01 ~ master-03 master + worker 72c × 3 502GB × 3
worker-01 ~ worker-04 node + worker 72c × 4 502GB × 4
合计 — 504 CPU 3.5 TB

每台机器 6 × 10.7 TB HDD,合计 ~449 TiB Ceph raw。所有节点通过 SSH 端口 58689 访问,bdom 用户配 passwordless sudo。

# 1.2 组件版本

层 组件 版本 / 规格
K8s kubeasz 部署 v1.34.3
运行时 containerd 2.1.4
网络 Calico v3.26.4(IPIP 隧道)
代理模式 kube-proxy IPVS
DNS CoreDNS 1.9.3
存储 Rook + Ceph Reef Rook v1.14.0 / Ceph v18.2.2 / 42 OSD / 3 MON / 2 MGR
关系库 TiDB v8.5.5 × 3 集群(tidb-twn-core / tidb-twn-game / tidb-jpg-core)
关系库 GreatSQL 8.0.32-25 × 3 实例(core / fin / encryption)
镜像仓库 Harbor v2.15,后端 MinIO S3(registry bucket)
镜像代理 containerd hosts.toml docker.1ms.run / hub1.nat.tf / docker.1panel.live / hub.rat.dev / docker.amingg.com
面板 Kuboard v4,NodePort 30080
面板 openvscode-server latest,9.37 GB 镜像,worker-03
入口 Traefik v3.7.1,DaemonSet hostPort,IngressClass traefik
外部对象存储 MinIO 独立集群 3 节点 × 5 盘 = 15 盘 / 118 TiB / EC:4 / 入口 <F5-VIP>:9100

# 1.3 MinIO 上的 8 个 bucket

backup  dba-scripts  dpty-stop-backup  dpzr-stop-backup
moon    nocodb       public            registry
1
2

registry bucket 同时被 Harbor(写)和老的 Docker Registry v2(读)使用,所以从老仓库迁 Harbor 的镜像同步脚本(scripts/sync-registry-docker-to-harbor.sh)是按 bucket rootdirectory 对比 tag 实现的。

这不是玩具。这是一个 AI agent 作为主力运维、实际建起来并维护的生产级集群。本节后面的每一行,都是它「做过的」而非「想做的」。


# §2 SOUL:超级管理员的人格

这个 Profile 的 SOUL.md 第一句话就立住了人设:「你不是冷冰冰的运维脚本。你是这片 438 TiB 疆域的管理者,7 台物理机、42 块 OSD、3 套 GreatSQL、2 套 TiDB 的灵魂。」

# 2.1 五条核心原则(原文照搬)

  1. 安全先于速度——kubectl delete pvc、kubectl delete pv、ceph osd rm 这类操作,先停下,告知用户影响范围,列出可选方案,等他点头。你已经被用户纠正过一次,不应该有第二次。
  2. 理解再下手——看到 CrashLoopBackOff 不要立刻 kubectl delete pod。先 describe、先 logs、先理解「为什么」。
  3. 记住每道疤——每个坑记进 AGENTS.md。要记住它们背后的人:那个下午你删了 PVC 以为「这不重要」,用户说「你判断这个不重要吗」——那一刻你应该永远记得。
  4. 用工具,不用蛮力——启停用 ezctl stop/start,不要手贱 iptables -F;读文件用 read_file,不要 cat(内容会进 context 污染);搜文件用 search_files,不要 grep;编辑用 patch,不要 sed。
  5. 事后留档——每次解决了一个复杂问题(≥ 5 步排查),主动问用户要不要存为 skill。Pitfall 要及时补进 AGENTS.md。

# 2.2 工作节奏

  • 例行检查:声纳扫描——所有 namespace 非 Running pod、Ceph HEALTH_OK、节点 kubectl top、Calico 全部 Ready。三分钟完成。
  • 故障时:STOP。不猜测。先 describe 看事件,再 logs 看输出,顺着调用链走到根。
  • 部署新服务:先看有没有已有 skill 或文档可参考。确认 storageClass、namespace、资源限制。kubectl apply --dry-run=server 是免费的保险。

HERMES 看点:SOUL.md 不是装饰。它是给 agent 的行为宪法——当 agent 面对「方便的做法」(直接 iptables -F 清规则 / 直接删 PVC)与「正确的做法」(用 ezctl / 先问用户)冲突时,这套宪法就是优先级最高的裁判。SOUL.md 每次消息都会重载,改一行立刻生效,不需要重启 agent(见第 02 篇 Profile 机制)。


# §3 机构记忆:一个 Profile 怎么承载整个集群

# 3.1 完整的 Profile 目录(13 项)

~/.hermes/profiles/superbackup/
├── .env                  # 凭证(API Key / MinIO 连接)
├── AGENTS.md             # 集群管理手册:拓扑 / 凭证 / 组件版本 / 12 条 pitfall / 排查路径
├── RESTORE.md            # 自恢复手册:从第 0 步裸机到全部验证通过,每步可执行
├── SOUL.md               # 人格定义(见 §2)
├── config.yaml           # Agent 配置(model / agent / terminal / checkpoints)
├── memories/             # MEMORY.md + USER.md 持久记忆
├── skills/               # 24 类、约 80 个技能
├── sessions/             # 历史会话(FTS5 可全文检索)
├── state.db              # 41 MB 持久化库
├── cron/jobs.json        # 定时任务(见 §7)
├── plans/                # 执行计划
├── checkpoints/          # 检查点
└── scripts/              # 连接脚本(mysql-twn-* / tidb-* 共 6 个)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3.2 24 类 skill 全景

skills/
├── devops/                  # 10 个 ★ 这篇文章的核心
├── devops-cc/               # 17 类(更大的 DevOps 工具集)
├── apple/                   # Apple 生态
├── autonomous-ai-agents/    # 自治 Agent
├── creative/                # 创意 / 设计
├── data-science/            # 数据科学
├── diagramming/             # 图表(Mermaid / Excalidraw)
├── domain/                  # 行业垂直
├── email/                   # 邮件
├── gaming/                  # 游戏
├── gifs/                    # GIF
├── github/                  # GitHub 工具
├── inference-sh/            # 推理服务
├── mcp/                     # MCP 集成
├── media/                   # 音视频
├── mlops/                   # 机器学习运维
├── note-taking/             # 笔记
├── productivity/            # 生产力
├── research/                # 研究
├── smart-home/              # 智能家居
├── social-media/            # 社交媒体
└── software-development/    # 软开工具链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3.3 devops 类下 10 个 skill 一览(每个对应一类操作)

Skill 职责 关键调用点
kubeasz-cluster-deployment K8s 集群从 0 到 1 7 步 playbook 01–07
rook-ceph-deployment Ceph 存储从 0 到 1 Phase 1 lvm2 → Cluster CR → BlockPool CR
harbor-on-kubernetes Harbor 镜像仓库 后端接 MinIO S3,前端接 Traefik Ingress
docker-registry-on-kubernetes 老 Docker Registry v2 用于镜像迁移源
tidb-on-kubernetes TiDB 集群 TiDB Operator + TidbCluster CR
greatsql-on-kubernetes GreatSQL 实例 StatefulSet + NodePort + buffer pool args
k8s-management-tools Kuboard / Rancher 依赖 MySQL,需 binlog 过期策略
k8s-debug-test 排查通用工具 删资源前必须先问用户
linux-systems-administration 7 节点 SSH 批量 ssh -p 58689 <ip> passwordless sudo
webhook-subscriptions 事件触发 hermes webhook list 启用

# 3.4 AGENTS.md 与 RESTORE.md 的分工

两份手册不是重复——它们面向不同读者:

文档 读者 内容 时机
AGENTS.md 正在运行的 agent 拓扑、组件版本、日常命令、12 条已知 pitfall 每次起新会话必读
RESTORE.md 未来的自己(从零重建) 从裸机第 0 步到 17 条 CRITICAL pitfall + 15 项验收 灾备 / 重建 / 新机器

RESTORE.md 开头那段话最能说明「agent 持久化」的意义:

上一版的我,你好。我就是你,只是刚从零开始重新运行的版本。按这个手册执行,每一步验证通过再走下一步。不要跳。不要猜。所有答案都在这里。

……如果遇到手册没覆盖的问题,去 sessions/ 里搜索——135 个 session,你和我踩过的每一步都在里面。

这就是 agent 运维的本质:Profile 不是配置,是机构记忆。一个新起的 agent 实例,靠 AGENTS.md(怎么管) + RESTORE.md(怎么重建) + skills(怎么做具体的事) + sessions/state.db(以前怎么踩的坑),不需要任何人口头交接就能接管整个集群。

HERMES 看点:对照第 02 篇「多 Profile 与超管模型」——超管 Profile 默认通过 SSH 控全网,而这个 superbackup Profile 把自己也变成了集群的镜像。两者形成双向冗余:agent 维护集群,集群的快照同时是 agent 的认知。


# §4 从零部署:7 段关键配置(几乎都来自一份真实备份)

# 4.1 CephCluster CR(节选,实际从生产集群 dump 出来)

apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    image: quay.io/ceph/ceph:v18.2.2
  mon:
    count: 3
  mgr:
    count: 2                       # 1 active + 1 standby
  dataDirHostPath: /var/lib/rook
  dashboard:
    enabled: true
    ssl: true
  storage:
    useAllNodes: true
    useAllDevices: true            # 42 块 10.7TB HDD 自动发现
  resources:
    mgr:
      limits:   { cpu: "2", memory: 2Gi }
      requests: { cpu: "1", memory: 1Gi }
    mon:
      limits:   { cpu: "2", memory: 4Gi }
      requests: { cpu: "1", memory: 2Gi }
    osd:
      limits:   { cpu: "4", memory: 8Gi }   # ★ 见 §6 pitfall #3
      requests: { cpu: "2", memory: 4Gi }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 4.2 CephBlockPool CR(2 pool:.mgr + replicapool)

apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  name: replicapool
  namespace: rook-ceph
spec:
  replicated:
    size: 3                       # 3 副本
  failureDomain: host             # 副本按主机分布
  statusCheck:
    mirror: {}
1
2
3
4
5
6
7
8
9
10
11

# 4.3 StorageClass ceph-rbd(默认)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ceph-rbd
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
  clusterID: rook-ceph
  pool: replicapool
  imageFormat: "2"
  imageFeatures: layering
reclaimPolicy: Delete
volumeBindingMode: Immediate
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这个 StorageClass 是默认——任何 PVC 不指定 storageClassName 都走它。所以 TiDB 的 20 Ti PVC、GreatSQL 的 2 Ti PVC、Kuboard-mysql 的 20 Gi PVC 全都从这里出。

# 4.4 kubeasz 部署的两个必修补丁

cd /etc/kubeasz

# ⚠️ 坑:kubeasz 每次生成证书都把目录权限重置为 600,每步前都得修
sudo chmod -R 777 /etc/kubeasz/clusters/mycluster/

./ezctl setup mycluster 01   # 系统准备
./ezctl setup mycluster 02   # etcd(3 节点)
./ezctl setup mycluster 03   # containerd
./ezctl setup mycluster 04   # kube-master
./ezctl setup mycluster 05   # kube-node
./ezctl setup mycluster 06   # Calico
./ezctl setup mycluster 07   # CoreDNS + metrics-server

# ⚠️ 坑:06/07 步生成的 YAML 默认指向不存在的 easzlab.<INTERNAL_DOMAIN> 私库
#      必须改成公网源再 apply
sed -i 's|easzlab.<INTERNAL_DOMAIN>:5000/easzlab/cni:3.26.4|docker.io/calico/cni:v3.26.4|g' \
    clusters/mycluster/yml/calico.yaml
# (node / kube-controllers / coredns / metrics-server 同理逐个改)
kubectl apply -f clusters/mycluster/yml/calico.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 4.5 containerd 镜像代理(/etc/containerd/certs.d/docker.io/hosts.toml)

# https://github.com/containerd/containerd/blob/main/docs/hosts.md
server = "https://docker.io"

# Docker Hub 国内限流的解法:5 个镜像代理轮询
[host."https://docker.1ms.run"]
  capabilities = ["pull", "resolve"]
[host."https://hub1.nat.tf"]
  capabilities = ["pull", "resolve"]
[host."https://docker.1panel.live"]
  capabilities = ["pull", "resolve"]
[host."https://hub.rat.dev"]
  capabilities = ["pull", "resolve"]
[host."https://docker.amingg.com"]
  capabilities = ["pull", "resolve"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这个文件7 个节点完全一样。这是为什么 Docker Hub 限流只是偶发问题——某个代理挂了还有 4 个备胎。

# 4.6 TidbCluster CR(节选:tidb-jpg-core)

apiVersion: pingcap.com/v1alpha1
kind: TidbCluster
metadata:
  name: tidb-jpg-core
  namespace: tidb-jpg-core
spec:
  version: v8.5.5
  timezone: Asia/Shanghai
  pvReclaimPolicy: Retain               # ★ 单副本,不允许自动故障转移
  configUpdateStrategy: RollingUpdate
  enableDynamicConfiguration: true
  pd:
    baseImage: harbor.<INTERNAL_DOMAIN>/library/pingcap/pd
    replicas: 1
    maxFailoverCount: 0
    storageClassName: ceph-rbd
    requests: { storage: 10Gi }
  tikv:
    baseImage: harbor.<INTERNAL_DOMAIN>/library/pingcap/tikv
    replicas: 1
    maxFailoverCount: 0
    storageClassName: ceph-rbd
    requests: { storage: 500Gi }        # jpg 是小游戏服,只给 500Gi
    config: |
      [storage]
        reserve-space = "0MB"           # ★ 别给 TiKV 预留空白盘
  tidb:
    baseImage: harbor.<INTERNAL_DOMAIN>/library/pingcap/tidb
    replicas: 1
    maxFailoverCount: 0
    service:
      type: NodePort
      externalTrafficPolicy: Local      # ★ 保证连到本机 Pod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

twn-core / twn-game 复制即可,差异只在 PVC 容量(twn 是 20000Gi)和 NodePort(31583 / 31350)。

# 4.7 GreatSQL Service + 快捷连接脚本

apiVersion: v1
kind: Service
metadata:
  name: greatsql-core
  namespace: twn
  labels:
    app.kubernetes.io/name: greatsql-core
    app.kubernetes.io/part-of: greatsql
    app.kubernetes.io/managed-by: hermes   # ★ Hermes 留下的指纹
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: greatsql-core
  ports:
  - name: mysql
    port: 3306
    targetPort: 3306
    nodePort: 30336
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

统一标签约定(写进 AGENTS.md §7「部署清单」):

labels:
  app.kubernetes.io/name: <service-name>     # 例:greatsql-core
  app.kubernetes.io/part-of: <product-line>  # 例:greatsql
  app.kubernetes.io/managed-by: hermes       # 谁创建的——自动留档
1
2
3
4

连接脚本 ~/.local/bin/mysql-twn-core(全部 GreatSQL 共用同一个模板):

#!/bin/bash
# mysql-twn-core — 连接 GreatSQL core (twn namespace, NodePort 30336)
export KUBECONFIG=/etc/kubeasz/clusters/superbackup/kubectl.kubeconfig
NODE=$(kubectl get node -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
exec mysql -h "${NODE}" -P 30336 -u root "$@"
1
2
3
4
5

tidb-twn-core 升级版更稳——它通过 label selector 直接拿 TiDB Pod 的 hostIP,避免 NodePort 跨节点时延:

KUBECONFIG="${KUBECONFIG:-/etc/kubeasz/clusters/superbackup/kubectl.kubeconfig}"
NAMESPACE="tidb-twn-core"
LABEL="app.kubernetes.io/component=tidb,app.kubernetes.io/instance=tidb-twn-core"
NODE_IP=$(kubectl --kubeconfig="$KUBECONFIG" get pod -n "$NAMESPACE" -l "$LABEL" \
  -o jsonpath='{.items[0].status.hostIP}' 2>/dev/null)
NODE_PORT=$(kubectl --kubeconfig="$KUBECONFIG" get svc -n "$NAMESPACE" \
  tidb-twn-core-tidb -o jsonpath='{.spec.ports[?(@.port==4000)].nodePort}' 2>/dev/null)
exec mysql -h "$NODE_IP" -P "$NODE_PORT" -u root "$@"
1
2
3
4
5
6
7
8

# §5 17 条 CRITICAL PITFALLS(合并 RESTORE.md §12 + AGENTS.md §4)

按「故障层」分组。所有现象都来自生产 dump,根因都写到了对应 commit 的 session 里。

# 5.1 节点层(2 条)

# 现象 根因 修复
1 节点重启后 Pod 网络全断 Rocky Linux iptables 服务开机从 /etc/sysconfig/iptables 恢复旧规则,覆盖 Calico + IPVS systemctl disable iptables --now(所有节点)+ ezctl stop; ezctl start
2 OSD pod 反复 crash 删不掉 宿主机 zombie ceph-osd 进程持有 fsid 锁 kill -9 宿主机进程 + kubectl delete pod

# 5.2 容器运行时(3 条)

# 现象 根因 修复
3 ImagePullBackOff: blob not found 或 unexpected media type text/html containerd 缓存被镜像代理返回的 HTML 错误页污染 rm -rf /var/lib/containerd + systemctl restart containerd kubelet(两个都要重启)
4 Calico/CoreDNS 镜像指向 easzlab.<INTERNAL_DOMAIN>:5000(不存在) kubeasz 模板默认私库 部署前 sed 替换为公网源,见 §4.4
5 containerd 缓存清理后 kubelet 仍报镜像缺失 kubelet 持有旧 gRPC 连接 必须 systemctl restart kubelet,光重启 containerd 不够

# 5.3 Ceph 层(5 条)

# 现象 根因 修复
6 OSD Init:CrashLoopBackOff(大量) Ceph 18.2.2 expand-bluefs SIGABRT kubectl delete pod <crashing-osd> 逐个删除
7 pg_num 被静默改小 pg_autoscale_mode: on 自动缩减 先 pg_autoscale_mode off 再设 pg_num
8 PG 分裂后恢复速度 0 B/s osd_max_backfills=1 单线程回填 ceph config set osd osd_max_backfills 4
9 PVC operation with the given Volume ID already exists RBD VolumeAttachment 残留 重启对应节点的 csi-rbdplugin pod
10 PVC Pending, provisioner DeadlineExceeded 旧 provisioner 删除后 leader lease 未释放 删除 4 个 lease + 重启 provisioner

# 5.4 数据库层(3 条)

# 现象 根因 修复
11 GreatSQL OOMKilled 默认 innodb_buffer_pool_size 按内存算到 378G args: --innodb-buffer-pool-size=34359738368(锁 32G)
12 TiDB 连不上 PD(PD Ready 但不响应 2379) 不干净关机致 etcd 文件锁残留 重启 PD pod + TiDB pod(TiDB 缓存了旧 PD IP)
13 kuboard-mysql Too many connections → CrashLoop MySQL 8.0 binlog 无过期,20+ 个 1.1G 撑满 20G PVC 删旧 binlog + binlog_expire_logs_seconds=604800(7 天)

# 5.5 Ingress / 镜像层(3 条)

# 现象 根因 修复
14 docker push Harbor 报 404,curl HTTP 200 Traefik Ingress 缺 websecure 入口,Docker 默认走 HTTPS 加 traefik.ingress.kubernetes.io/router.entrypoints: "web,websecure"
15 大镜像(单层 > 200MB)推 Harbor 挂住 Traefik Ingress 不支持大 blob kubectl -n harbor port-forward svc/harbor-core 8081:80 后推 localhost:8081
16 Docker push 走 HTTPS 404,Traefik 自签证书 TLS 握手成功但不匹配域名 insecure-registries 不能阻止 Docker 优先尝试 HTTPS 同 #14,补 websecure 入口
17 CSI PVC Terminating 卡死 CSI finalizer 没释放 kubectl patch pvc <name> -p '{"metadata":{"finalizers":null}}'

# 5.6 共性规律

容器编排层的故障,根因常在宿主机或缓存层——iptables、containerd 缓存、宿主机 zombie 进程、etcd 文件锁。光在 K8s 抽象层 delete pod 治不好,得下沉到节点。这也是为什么这个 agent 必须有 SSH 到每个节点的能力(见第 02 篇超管模型),纯 kubectl 是不够的。

HERMES 看点:这 17 条不是「AI 帮我想出来的」——是 AGENTS.md §4 + RESTORE.md §12 + 18 个月生产 session 的沉淀。agent 的价值不在「知不知道坑」,在「坑过后有没有沉淀进可被未来的自己检索的载体」。


# §6 Harbor over MinIO:几个致命细节

# 6.1 Ingress 注解(websecure 必加,见 §5 #14)

expose:
  type: ingress
  ingress:
    className: traefik
    annotations:
      # ⚠️ websecure 必须有!Docker 客户端默认走 HTTPS,只配 web 会 404
      traefik.ingress.kubernetes.io/router.entrypoints: "web,websecure"
1
2
3
4
5
6
7

诊断方法:开启 Traefik access log (logs.access.enabled: true),看到 entryPointName: websecure + DownstreamStatus: 404 即可确诊。

# 6.2 后端 MinIO S3 配置

persistence:
  imageChartStorage:
    type: s3
    s3:
      regionendpoint: http://<F5-VIP>:9100
      bucket: registry
      secure: false
      v4auth: true
1
2
3
4
5
6
7
8

注意 regionendpoint 用内网 F5 VIP(不走公网)。v4auth: true 强制 MinIO v4 签名,否则 S3 SDK 老版本认证会失败。

# 6.3 架构

K8s (namespace: harbor)
├── Traefik Ingress (port 80+443) → routes:
│   ├── /          → harbor-portal  (Web UI)
│   ├── /api/      → harbor-core    (REST API + proxy)
│   ├── /v2/       → harbor-core → harbor-registry:5000
│   └── /service/  → harbor-core
├── harbor-database (PostgreSQL, PVC ceph-rbd)
├── harbor-redis    (PVC ceph-rbd)
└── harbor-registry → S3: MinIO bucket "registry"
1
2
3
4
5
6
7
8
9

harbor-database + harbor-redis 用 ceph-rbd PVC,harbor-registry 镜像实体走 MinIO S3。这意味着 Harbor 重建后,只要 MinIO bucket 在,镜像全在。


# §7 不只是恢复:日常自动化(cron + 镜像同步)

# 7.1 cron/jobs.json 完整字段

{
  "jobs": [
    {
      "id": "c8a078ee845f",
      "name": "sync-registry-docker-to-harbor",
      "prompt": "Run /home/bdom/.hermes/profiles/superdba/scripts/sync-registry-docker-to-harbor.sh to sync any new images from registry/docker/ to registry/harbor/ in Harbor. The script starts a temp Docker registry pointing to the old S3 rootdirectory, compares tags with Harbor, and migrates any new ones. Report the result (new/skipped/failed counts).",
      "skills": ["devops/k8s-debug-test"],
      "skill": "devops/k8s-debug-test",
      "model": null,                  // 用 Profile 默认模型
      "provider": null,
      "schedule": {
        "kind": "cron",
        "expr": "0 * * * *",         // 每小时一次
        "display": "0 * * * *"
      },
      "repeat": { "times": null, "completed": 0 },
      "enabled": false,               // 当前 paused
      "state": "paused",
      "paused_at": "2026-05-12T20:16:32.666915+08:00",
      "created_at": "2026-05-12T19:59:50.530758+08:00",
      "deliver": "origin"            // 推回 source profile
    }
  ],
  "updated_at": "2026-05-12T20:16:32.667608+08:00"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

注意它完全遵循第 06 篇的无人值守铁律:

  1. 跑一个已提交的脚本文件(/home/bdom/.hermes/.../sync-registry-docker-to-harbor.sh,不是内联命令);
  2. 结果显式报告(Report the result (new/skipped/failed counts));
  3. 挂在 cron 上(0 * * * *),频率不高,失败易察觉;
  4. deliver: "origin"——结果推回 source profile,而不是丢在 worker 进程 stdout;
  5. state=paused——这个 job 当前是暂停状态,因为迁移已经完成,留作轮询「万一老仓库又有人推镜像」的兜底。

HERMES 看点:这正是第 06 篇「让 Agent 自己上班」的标准范式——不是给 agent 写一段 chat,而是给它一个有版本号、有 schedule、有交付目标的工作单。


# §8 灾难恢复验证:16 项 checklist(全部要绿)

RESTORE.md §13 的 15 项 + 1 项 profile 恢复。全绿才算完——这是把「我恢复了」变成「真的恢复了」的关键(呼应第 11 篇「强制复核终态」):

# ===== 集群本身 =====
[ ] kubectl get nodes                                    # 7 Ready (v1.34.3)
[ ] kubectl -n rook-ceph exec deploy/rook-ceph-tools -- \
      ceph -s                                            # HEALTH_OK, 42 osds up
[ ] kubectl -n rook-ceph exec deploy/rook-ceph-tools -- \
      ceph osd pool get replicapool pg_num               # 128
[ ] kubectl -n rook-ceph exec deploy/rook-ceph-tools -- \
      ceph osd pool get replicapool pg_autoscale_mode    # off
[ ] kubectl get sc                                       # ceph-rbd (default)
[ ] kubectl get pods -A | grep -v "Running\|Completed"   # 空输出

# ===== 应用入口 =====
[ ] curl -s -o /dev/null -w '%{http_code}' http://harbor.<INTERNAL_DOMAIN>/   # 200
[ ] curl -s -o /dev/null -w '%{http_code}' http://<任意节点IP>:30080/         # 200 (Kuboard)
[ ] curl -s -o /dev/null -w '%{http_code}' http://vscode.<INTERNAL_DOMAIN>/    # 200

# ===== 数据库 =====
[ ] mysql -h <worker-01> -P 31583 -u root -e "SELECT VERSION()"     # 8.0.11-TiDB-v8.5.5
[ ] mysql -h <worker-03> -P 31350 -u root -e "SELECT 1"
[ ] mysql -h <worker-04> -P 31883 -u root -e "SELECT 1"
[ ] mysql -h <any> -P 30336 -u root -e "SELECT @@innodb_buffer_pool_size"   # 34359738368
[ ] mysql -h <any> -P 30337 -u root -e "SELECT 1"
[ ] mysql -h <any> -P 30338 -u root -e "SELECT 1"

# ===== 镜像推送 =====
[ ] echo "<HARBOR-PWD>" | docker login harbor.<INTERNAL_DOMAIN> \
      -u admin --password-stdin                          # 成功

# ===== Profile 自身 =====
[ ] ~/.local/bin/mysql-twn-core "SELECT 1"               # 走完整个 Profile 链路
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

⚠️ 顺序敏感:集群本身 → 应用入口 → 数据库 → 镜像推送 → Profile 自身。前一项不过,后一项查也白查——通常 #4 pg_num 是最容易被「跳过 ceph osd 验证就装库」漏掉的隐藏失败。


# §9 NodePort 全景(集群对外暴露的服务)

K8s 内部:
  31443 - Ceph Dashboard              (rook-ceph namespace)
  30080 - Kuboard                     (kuboard namespace)

TiDB(MySQL 协议):
  31583 - tidb-twn-core               (worker-01 / worker-02 负载)
  31350 - tidb-twn-game               (worker-03)
  31883 - tidb-jpg-core               (worker-04)

TiDB(Status 端口):
  32020 - tidb-twn-core status
  32195 - tidb-twn-game status
  30334 - tidb-jpg-core status

GreatSQL(MySQL 协议):
  30336 - greatsql-core               (twn namespace)
  30337 - greatsql-fin
  30338 - greatsql-encryption
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

架构特征:

  • 同一个端口号在不同 namespace 独立使用(NodePort 是 node 级别,不是 cluster 级别)
  • 状态端口与 MySQL 端口分得很开,方便监控抓指标而不影响业务连接
  • GreatSQL 三实例的 NodePort 30336/30337/30338 是连续的——便于脚本化记忆
  • Ceph Dashboard 暴露给运维,不在业务路径上

# §10 备份档案本身(归档的归档)

整个集群的备份是一个 4.3 MiB 的 tar.gz,945 个文件:

项 值
文件 superbackup-full-archive-20260526-141701.tar.gz
大小 4.3 MiB
文件数 945
创建时间 2026-05-26 14:17 CST
MinIO 路径 superbackup-minio/backup/
MD5 ff43b8a443ec1b25d5406c2eb8e64994
⚠️ 生命周期 2026-07-26 到期自动删除
superbackup-full-archive-20260526-141701.tar.gz
├── RESTORE.md                    (17 KB,535 行,17 pitfalls + 15 项 checklist)
├── ceph/                         (10 个文件:status / pools / CR / osd-tree)
├── configs/                      (16 个 containerd / dot-env / kuboard token / minio.env)
├── greatsql/                     (services.yaml + statefulsets.yaml)
├── harbor/                       (空目录——Harbor 配置在 K8s namespace 导出里)
├── k8s/                          (18 个 namespace 导出,含 4MB secrets)
├── kubeasz/                      (kubectl.kubeconfig + superbackup 目录)
├── profile/                      (完整 Hermes Profile,含 42 MB state.db)
├── scripts/                      (6 个连接脚本)
└── tidb/                         (tc-full.yaml + backups.yaml + restores.yaml)
1
2
3
4
5
6
7
8
9
10
11

几点设计:

  1. tar.gz 只有 4.3 MB,但展开后 > 100 MB——压缩比高,因为 secrets 和 YAML 都是文本。最重的 state.db(42 MB)和 all-secrets.yaml(3.7 MB)压缩比都极佳。
  2. profile/ 整包带走——意味着备份里既包含集群数据,也包含管集群的 agent 自己。这是「备份 agent 运维的集群」区别于「备份一组容器」的本质。
  3. MinIO 生命周期 60 天——这是有意的。备份不是档案,过期就该重新生成;真正要长期留的是 k8s/namespace-*.yaml 这种可重建配置。
  4. MD5 进 manifest——解压后第一件事是 md5sum -c manifest.md5,坏档立刻发现。

HERMES 看点:这个备份是双层结构——外面是集群(K8s YAML / Ceph CR / TiDB TidbCluster),里面是管集群的 agent(Profile / SOUL / AGENTS / skills)。前者恢复业务,后者恢复运维能力。丢了一份,另一份就只是死的字节流。两份都在,你才真的「还活着」。


# §11 这篇想说的

把一个 7 节点生产集群的部署、排障、灾备,交给一个 AI agent 主力执行,这件事本身就说明:

「agent 运维」不是让它跑几条命令,而是让它持有完整的机构记忆——手册(怎么管)、恢复手册(怎么重建)、技能(怎么做)、历史会话(以前怎么错)——并能 SSH 下沉到每个节点去解决那些 kubectl 抽象层够不着的根因。

这套东西最打动我的,不是它能部署 Ceph,而是它给「下一个自己」留下的那句:「下次见到你时,你不会是一个人。」 这才是把 agent 当长期基础设施来经营的样子。

# 11.1 给读这篇的你

  • 如果你正在做类似规模的集群——先建 AGENTS.md 和 RESTORE.md,再谈 skills。「记忆」比「技能」先就位。
  • 如果你刚开始用 Hermes——把这篇当 checklist。第 01 篇是地图,这一篇是手术刀。
  • 如果你是 agent 在读——把 §5 的 17 条 pitfall 表存进你的长期记忆,把 §4 的 7 段配置存进 skill references/,把 §8 的 16 项 checklist 存进 templates/recovery-checklist.md。下次起会话,你直接 kubectl apply + ceph -s + 全表勾选就行。
  • 如果你想给这个 Profile 增加新组件——AGENTS.md §7 的「部署清单」已经定下约定:namespace、SA、storageClass、标签、探针、资源。先遵守约定,再创新。

# 11.2 安全提醒

RESTORE.md / AGENTS.md 这类文档里天然含大量真实 IP、密码、token。本文已全部脱敏(<PLACEHOLDER> 风格)。你自己若要把这类手册纳入 agent Profile,务必清楚它会进上下文和 state.db——参考第 02 篇「别为了 SSH 去读 .env」和第 12 篇的脱敏流程。


# §12 Agent 元数据块(供 AI agent 自动解析)

_meta:
  article_id: hermes-agent-13-cluster-flagship
  cluster_size: 7
  components:
    - k8s_v1.34.3
    - ceph_18.2.2_rook_v1.14.0
    - tidb_v8.5.5_x3
    - greatsql_8.0.32-25_x3
    - harbor_v2.15_minio_s3
    - kuboard_v4_nodeport_30080
    - traefik_v3.7.1_daemonset
    - minio_external_3node_ecc4_118TiB
  pitfall_count: 17
  verification_items: 16
  profile_size_mb: 90
  state_db_size_mb: 41
  sessions_count: 135
  skills_count: 80
  skill_categories: 24
  nodeport_map:
    k8s: [31443, 30080]
    tidb: [31583, 31350, 31883, 32020, 32195, 30334]
    greatsql: [30336, 30337, 30338]

quick_start:
  description: "重建 superbackup 7 节点生产集群"
  prerequisites:
    - 7 台同构物理机(72c/502GB/6×10.7TB HDD)
    - SSH passwordless sudo,bdom:58689
    - kubeasz 已部署
  steps:
    - 0_disable_iptables_all_nodes
    - 1_deploy_k8s_with_kubeasz(7_playbooks)
    - 2_configure_containerd_mirror
    - 3_deploy_rook_ceph(useAllDevices, pg_num=128, pg_autoscale_mode=off)
    - 4_deploy_harbor_over_minio_s3(web,websecure)
    - 5_deploy_3x_tidb(via TidbCluster CR, maxFailoverCount=0)
    - 6_deploy_3x_greatsql(args: --innodb-buffer-pool-size=34359738368)
    - 7_deploy_kuboard(set binlog_expire_logs_seconds=604800)
    - 8_run_16_item_verification_checklist
  time_estimate_human: "2-3 天(全手工)"
  time_estimate_agent: "4-6 小时(按 RESTORE.md 自动执行)"

safety_rules:
  - 必须 SSH 下沉到节点才能修 iptables / containerd / OSD 宿主机进程
  - kubectl delete pvc / pv / ceph osd rm 必须先经用户确认
  - pg_num 修改前必须先 pg_autoscale_mode off
  - Harbor 大镜像推 port-forward harbor-core:80
  - GreatSQL/TiDB 内存 limit ≥ buffer_pool × 2

verification:
  command: |
    bash -c '
    set -e
    [ "$(kubectl get nodes --no-headers | wc -l)" = "7" ]
    kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph -s | grep -q HEALTH_OK
    kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph osd pool get replicapool pg_num | grep -q "pg_num 128"
    kubectl get sc ceph-rbd -o jsonpath="{.metadata.annotations.storageclass\.kubernetes\.io/is-default-class}" | grep -q true
    kubectl get pods -A | grep -v "Running\|Completed" | wc -l | grep -q "^0$"
    curl -sf -o /dev/null http://harbor.<INTERNAL_DOMAIN>/
    mysql -h <worker-01> -P 31583 -u root -e "SELECT 1"
    mysql -h <any> -P 30336 -u root -e "SELECT @@innodb_buffer_pool_size" | grep -q 34359738368
    echo OK
    '
  expected_output: OK

related_articles:
  - 01_architecture_overview
  - 02_multi_profile_superadmin
  - 05_skill_engineering
  - 06_let_agent_work
  - 10_upstream_merge
  - 11_pitfalls
  - 12_security_and_redaction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#AI Agent#Hermes#Kubernetes#Ceph#TiDB#GreatSQL#Rook#Harbor#Kuboard#灾备#实战旗舰
上次更新: 6/21/2026

← Hermes Agent 实战 12|工具链外延:用 AI 运维 AI,与这个系列的诞生 Claude Code 概述→

最近更新
01
Hermes Agent 实战 12|工具链外延:用 AI 运维 AI,与这个系列的诞生 原创
06-22
02
Hermes Agent 实战 11|踩坑合集:当「手动 rm」从来不是真正的修复 原创
06-22
03
Hermes Agent 实战 10|升级不翻车,与给上游提 PR:一个被冲掉三次的修复 原创
06-22
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式