入职 Web3 运维日记 · 第 10 日:终极考核 —— 混沌工程 (Chaos Engineering) 与区域级容灾

时间 :入职第 10 天,下午 02:00 天气 :晴,由于服务器房冷气太足,感觉有点冷 事件:CTO 发起的突袭式演练 (Fire Drill)

下午两点,我正盯着 Grafana 看板上平稳的曲线发呆。 CTO 和基础设施负责人(Head of Infra)突然走到了我身后。

CTO 笑着说:"Alen,前几天你搭节点、配监控、搞安全、防夹子,做得都不错。你的架构图画得很漂亮,号称是高可用 (HA) 的。" 我点点头:"是的,基于 AWS 多可用区 (Multi-AZ) 设计的。"

CTO 拿出了他的笔记本电脑,打开 AWS 控制台,指着我那台正在跑生产流量的主节点 Geth-Primary (ap-southeast-1a) 。 "如果我现在把这台机器 Terminate (销毁) 了,公司业务会挂多久?"

我愣了一下:"这是生产环境......" CTO 手指悬在鼠标上:"在 Web3,真正的故障从来不分环境。你有 5 分钟准备时间。5 分钟后,我会手动模拟 可用区 A (Zone A) 级大断网。"

这就是第 10 天的终极考核:人为制造灾难

🧨 1. 下午 02:05:第一刀 ------ 核心节点"暴毙"

我迅速打开了我的备用终端,确认我的 Warm Standby (热备) 节点状态。

  • Node A (Primary): 位于 Zone A,处理 100% 流量。

  • Node B (Standby): 位于 Zone B,实时同步中,Nginx 权重为 0(备用)。

CTO 没有手软,直接点击了 Terminate Instance。 瞬间,Node A 的 SSH 连接断开。 Grafana 上,Node A 的各项指标(Peer Count, Block Height)呈断崖式下跌至 0。

Nginx 层的反应: 我之前配置的 Nginx 主动健康检查 (Active Health Check) 开始报错。

复制代码
# Nginx Error Log
[error] ... upstream server 172.31.10.100:8545 is down

自动切换逻辑: 我的 Nginx 配置里有这样一段:

复制代码
upstream geth_backend {
    server 172.31.10.100:8545 max_fails=3 fail_timeout=5s; # Primary
    server 172.31.20.100:8545 backup; # Standby (平时不接客,老大挂了才接)
}

仅仅过了 3 秒钟 ,Nginx 发现 Primary 死了,自动激活了 backup 标记的 Node B。

业务表现: Scanner 组长在群里喊了一句:"刚才好像有两个请求超时报错了,但马上又好了。你们在发布吗?" 我回复:"没事,继续跑。"

第一关 Pass。 读请求 (Read Ops) 几乎无感切换。

🧱 2. 下午 02:30:第二刀 ------ 签名服务断连

CTO 见节点切换成功,并没有停手。 "节点活着只能查账。如果 Redis (Nonce 计数器) 所在的主机也挂了呢?提现还能发出去吗?"

他模拟了 Redis 主节点宕机。 我们在 Day 6 为了解决 Nonce 冲突引入了 Redis。如果 Redis 挂了,Nonce 就会乱序,提现就会卡死。

AWS ElastiCache 的表现: 我使用的是 AWS ElastiCache (Cluster Mode Disabled) ,配置了 Multi-AZ 自动故障转移

  • 现象 :提现服务开始大量报错 Connection Refused

  • 持续时间 :大约 25 秒

  • 恢复:AWS 自动将位于 Zone B 的从节点 (Replica) 提升为新的主节点 (Master),并更新了 DNS 记录。

Alen 的补救 (Retry Policy) : 虽然 AWS 自动恢复了,但这 25 秒的报错怎么办? 我之前在提现服务的代码里强制要求开发加了 指数退避重试 (Exponential Backoff)

复制代码
# 提现微服务伪代码
def get_nonce():
    for i in range(5):
        try:
            return redis.incr("nonce")
        except ConnectionError:
            sleep(2 ** i) # 等待 1s, 2s, 4s...

正是这个重试机制,扛住了那 25 秒的 DNS 切换窗口。没有任何一笔提现请求彻底失败。

第二关 Pass。 写请求 (Write Ops) 有短暂延迟,但数据未丢失。

☠️ 3. 下午 03:00:第三刀 ------ 验证者 (Validator) 的生死抉择

这是最惊险的一关。 CTO 问:"你的 Lighthouse 验证者节点 (负责给以太坊网络出块赚钱) 也在 Zone A。现在它挂了,你要不要在 Zone B 启动备用的验证者?"

Alen 的回答: "绝对不要!坚决不启动!"

CTO 挑了挑眉:"哦?为什么?你想漏块 (Missed Block) 吗?"

Alen 的解释: "漏块顶多损失一点收益(0.05 ETH)。 但如果我启动了备用验证者,而 Zone A 的原节点其实没有死透 (只是网络分区,它还在那边偷偷签名),那么全网就会出现两个我签名的区块。 这叫 双重签名 (Double Signing) 。 后果是 Slashing (罚没) :我会瞬间被没收 32 ETH ,并被踢出网络。 在验证者运维中,Safety (不被罚) > Liveness (不漏块)。"

