
AWS T3实例默认无限CPU积分模式导致账单飙升
来源标题:My Server Has No SSH Port. It Also Had No Disk Space
AWS T3实例默认以无限CPU积分模式启动,超出基准性能后不降速而是额外收费,导致一台运行静态网站的t3.micro月度账单从预计的约12美元涨到33至37美元。9月出现三次CPU积分费用飙升,累计总花费达101.81美元。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
一篇没人要求的后续,主角是 t3.micro、一块塞满的磁盘,以及把它吃掉的 IDE。
六月的时候我写过一篇文章,讲我关掉个人服务器上的 22 端口,改用身份代理登录。没什么可扫描的,没什么可暴力破解的,每个会话都有日志。我当时还挺得意。
而这一篇讲的是:屋子里堆满了箱子,然后我发现,一栋没有门的房子,想再进去也出奇地难。
配置很小。斯德哥尔摩的一台 t3.micro,Ubuntu,10 GB 磁盘,前面是宿主机的 nginx,后面挂着几个 nginx:alpine 容器提供静态 HTML。我拿餐巾纸一算,大概每月 12 美元。
Cost Explorer 说九月是 33 美元,十月还差四天就已经 37 美元了。
一个静态网站总共花了 101.81 美元。月均 7.83 美元——就是那种会把问题藏起来的平均数。
这些费用全被抵扣额度吸收了,这也是这篇帖子还能当笑话讲的原因。
Cost Anomaly Detection 其实早就抓到了:九月有三次尖峰,全都出现在一个叫 EUN1-CPUCredits:t3 的用量类型上。翻译一下:T3 实例默认以 unlimited credit 模式启动。一旦跑在基线之上(micro 每个 vCPU 是 10%),AWS 不会限流,而是直接计费。悄无声息地,按 vCPU-小时计。
另外我发现,我的异常告警订阅设成了“$100 且 40%”。在一个每月只花 12 美元的账户上,这个告警大约要等账单翻倍、然后再买条船的时候才会触发。现在改成 3 美元了。
aws ec2 modify-instance-credit-specification
--instance-credit-specification "InstanceId=i-xxxxxxxx,CpuCredits=standard"
standard 模式会限流,而不是计费。你的网站先变慢,你的银行卡后变难过。
于是真正的问题来了:一个静态网站到底在拿 CPU 干什么?
我还在琢磨这件事的时候,SSM 不让我进了。
$ aws ssm start-session --target i-xxxxxxxx Starting session with SessionId: jim-xxxxxxxx SessionId: jim-xxxxxxxx : Plugin with name Standard_Stream not found. Step name: Standard_Stream
agent 报告 Online。Run Command 失败。重启也没用。错误提到一个插件,Ubuntu 上的 agent 是以 snap 形式发布的,snap 会自动更新……显然是 agent 更新坏了。我和我的 AI 助手立刻达成一致——事后想想,这本来应该让我警惕。我们写了一个干净利落的小 user-data 脚本把 snap 回滚,我停了实例,把脚本贴进去,再启动。
EC2 → Monitor and troubleshoot → Get system log。两百行 systemd 欢天喜地,然后是 cloud-init:
[ 8.066740] cloud-init[529]: /var/log/cloud-init-output.log: No space left on device (os error 28) [ 8.162302] cloud-init[529]: main.py[ERROR]: failed stage init [ 8.170980] cloud-init[529]: OSError: [Errno 28] No space left on device ... [FAILED] Failed to start docker.service - Docker Application Container Engine. ... 2026/10/27 17:59:10Z: Amazon SSM Agent v3.3.4793.0 is running
最后一行是我最喜欢的。agent 在跑。它只是什么都干不了。
磁盘满了。不是快满了。是满到 cloud-init 都没法把我的恢复脚本写到磁盘上再执行。满到 SSM 的会话 worker 建不了自己的临时文件——而它对此的汇报,自然就是“缺了个插件”。Docker 也没能启动,所以网站正对着零个观众返回 502,每月还花 39 美元。
先读日志,再写修复。我知道这个道理。我教这个。算了。
最直接的做法是换块更大的盘。EBS 卷支持在线扩容,所以从 10 GB 到 20 GB 只要点一下,cloud-init 会在下次启动时扩展文件系统。
只不过 cloud-init 在磁盘满的时候会先崩溃,根本走不到扩展文件系统那一步。这台机器没法自救,因为自救需要几个字节的空闲空间。
于是就有了最古老的招数:救援实例。
sudo growpart /dev/nvme1n1 1 sudo mount /dev/nvme1n1p1 /mnt/old sudo resize2fs /dev/nvme1n1p1 # online grow, no fsck dance sudo du -xh /mnt/old --max-depth=3 | sort -h | tail
结果出来了:
404M /mnt/old/var/log 1.3G /mnt/old/usr/lib 2.5G /mnt/old/usr 4.5G /mnt/old/home/ubuntu/.vscode-server 8.6G /mnt/old
磁盘一多半都是 VS Code Remote server:一个 Node 运行时、每个扩展,还有几个旧版本,出于情怀留着。我把编辑器连到这台机器上过几次,为了“快速改点 HTML”。那个 HTML 文件是 14 KB。用来编辑它的工具是 4.5 GB,而且我相当确定,它在 1 GB 机器上跑的那些 Node 进程,正是烧掉 CPU 额度的元凶。
所以我的静态网站花了三倍的钱,然后把自己锁在了门外,而罪魁祸首就是我用来照看它的编辑器。
一句诚实的脚注:我手滑把卸载命令打错了($ $ sudo umount 不是命令),所以我在卸载之前就停掉了救援实例。正常关机才会卸载并刷写所有内容。挂载着 ext4 卷就直接分离,就是一个糟糕的夜晚变成糟糕的周末的方式。
重新以 /dev/sda1 挂载,启动,curl:200 OK。没人注意到它挂过,这很能说明我的流量状况。
清理:删掉 .vscode-server,清理并限制 journal 大小,apt-get clean,让 snapd 少留几个旧版本,把我的 GoAccess 面板从每 60 秒一次改成每 15 分钟一次。它每次运行都会重新解析整个访问日志,而这么小众的受众,没人需要按分钟的分析。
/etc/systemd/journald.conf.d/size.conf
[Journal] SystemMaxUse=200M
然后加了个告警,因为“磁盘满了,我是从 SSM 报错里知道的”不算监控策略。一个 systemd timer 每小时跑一次,超过 80% 就通过 SNS 发邮件给我,每天最多一次(冷却逻辑这里省略了):
usage=$(df --output=pcent / | tail -1 | tr -dc '0-9')
(( usage < THRESHOLD )) && exit 0
aws sns publish --topic-arn "$TOPIC_ARN"
--subject "Disk ${usage}% full on $(hostname)"
--message "$(df -h /; du -xh / --max-depth=2 2>/dev/null | sort -h | tail)"
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。