时间:入职第 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 当成新的主节点用下去。