CTO 满意地点头:"很好。很多新人为了追求 100% 在线率,搞了自动验证者故障转移,结果把自己搞破产了。"

🧹 4. 下午 04:30:灾后重建 (IaC)

演练结束。Zone A 的废墟还在那儿。 我需要把环境恢复原状。

如果是以前,我要手动去申请 EC2,手动装 Geth,手动同步数据。 但现在,我有 Terraform

操作实录:

  1. 检测 :Terraform 发现 aws_instance.geth_primary 状态为 terminated

  2. 修复

    复制代码
    terraform apply
  3. 执行

    • Terraform 自动申请了一台新的 EC2。

    • 自动挂载了之前做过快照的 io2 数据盘(数据没丢)。

    • UserData 脚本自动拉起 Geth 和 Prometheus Exporter。

  4. 归位:新节点启动后,同步了最近几十分钟的区块。我手动在 Nginx 里把它改回 Primary,流量切回主可用区。

一切就像什么都没发生过一样。

📝 Day 10 总结

CTO 合上电脑,拍了拍我的肩膀:"Alen,恭喜转正。你的系统扛住了'拔网线'。"

回顾这 10 天:

  1. 搭建:从零拉起 Geth/Lighthouse。

  2. 安全:Nginx 反代与防火墙。

  3. 监控:Prometheus + 飞书告警。

  4. 升级:EBS 快照蓝绿部署。

  5. 钱包:KMS 签名与 Nonce 管理。

  6. 数据:归档节点与 Trace 路由。

  7. 博弈:Flashbots 防夹与 MEV 创收。

  8. 共识:Finalized 确认数与 Reorg 防御。

  9. 容灾:Day 10 的多可用区切换。

Alen 的最终感悟:

"Web3 运维很难,因为你要懂密码学、博弈论、金融风控。 Web3 运维也很简单,因为去中心化本身就是最好的高可用。

我们不是在维护一台服务器,我们是在维护一个资产高达数千亿美金的分布式账本的一个接入点。 敬畏技术,敬畏风险,敬畏每一行 Log。"


1. 为什么不用 AWS ALB (Application Load Balancer),非要自己折腾 Nginx?

Alen 的回答: 在 Day 10 这种灾难恢复场景下,ALB 确实看起来更省事。但结合 Day 4、Day 7、Day 8 的需求,ALB 是完全不够用的

  • 原因一:ALB 是"傻瓜式"的 (Layer 7 HTTP),Nginx 是"智能"的 (Layer 7 Logic)

    • ALB 的能力 :只能根据 URL 路径 (/api) 或 Host (api.bybit.com) 转发。

    • Nginx 的能力 :可以解析 Request Body (请求体)

    • 回顾 Day 7 & 8 :我们需要根据 JSON-RPC 里的 method: "debug_traceTransaction"method: "eth_sendRawTransaction" 这种内容 来决定转发给 Alchemy 还是 Flashbots。这是 ALB 做不到的(ALB 看不到 Body 里的 JSON 字段)。

  • 原因二:健康检查 (Health Check) 的深度

    • ALB:只要 TCP 端口通了,或者 HTTP 返回 200,它就认为节点是活的。

    • Nginx + Lua:我可以写脚本去问节点:"你的区块高度是多少?"如果节点虽然活着(HTTP 200),但是高度落后主网 500 个块,Nginx 可以认为它"半死不活",从而把流量切走。ALB 做不到这么细。

  • 原因三:成本与完全控制

    • ALB 是按小时+流量收费的黑盒。

    • Nginx 是免费的,而且我就装在 Geth 节点本机(或者单独的网关机),我对它的超时时间、重试策略有 100% 的控制权。


2. "指数退避重试" 是开发写的代码,为什么算在运维 Alen 头上?

Alen 的回答: 这是 SRE (站点可靠性工程) 的核心理念:"架构师制定规则,开发人员实现规则"。

  • 开发者的视角 :他们关注的是"功能实现"。只要 redis.incr() 能跑通就行。他们往往假设网络是永远稳定的。

  • 运维的视角 :Alen 知道 AWS 发生主从切换时,会有 15-30 秒 的 DNS 漂移时间。在这段时间内,Redis 是不可用的。

  • Alen 的工作

    1. Code Review (代码审查) :在上线前,Alen 会检查代码。如果发现开发没写 Retry 逻辑,或者写的是 while(true) 死循环重试(这会把恢复中的 Redis 再次打挂),Alen 会拒绝上线

    2. SDK 封装 :在成熟的大厂,运维平台组会提供一个标准的 BybitRedisClient,里面内置了指数退避逻辑。开发直接调用这个 Client,不需要自己写 Retry。

所以,虽然代码是开发敲的,但"必须加重试"这个架构决策是 Alen 下达的死命令。


3. "原节点没死透(只是网络分区)" 是什么意思?关机了还没死透吗?

