时间 :入职第 5 天 天气 :晴,由于肾上腺素飙升感觉有点热 任务 :以太坊 "Shanghai" (上海) 硬分叉升级 SLA 要求:业务零中断
刚到公司打开 Slack,我就感受到了一股紧迫的气氛。 #announcements 频道里弹出了一条加急通知:以太坊主网将于区块高度 18,500,000 正式激活 "Shanghai" 升级。
组长把我叫到白板前,画了两条线:一条向左,一条向右。 "Alen,这是硬分叉 (Hard Fork)。如果我们在高度达到之前没有升级 Geth 和 Lighthouse,我们的节点就会因为不认识新规则,跑向右边这条废弃的分叉链。到时候 Scanner 读到的就是假数据,这属于 P0 级事故。"
目前的挑战:
-
截止时间:还有 48 小时。
-
业务限制 :Scanner 正在高频扫链,一秒钟都不能停。停机意味着漏块,漏块意味着用户充值无法入账。
🧠 1. 上午 10:00:制定策略 ------ 拒绝原地升级
我的第一反应是像维护 Web2 服务那样:停服务 -> 换包 -> 重启。 但在脑子里预演了一遍后,我否决了这个方案。
-
风险点:
-
Geth 在重启时需要重新加载巨大的数据库索引,可能耗时 5-10 分钟。这段时间 Nginx 会报 502,Scanner 业务会断。
-
万一新版本 binary 有 Bug 起不来,或者数据库结构不兼容需要 Migration(迁移),那我就挂在半空了,回滚都来不及。
-
决定方案:AWS 蓝绿部署 (Blue/Green Deployment) 我要起一台全新的 Node B (Green) ,在后台升级并同步好。等它准备就绪,再在 Nginx 层把流量像扳道岔一样切过去。用户无感,风险可控。
☁️ 2. 上午 11:00:AWS 的"时间魔法" ------ EBS 快照
如果从零同步一台新节点需要 2 天,根本来不及。这时候,我作为 AWS 老兵的经验派上了用场。
操作步骤:
-
制作快照 (无需停机) : 我登录 AWS 控制台,找到正在运行的 Node A (旧节点) 的数据盘卷 (
vol-xxxxxx)。这块盘是io2类型的,支持高性能热快照。-
Action : Create Snapshot
-
Description :
eth-mainnet-pre-shanghai-fork -
耗时: 因为是增量快照,大约 15 分钟就完成了。
-
-
克隆磁盘 : 用刚刚做好的快照,创建一块新的 EBS 卷 (
vol-yyyyyy)。-
Type :
io2 Block Express(必须保持高性能) -
IOPS: 10,000
-
Size: 2000 GB
-
-
启动 Node B : 启动一台新的 EC2 实例 (
r6g.2xlarge),把这块新磁盘挂载到/dev/nvme1n1。
此时的状态 : 我拥有了一台 Node B,它的硬盘里拥有 Node A 15 分钟前所有的区块链数据(包括 JWT 密钥等配置文件)。我省去了 2 天的同步时间。
🛠️ 3. 下午 2:00:在 Node B 上执行升级
现在,我有充足的时间在 Node B 上折腾,完全不影响正在跑业务的 Node A。
SSH 连接 Node B 进行操作:
-
挂载磁盘:
sudo mount /dev/nvme1n1 /data/ethereum # 确认数据都在 ls -l /data/ethereum/execution/geth/chaindata -
下载新版本客户端 : 根据公告,我们需要 Geth
v1.11.5和 Lighthousev4.0.1。# 下载并解压 Geth wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.5.tar.gz tar -xvf geth-linux-amd64-1.11.5.tar.gz sudo cp geth-linux-amd64-1.11.5/geth /usr/local/bin/geth # 下载并解压 Lighthouse wget https://github.com/sigp/lighthouse/releases/download/v4.0.1/lighthouse-v4.0.1-x86_64-unknown-linux-gnu.tar.gz tar -xvf lighthouse-v4.0.1-x86_64-unknown-linux-gnu.tar.gz sudo cp lighthouse /usr/local/bin/lighthouse -
关键验证 (Version Check): 在启动前,必须确认新程序包含了硬分叉的代码逻辑。
/usr/local/bin/geth version # Output: Version: 1.11.5-stable # Check flags: ShanghaiTime = 1681338479看到这个时间戳,我就放心了。
-
启动服务: 使用 Day 1 写好的 systemd 脚本(无需修改,因为目录结构一样)。
sudo systemctl start geth lighthouse
同步追赶: 因为快照是几小时前打的,Node B 启动后,Lighthouse 迅速连接 Peers,指挥 Geth 开始下载这几个小时落后的区块。
-
journalctl -fu geth显示:Importing new chain segment。 -
15 分钟后,日志显示
age=4s。Node B 已追平主网,且运行着最新版本。
🔀 4. 下午 4:30:无感切换 (Traffic Switch)
现在我有两套环境:
-
Node A (旧) :
172.31.20.100,正在承载 Scanner 流量。 -
Node B (新) :
172.31.20.101,已同步,空闲。
我在 Node A 上配置了 Nginx(参见 Day 4),现在只需要修改 Nginx 的后端指向,就能把流量引到 Node B。虽然这会引入一次跨机器的内网跳转(约 1ms 延迟),但为了平滑升级,这是值得的。
修改 Nginx 配置:
# /etc/nginx/sites-available/ethereum-rpc.conf
upstream geth_backend {
# [旧配置] 指向本机 (注释掉)
# server 127.0.0.1:8545;
# [新配置] 指向 Node B 的内网 IP
# 保持长连接 (keepalive) 以减少 TCP 握手开销
server 172.31.20.101:8545;
keepalive 32;
}
执行切换命令:
# 检查配置语法
sudo nginx -t
# 平滑重载 (不中断现有连接)
sudo nginx -s reload
✅ 5. 下午 5:00:验证与收尾
切换的一瞬间,我盯着三个窗口:
-
Scanner 业务日志 :一片祥和,没有
502,没有Timeout。业务方甚至没感觉到发生了变化。 -
Node B 监控:CPU 和网络流量瞬间上来,开始处理请求。
-
Node A 监控:流量归零,变成了一台安静的备用机。
后续处理: 我并没有立即销毁 Node A。
-
我登录 Node A,把它的软件也升级到了最新版。
-
我把它保留作为 Cold Standby (冷备)。
-
如果明天 Node B 所在的 AWS 可用区 (Availability Zone) 挂了,或者新硬盘坏了,我只需要把 Nginx 配置改回
127.0.0.1,一秒钟就能切回来。
今日总结: Web3 的升级比 Web2 更残酷,因为涉及"共识"。一旦掉队,就是两个世界的差别。 但运维的本质是不变的------利用冗余架构(AWS 快照 + 蓝绿部署)来对抗不确定性。
📚 Day 5 特别附录:硬分叉与网络架构详解
1. "Shanghai Upgrade" (上海升级) 是什么意思?是在上海进行升级吗?
Alen 的回答: 哈哈,不是的。这只是一个 "代号"。
-
命名规则 :以太坊的每一次重大升级,通常会用 "Devcon(开发者大会)举办过的城市" 来命名。
-
历史:
-
比如以前有 "London Upgrade"(伦敦升级,EIP-1559 燃烧机制)。
-
这次叫 "Shanghai Upgrade"(上海升级,允许信标链质押提款)。
-
下次可能叫 "Cancun Upgrade"(坎昆升级)。
-
-
本质 :这就像 Android 系统用甜点命名(安卓 8 是 Oreo,安卓 9 是 Pie),或者 macOS 用加州地名命名(Mojave, Catalina)一样。它只是指代 v1.11.5 这个版本引入的一系列新功能,跟物理地点毫无关系。
2. "确认了最终高度",是说老版本到了这个高度就必须换新版本?
Alen 的回答: 是的,绝对必须换。
-
原理:区块链的代码里其实写了一段像"定时炸弹"一样的逻辑:
if current_block_height >= 18,500,000: use_new_rules() # 使用上海升级的新规则(比如允许提款) else: use_old_rules() # 继续用老规则 -
老版本的 Geth :它的代码里没有
new_rules()这部分逻辑。 -
后果 :当区块高度达到
18,500,000时,全网都在用新规则打包区块。你的老版本 Geth 会看着新区块说:"咦?这个区块格式不对啊,我不认!" 于是,你的老节点就会拒绝同步接下来的所有新区块,或者卡死,或者走上一条错误的路。
3. "Scanner 读到假数据,用户充值到了但我们看不到"是什么意思?(通俗解释)
Alen 的回答: 这是最可怕的后果。我们用 "火车变轨" 来打比方。
-
场景:
-
区块链就像一条铁轨,所有节点都是火车,跑在同一条轨道上。
-
高度 18,500,000 是一个 "变轨点" (Switch)。
-
-
正常情况(升级了的节点):
-
到了变轨点,大家按照新地图,向 "左" 拐,继续在那条繁华的 主网 (Mainnet) 上跑。
-
用户的充值交易(比如 100 ETH),都在这条 主网 上。
-
-
异常情况(没升级的 Alen 节点):
-
到了变轨点,Alen 的老节点没有新地图,它不知道要向左拐。
-
它会直直地向 "右" 冲过去,进入一条 "废弃的岔路" (Forked Chain)。
-
关键点:这条岔路可能也会生成区块(虽然是无效的),Alen 的节点会以为自己还在正常跑。
-
-
结果:
-
用户 :在 主网(左边)充了 100 ETH。Etherscan 上显示到账了。
-
Alen 的 Scanner :连着 Alen 的老节点,盯着 废弃岔路 (右边)。废弃岔路上没有这笔 100 ETH 的交易。
-
悲剧:Scanner 告诉公司数据库:"没有充值"。用户怒了:"链上明明有钱,你们交易所吞我钱!"
-
这就是所谓的 "跑到分叉链上" ,Scanner 读到的全是那条废弃链上的 "假数据"(或者说是平行宇宙里的无效数据)。
4. Nginx 指向另一台机器 (Node B),会有网络延迟影响吗?
Alen 的回答: 你非常敏锐!这里确实有网络拓扑的变化,但在这个场景下是 可以接受的。
我们来对比一下 "不能跨机器" 和 "能跨机器" 的区别:
A. 为什么 Geth 和 Lighthouse 不能跨机器(或最好别跨)?
-
通信频率:极高。每秒钟可能交互几百次。
-
协议:Engine API。
-
敏感度 :这是共识层 。如果延迟高了,会导致验证签名超时,直接导致节点漏块。这是毫秒必争的。
B. 为什么 Nginx 和 Geth 可以跨机器?
-
通信频率:中等。Scanner 业务每秒发几百个请求查询余额。
-
协议:HTTP / JSON-RPC。
-
敏感度 :这是业务层。
-
内网延迟 :AWS 同一个 VPC 内网(同可用区),服务器 A 访问服务器 B,延迟通常在 0.5ms ~ 1ms 左右。
-
业务容忍度 :Scanner 查询一个余额,Geth 处理需要 20ms。加上 Nginx 的转发 1ms,变成 21ms。对业务来说,这 1ms 的差距完全无感。
-
结论 : 在"蓝绿部署"这种特殊升级时刻,为了保证 0 停机,引入这 1ms 的内网延迟是完全划算的交易。等升级稳定了,Alen 其实可以把 Nginx 再改回指向 Localhost,或者干脆就把 Node B 当成新的主节点用下去。