ECS切换时磁盘和安全组双故障记录

举报
yd_272744321 发表于 2026/11/06 10:33:15 2026/11/06
【摘要】 我在华为云 ECS 做 Go 服务切换时,连续踩到数据盘挂载失败和安全组出方向规则过严两个坑,导致发布窗口从 30 分钟拖到 2 小时多。这里把报错、cloud-init 日志、修复命令、耗时和额外费用都记下来,给团队留一份可复盘的变更记录。

周二 20:05,我在华为云 cn-north-4 做内部 Go API 的 ECS 切换,变更单写的是 30 分钟窗口,结果到 22:18 才算稳定,值班群里从“准备切流”变成了“先别动我还在修”。

这次没有外部组件抖动,故障是我自己造出来的。

发布当天环境和磁盘故障

新机器是 c7.large.4,4 vCPU、8 GiB 内存,Ubuntu 22.04.4 LTS,系统盘 40 GiB ESSD,数据盘 200 GiB ESSD;服务是 Go 1.22.6 编译,容器运行时 Docker 24.0.7,镜像仓库 SWR,镜像 tag 是 api:2026.11.01-rc3,压缩包 1.9GB,解压后层大小 5.6GB。依赖是 RDS MySQL 10.23.17.12:3306 和 OBS obs.cn-north-4.myhuaweicloud.com:443,切流方式是 ELB 权重 100:0 改 0:100。

uname -a
Linux ecs-api-03 5.15.0-91-generic #101-Ubuntu SMP x86_64 GNU/Linux

docker version --format '{{.Server.Version}}'
24.0.7

cloud-init --version
/usr/bin/cloud-init 23.1.2

lsblk
nvme0n1      40G
└─nvme0n1p1  40G /
nvme1n1     200G

free -h
Mem: 7.6Gi total, 1.1Gi used, 5.4Gi free

我把风险想轻了。

切换前我只做了 5 分钟冒烟,命令是 curl -fsS http://127.0.0.1:18080/healthz、docker pull swr.cn-north-4.myhuaweicloud.com/library/busybox:1.36、nc -zv 10.23.17.12 3306 -w 2。我当时觉得三条都过就行,现在看这套检查只覆盖了“能通”,没有覆盖“通了以后拉大镜像会不会爆盘”,也没测 OBS TLS。

20:17 开始拉正式镜像,20:21 部署脚本在 docker pull 阶段连续失败三次,报错完全一样。

docker: failed to register layer: write /var/lib/docker/tmp/GetImageBlob325909813: no space left on device
failed to copy: httpReadSeeker: failed open: unexpected EOF
write /var/lib/docker/overlay2/3f89.../diff/usr/local/bin/api: no space left on device

我第一反应是 SWR 抽风,还多试了一次 docker pull --max-concurrent-downloads=1,想靠降低并发绕过去,结果 3 分 12 秒后在同一个 layer digest 再次报 no space left on device。

df -h
/dev/nvme0n1p1   40G   38G  1.1G  98% /
/dev/nvme1n1    197G  220M 187G   1% /data

docker info | grep -E 'Docker Root Dir|Storage Driver'
Storage Driver: overlay2
Docker Root Dir: /var/lib/docker

20:26 我做了一个错误尝试,先清理垃圾挤空间,命令是 docker system prune -af 和 journalctl --vacuum-size=200M。两条命令跑完回收了 742.3MB 加 318MB,系统盘可用从 1.1G 到 2.1G,但重新拉业务镜像还是失败,因为单层解压写入峰值就超过 2G,这个方向从一开始就不对。

docker system prune -af
Total reclaimed space: 742.3MB

journalctl --vacuum-size=200M
Vacuuming done, freed 318.0M of archived journals

df -h /
/dev/nvme0n1p1   40G   36G  2.1G  95% /

20:30 我才去翻 cloud-init 日志,线索其实写得很直白,是我自己前面没看。

tail -n 40 /var/log/cloud-init-output.log
cc_scripts_user.py[WARNING]: Failed running /var/lib/cloud/instance/scripts/part-001 [1]
mkfs.ext4: The file /dev/vdb does not exist and no size was specified.
mount: /data: special device /dev/vdb does not exist.

# part-001 原脚本
mkfs.ext4 /dev/vdb
mkdir -p /data
mount /dev/vdb /data

这批实例磁盘名是 NVMe,设备是 /dev/nvme1n1,我脚本里硬编码 /dev/vdb,格式化和挂载都没真正执行,Docker data-root 也没迁过去,容器层一直写系统盘。