Alen 的回答: 这是一个极其凶险的分布式系统 陷阱,俗称 "脑裂 (Split Brain)"

  • 情况 A:CTO 手动点了 Terminate (关机)

    • 这是 "死透了"。实例被销毁,硬盘被卸载。这时候你在 Zone B 启动备用节点是 100% 安全的。
  • 情况 B:真实世界的故障 ------ 光缆被挖断 (网络分区)

    • 想象一下,Zone A 的机房依然有电,服务器依然在跑,Geth 依然在运行。

    • 但是,连接 Zone A 和 Zone B 之间的光缆断了

    • Zone B 的视角:我看不到 Zone A 了,心跳检测失败。Zone B 以为 Zone A 挂了。

    • Zone A 的视角:我活得好好的,只是连不上 Zone B 了。但我还能连上部分互联网。

  • 恐怖后果 (Double Signing)

    • Zone A 的节点:继续工作,给区块 100 签名。

    • Zone B 的备用节点(如果你配置了自动启动):认为老大死了,自己上位,也给区块 100 签名。

    • 全网视角 :以太坊网络收到了两个来自同一个验证者私钥的签名,但内容不同。

    • 判定 :这不仅是故障,这是攻击(企图制造分叉)。

结论 :对于验证者节点,只要你不能 100% 确认老节点物理销毁了,就绝对不能启动新节点。 宁可挂机,不可双签。


4. 为什么会被罚款 (Slashing)?

Alen 的回答: 这是 以太坊 PoS (权益证明) 共识机制的"军法"。

  • 规则 :你想当验证者(Validator)赚钱,你必须先抵押 32 ETH (约 6-8 万美金) 作为保证金。

  • 红线双重签名 (Double Signing) 是最严重的罪行。因为这会破坏区块链的唯一性,导致网络分叉。

  • 惩罚 (Slashing)

    1. 立刻没收:直接从你的 32 ETH 里扣除一部分(比如 1 ETH)。

    2. 强制驱逐:你的验证者资格被剥夺,踢出网络。

    3. 慢性失血:在被踢出的过程中(有个排队期),你的余额会持续减少。

    4. 社会性死亡:你的验证者公钥被永远标记为"作恶者"。

对比

  • 漏块 (Downtime):只是**"少赚工资"**(几分钟几块钱)。

  • 双签 (Double Sign):是**"没收家产"**(几万美金瞬间蒸发)。

  • 运维原则:保住本金永远比赚那点小钱重要。


5. terraform apply 怎么做到的?不需要安装 Geth 吗?

Alen 的回答: 这就是 IaC (Infrastructure as Code) 的魔力。Terraform 用的不是 YAML,而是 HCL (HashiCorp Configuration Language) ,文件后缀是 .tf

为什么不需要手动安装 Geth?因为我们用了 "黄金镜像 (Golden AMI)" + "数据快照 (EBS Snapshot)"

自动化流程揭秘:

  1. AMI (系统盘镜像)

    • Alen 在入职第 3 天的时候,其实做过一件事:在一台干净的 EC2 上装好了 Linux + Geth + Prometheus Exporter + Nginx。

    • 然后把这台机器制作成了一个 AWS AMI(比如叫 ami-geth-v1.11.5)。

    • 这个 AMI 里已经有了软件环境,开机就能用。

  2. Snapshot (数据盘快照)

    • Day 5 升级时做的那个快照,里面有几 TB 的区块数据。
  3. Terraform 代码 (main.tf)

    复制代码
    resource "aws_instance" "geth_node" {
      ami           = "ami-geth-v1.11.5"  # 引用做好的系统镜像
      instance_type = "r6g.2xlarge"
    
      # 启动脚本 (User Data)
      user_data = <<-EOF
        #!/bin/bash
        # 1. 挂载数据盘
        mount /dev/nvme1n1 /data
        # 2. 启动服务 (软件都在 AMI 里装好了)
        systemctl start geth
        systemctl start nginx
      EOF
    }
    
    resource "aws_volume_attachment" "ebs_att" {
      device_name = "/dev/nvme1n1"
      volume_id   = aws_ebs_volume.data_disk.id
      instance_id = aws_instance.geth_node.id
    }

总结 : 当你敲下 terraform apply

  1. AWS 启动一台已经装好软件的电脑(AMI)。

  2. AWS 把装满数据的一块硬盘插上去(EBS)。

  3. 电脑开机,执行 UserData 脚本,挂载硬盘,启动软件。

  4. 全程 2 分钟,不需要人工 SSH 进去敲任何命令。

相关推荐
木西18 小时前
实战:基于 Solidity 0.8.27 与 OpenZeppelin V5 构建多链恶搞代币(以 SPX6900 为例)
web3·智能合约·solidity
大树882 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质2 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工2 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智2 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_2 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
施努卡机器视觉2 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
AC赳赳老秦2 天前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw
java_cj2 天前
深入kube-apiserver认证机制:从Bearer Token到mTLS的完整认证链解析
linux·运维·服务器·云原生·容器·kubernetes