20:34 我按顺序停容器、停 Docker、重建挂载,再迁移目录。这里每一步我都改了原因。把 mkfs.ext4 /dev/vdb 改成 mkfs.ext4 -F /dev/nvme1n1 是为了匹配 NVMe 设备;/etc/fstab 不再写设备名而是写 UUID,避免重启后盘序变化;/etc/docker/daemon.json 新增 data-root 指向 /data/docker,防止 Docker 默认回写 /var/lib/docker。迁移前 du -sh /var/lib/docker 是 16.8G,rsync -aP 实测 8 分 41 秒,平均吞吐 110MB/s。

sudo systemctl stop docker
sudo blkid /dev/nvme1n1
# UUID="8d5f2b4d-7f4f-4e1a-8f3c-3f1d5a4e2a6b"

sudo mkfs.ext4 -F /dev/nvme1n1
sudo mkdir -p /data
sudo mount /dev/nvme1n1 /data
sudo rsync -aP /var/lib/docker/ /data/docker/

# /etc/fstab
UUID=8d5f2b4d-7f4f-4e1a-8f3c-3f1d5a4e2a6b /data ext4 defaults,nofail 0 2

# /etc/docker/daemon.json
{
  "data-root": "/data/docker",
  "log-driver": "json-file",
  "log-opts": {"max-size":"100m","max-file":"3"}
}

sudo systemctl daemon-reload
sudo systemctl start docker

这里我返工了两次。

第一次返工是我手快把 daemon.json 少写了一个右花括号,systemctl start docker 直接失败,journalctl -u docker -n 20 里报 invalid character '\n' in string literal;第二次返工是我忘了把旧目录改名,Docker 重启后仍然读到旧缓存,后来我执行 mv /var/lib/docker /var/lib/docker.bak.$(date +%s) 再重启才干净。20:49,docker info 显示 Docker Root Dir: /data/docker,镜像拉取恢复。

安全组故障、费用和还没做完的事

容器起来后 ELB 对 18080 的健康检查是绿的,但业务接口还是 500,日志里两类错误交替刷屏。

2026/11/01 20:57:13 db ping failed: dial tcp 10.23.17.12:3306: i/o timeout
2026/11/01 20:57:14 obs put failed: Post https://obs.cn-north-4.myhuaweicloud.com: net/http: TLS handshake timeout

nc -zv 10.23.17.12 3306 -w 2
Ncat: TIMEOUT.

curl -I https://obs.cn-north-4.myhuaweicloud.com --connect-timeout 3
curl: (28) Connection timed out after 3001 milliseconds

我给新 ECS 绑定了新建安全组 sg-api-tight-20261101,入方向只放了 ELB 子网 10.23.8.0/24:18080,出方向我只留 10.0.0.0/8 的 TCP,自以为内部依赖都在 VPC。实际 OBS 走公网 443,直接被我拦了;数据库还踩了更低级的坑,我把网段写错了。

# 21:03 第一次修正
Egress add: TCP 443 -> 0.0.0.0/0
Egress add: TCP 3306 -> 10.23.7.0/24   # 手滑写成 7

# 验证
nc -zv 10.23.17.12 3306 -w 2
Ncat: TIMEOUT.
curl -sS -o /dev/null -w '%{http_code}\n' https://obs.cn-north-4.myhuaweicloud.com
405

OBS 在 21:04 就恢复,DB 还断着。我到 21:10 才在控制台审规则看出 CIDR 写错,改成 10.23.17.0/24,21:12 接口返回码回到 200,QPS 从 0 爬到 320,错误率峰值 96.4%,受影响最重的是下单和附件上传。故障有效区间是 20:25 到 21:12,一共 47 分钟。

费用我也记了,旧新 ECS 重叠运行从 20:05 到 22:18,按 c7.large.4 按需价折算 4.73 元;误挂的 200G ESSD 多留了 4 天,17.92 元;排障时做了 1 份 200G 快照留证据,1.08 元,总额外成本 23.73 元。钱不多,但把值班同事拖在会议里两个多小时,这个账更难看。

我现在还没做完两件事。cloud-init 模板还不是幂等,重复执行会覆盖 /etc/docker/daemon.json;安全组还在控制台手工点,容易再手滑。Terraform 分支我已经起了,资源准备收敛到同一模块,变量固定成 rds_port=3306、obs_port=443、smn_port=443、dns_port=53,并且在切流前加硬检查,不通过就中断。

[ -b /dev/nvme1n1 ] || { echo 'disk missing'; exit 1; }
findmnt /data >/dev/null || { echo '/data not mounted'; exit 1; }

docker info | grep -q '/data/docker' || { echo 'docker root wrong'; exit 1; }

nc -zvw2 10.23.17.12 3306 || { echo 'db unreachable'; exit 1; }
curl -sS --connect-timeout 2 https://obs.cn-north-4.myhuaweicloud.com >/dev/null || { echo 'obs unreachable'; exit 1; }

回切演练我还没做,Terraform 规则和现网手工规则混用会不会互相覆盖,这个要在下周三晚班前先在预发跑一遍。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。