Compiler Explorer 如何在 2026 年运行在 AWS 上
摘要
Matt Godbolt 解释了 Compiler Explorer 在 2026 年如何运行在 AWS 上,涵盖 CloudFront、负载均衡、自动扩展集群以及使用 Terraform 的基础设施即代码。
<p><a href="https://lobste.rs/s/1eie3y/how_compiler_explorer_runs_on_aws_2026">评论</a></p>
查看缓存全文
缓存时间: 2026/08/05 18:00
# Compiler Explorer 如何在 2026 年运行在 AWS 上 — Matt Godbolt 的博客
来源:https://xania.org/202608/how-compiler-explorer-runs-on-aws
本文借助 LLM 辅助撰写。详情见文末。
我一直想写一篇关于 [Compiler Explorer](https://godbolt.org/) 到底如何在亚马逊云上运行的最新文章,这个念头已经在我的清单上躺了很久,排在其他一些随机事项后面——那些事项占用了被我可笑地称为“业余时间”的东西¹(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:list)。
上次我写这个话题是在 [2016 年](https://xania.org/201609/how-compiler-explorer-runs-on-amazon),当时整个站点就是一个负载均衡器、几个实例,以及一些我在笔记本电脑上构建的 Docker 容器。去年夏天我写了一篇更长的 [工作原理说明](https://xania.org/202506/how-compiler-explorer-works),但那篇主要是讲 Compiler Explorer 本身,只是顺带提到它所在的云环境。
我们在 AWS 上运行这件事不是什么秘密,而且一切都公开可见:[infra 仓库](https://github.com/compiler-explorer/infra) 里有全部 Terraform 配置、安装脚本,以及我们用来驱动整个系统的 `ce` 命令行工具。所以如果你更想读真实代码而不是我的描述,请随意。因此这篇文章换个角度,沿着我们依赖的亚马逊服务来写,大致按照你的编译请求遇到它们的顺序。
### 让你的代码抵达我们的服务器
你的浏览器会与 [CloudFront](https://aws.amazon.com/cloudfront/)(亚马逊的 CDN)通信。我们运行两个独立的 CloudFront 分发。位于 `godbolt.org` 前面的那个主要只是把请求转发给我们的负载均衡器,在合理范围内进行缓存,并在返回时压缩内容²(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:brotli)。体积庞大的静态资源则存放在 S3 存储桶中,由第二个分发(`static.ce-cdn.net`)提供:包括编译后的 JavaScript、图片、网页字体等。其中最大的一部分是 [Monaco](https://github.com/microsoft/monaco-editor)——来自 Visual Studio Code 的编辑器组件,它为你提供语法高亮、波浪下划线以及其他功能。对于只想看汇编的人来说,要传输这么多 JavaScript 实在太多,因此值得缓存到离他们近的地方。
在这个分发前面是 [WAF](https://aws.amazon.com/waf/),负责我们的速率限制。我们的限制*非常*简单,也*非常*高,主要是因为以前限制更严格时,总是误伤 C++ 培训师:一个教室在会议 NAT 后面整体看起来就像一个非常活跃的 IP 地址。我们也许可以用指纹识别来做些更聪明的事,但调高限制更简单,而且从那以后也没出过问题。³(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:bans)
### 一个负载均衡器后面有一大堆实例组
CloudFront 后面是一个 [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/)(应用负载均衡器)。它根据路径判断请求属于哪个集群,然后从该集群中挑选一个健康的实例来处理。几乎所有请求都没有特殊前缀,都会进入生产环境实例组,这也是绝大多数流量的去向。`/beta*` 和 `/staging*` 分别进入 beta 和 staging 实例组⁴(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:envs)。`/winprod*` 进入一组运行 [MSVC](https://learn.microsoft.com/en-us/cpp/build/reference/compiling-a-c-cpp-program) 的 Windows 实例;`/aarch64prod*` 进入 [Graviton](https://aws.amazon.com/ec2/graviton/) 机器,这些机器原生运行 ARM 代码而不是在模拟器下运行;`/gpu*` 进入装有真正 NVIDIA 显卡的机器⁵(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:gpu)。
每一个这样的实例组就是一个 [Auto Scaling Group](https://aws.amazon.com/ec2/autoscaling/)(自动扩缩组),如今每个组都双份配备:蓝组和绿组。部署时,我们会启动另一种颜色,等待它变健康,然后把负载均衡器的目标组指向它,并排空旧组。如果出了问题,就再切回去。在此之前,部署意味着用我们的 `ce` 命令行工具对整个实例组做滚动重启,一次一个实例。那样做效果还行,但要回滚一个糟糕的发布版本,就得把整个实例组再滚动升级回上一个版本,当你刚刚搞挂站点时,这速度实在有点慢。
扩缩容本身很简单:我们只是尽量让平均 CPU 负载保持在阈值以下。我们也讨论过更复杂的方法,但这个方法开箱即用⁶(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:queuescale)。
### 租用没人想要的算力
生产环境实例组几乎全部使用 [spot 实例](https://aws.amazon.com/ec2/spot/)——闲置的 EC2 容量,以低价出售,前提是你在两分钟通知内可能被回收。相比按需实例能省 60-90%——对我们来说可是一大笔钱。
我们能做到这一点,是因为我们的实例其实无关紧要。所有需要持久保存的东西都放在共享文件系统、S3 或 DynamoDB 里,所以一个实例消失,只是被替换掉的另一个实例而已。我们在 `m5`、`m6`、`m7`、`r6` 以及 `i3`/`i4i` 家族中申请了十六种不同的实例类型。分配策略是 `price-capacity-optimized`——亚马逊的说法是“在这些里挑最便宜且最不容易被回收的”。实际上,我们被回收后,扩缩组会发现并替换一个新的。
### 为什么编译器才是难点
我们有大约 6,000 个编译器条目⁹(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:double),覆盖 93 种语言,而且我们从不删除其中任何一个⁷(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:never)。一旦某个编译器版本上线,它就会一直保留,因此那个展示 GCC 4.8 代码生成怪癖的 Stack Overflow 回答到今天仍然可以编译。这是我们对链接失效的一种对抗,也确实意味着我们囤积了大量二进制文件。
所有这些都存放在 [EFS](https://aws.amazon.com/efs/)(亚马逊的弹性 NFS)上,而 NFS 有延迟。C 系语言会引入海量非常小的头文件,所以朴素的做法慢得无法使用。我们的解决办法听起来有点蠢:为每个编译器构建一个 [SquashFS](https://docs.kernel.org/filesystems/squashfs.html) 镜像,把镜像*也*存放在 EFS 上,然后通过 loopback 设备挂载。这样内核会认为自己是在与本地块设备通信,能够正确缓存块,而不是每次都去另一个建筑里的服务器询问¹⁰(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:squash)。
这个方法有效,但它意味着每次启动都要挂载几千个镜像,这大约需要一整分钟,并且会把成千上万个文件系统的元数据留在内核内存里。这些缓存的元数据本身就是 SquashFS 优于 NFS 的主要原因之一,所以也不能说完全浪费;只是任何一个实例实际上只会用到其中少数几个编译器,我们却在为使用一小部分而支付全部的成本。我花了三年时间,经历了几次半途而废的尝试,最终答案是 [CEFS](https://xania.org/202509/cefs):内容寻址镜像,打包成约 20GB 的 bundle,由 [autofs](https://www.kernel.org/doc/html/latest/filesystems/autofs.html) 在首次访问该路径时按需挂载。这次迁移把镜像数量从 2,182 个减少到 121 个,操作系统启动时间从 50 秒降到 20 秒!此后又慢慢回升到大约 883 个,因为每晚的构建会不断产生新镜像,而合并只压缩已经存在的内容。在我写这篇文章时运行的一次垃圾回收找到了 111 个不再被任何东西引用的镜像,大约 63GiB⁸(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:gc)。
我们的存储空间今年也大幅下降,不过老实说我不太确定其中多少归功于 CEFS,多少归功于我终于删掉了旧的 squash 镜像:
```
Aug/04 01:55 admin-node~ $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p1 97G 20G 77G 21% /
fs-db4c8192.efs.us-east-1.amazonaws.com:/ 8.0E 2.2T 8.0E 1% /opt
```
2.2T,而一年前是 3.9T。当然距离 8 EB 还差得远。
### 每晚彻夜构建编译器
我们每晚从零开始构建一大堆编译器:GCC trunk、Clang trunk,以及一大串针对 reflection、contracts、coroutines 和其他有趣特性的实验分支。目前有 94 个夜间构建任务¹¹(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:jobs),比一年前的 73 个和 2022 年的 33 个都多。
这些任务运行在我们自己的 [GitHub Actions](https://github.com/features/actions) runner 上,这些 runner 架设在 [EC2](https://aws.amazon.com/ec2/) 上,按需启动,使用了出色的 [terraform-aws-github-runner](https://github.com/github-aws-runners/terraform-aws-github-runner),而上面所有编译器编排工作都是我们自己的。构建 trunk 版 LLVM 需要一台大机器和不少时间,而 GitHub 托管的 runner 在我们搭建这套系统时根本应付不来。
这是账单中每次有人要求再加一个编译器而我们答应时就会上涨的部分¹²(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:stale)。
### 那些还没完全做完的部分
我们构建但尚未完全推出的一样东西叫做 CE Router:一个小型实例组,其职责是决定编译*应该在哪里*进行,在 [DynamoDB](https://aws.amazon.com/dynamodb/) 中查找答案,把请求放到 [SQS](https://aws.amazon.com/sqs/) 队列上,结果通过 [API Gateway](https://aws.amazon.com/api-gateway/) WebSocket 返回你的浏览器。这样可以避免响应 HTTP 请求的机器必须是持有该编译器的机器。
它能工作,但还没有承载生产流量:会把编译请求送入它的负载均衡器规则仍然被注释掉,我们正在逐步迁移。你今天发起的编译仍然直接进入生产实例组中的一台机器,和过去一直一样。
不过在构建它的过程中确实遇到了一个 AWS 的坑:API Gateway WebSocket 帧上限为 32KiB,而几乎任何非平凡程序的汇编输出都会超过这个大小;反方向上 SQS 消息上限为 256KB,一个稍大的多文件项目就会超过。所以在两个方向上,如果内容太大,我们就把它塞进 S3,然后传一个键过去。
### 其他一切
还有一些零散的服务,各司其职:
- **[DynamoDB](https://aws.amazon.com/dynamodb/)** 保存短链接。你放进幻灯片里的每一个 `godbolt.org/z/...` 都是表里的一行,这样的行有几百万。
- **[S3](https://aws.amazon.com/s3/)** 存放我们构建的编译器、静态资源、日志,以及一个按天过期、内容寻址的编译缓存,这样第二个编译相同内容的人可以直接免费拿到结果。
- **[Lambda](https://aws.amazon.com/lambda/)** 负责杂活:[Claude Explain](https://xania.org/202505/ai-and-compiler-explorer) 后端、夜间版本跟踪,以及把 CloudWatch 告警推送到我们的 Discord 的那个任务,这样凌晨 3 点就不是只有我的手机在震动了。
- **[Route 53](https://aws.amazon.com/route53/)**、**[ACM](https://aws.amazon.com/certificate-manager/)**、**[CloudTrail](https://aws.amazon.com/cloudtrail/)**、**[Backup](https://aws.amazon.com/backup/)** 和 **[SES](https://aws.amazon.com/ses/)** 都是管道设施。我不会怎么想它们。
- **[CloudWatch](https://aws.amazon.com/cloudwatch/)** 驱动自动扩缩触发,不过实际查看指标时,我们运行的是 [Grafana](https://grafana.com/)、[Prometheus](https://prometheus.io/) 和 [Loki](https://grafana.com/oss/loki/)。这些看板是[公开的](https://stats.compiler-explorer.com/)。
所有东西都在 `us-east-1`。如果你从悉尼编译,你的代码要绕很长的路,而且恐怕还会继续这样:让几 TB 的编译器跨区域保持同步绝对是一场噩梦。
### 一些数字
每次编译我们都会记录一条高度匿名的 JSON 记录到 S3,上面架了一个 [Glue](https://aws.amazon.com/glue/) 表,所以写这篇文章时我真的去认真数了一下:**7 月有 5,238,210 次编译**,过去**十二个月总计 7,870 万次**。这一数据全年都在缓慢下滑,比去年秋天的峰值下降了大约三分之一。
每月编译量图表,从 2025 年 8 月到 2026 年 7 月,2025 年 10 月达到峰值 7.77 百万,2026 年 7 月降至 5.24 百万(https://xania.org/202608/compilations-per-month.svg)过去一年的每月编译量。由[此脚本](https://xania.org/202608/generate_compilation_chart.py)生成。
在那之前的大幅下降——从 2024 年每月 1400 万——我至少能解释一半:2024 年年中,我们把边输入边自动重新编译的默认延迟从 750ms 加倍到 1500ms,然后又增加到 2 秒¹³(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:delay)。如果你持续输入,把延迟加倍大约会使你产生的编译次数减半,而几乎没有人会察觉。不过最近这次下降不是这个原因:它是在一年多之后才开始的。
所以我没有真正靠谱的解释,也不容易去找到原因,因为我们刻意[不追踪你是谁](https://godbolt.org/#privacy):没有 cookie,没有任何能让我告诉你昨天有多少人使用站点的信息。我愿意每次都做同样的取舍,但这确实会让我偶尔盯着图表发愁。
既然查询已经打开了,这里看看大家在 7 月实际上编译了什么:
```
id compiles compiler
g161 1,386,532 GCC 16.1 (C++)
cg161 403,659 GCC 16.1 (C)
gsnapshot 298,256 GCC trunk
clang_trunk 287,885 Clang trunk
g152 239,331 GCC 15.2
clang2210 177,958 Clang 22.1
vcpp_v19_latest_x64 132,186 MSVC
r1970 65,380 Rust 1.97
python314 51,104 Python 3.14
```
最顶上那条占了**26%**——这差不多是你作为默认选项时预期的比例¹⁵(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:top10)。负载均衡器每月大约看到 1400 万个请求;今年最忙的一天是 4 月 21 日,有 144 万个请求。自动扩缩自己就处理了那一天;我是在写这篇文章时才发现这件事的。
目前所有这些在 AWS 上每个月大约花费我们 $3,600 美元,折算下来每次编译约 $0.0007¹⁴(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:percomp)。在将近一年的时间里,我们几乎没有为这些付过钱:AWS 的[开源项目信用额度](https://aws.amazon.com/blogs/opensource/aws-promotional-credits-open-source-projects/)几乎覆盖了全部费用。这些额度在春天用完了,我们自己付了几个月,然后 AWS 刚刚又续了一年,这是巨大的帮助。无论如何,我们在资金上都没问题,这要感谢我们的 [Patreon](https://patreon.com/mattgodbolt) 支持者、[GitHub 赞助者](https://github.com/sponsors/mattgodbolt)和[商业赞助商](https://godbolt.org/#sponsors)。我去年做过一份[完整的成本明细](https://xania.org/202506/compiler-explorer-cost-transparency),现在大致结构仍然不变。
可视化显示 93 种语言中的 6,157 个编译器(https://xania.org/202608/compiler-wall-dynamic.svg)一年后的编译器墙:93 种语言中的 6,157 个条目。与去年[相同的脚本](https://xania.org/202608/generate_compiler_wall.py)重新生成。
### 我仍然想修复的问题
部署比过去好了,但其中仍然有比我想要的更多的人工干预¹⁶(https://xania.org/202608/how-compiler-explorer-runs-on-aws#fn:
相似文章
@heygurisingh: 卧槽……一个团队刚刚开源了一个 AWS 模拟器,仅用 13 MiB 内存就能在笔记本上运行整个云服务。……
Floci 是一个新开源的轻量级 AWS 模拟器,仅用 13 MiB 内存即可在笔记本上运行 45 项服务,为 LocalStack 等基于 Docker 的工具提供了更快速、更节省资源的替代方案。
10,000 Lines Later: When a Tool Became a Compiler - Rob Durst - Gleam Gathering 2026
Rob Durst 在 Gleam Gathering 2026 上分享了如何用 Gleam 将 YAML-to-Terraform 的配置工具重写为编译器,并从中体会到类型驱动设计和解码器模式的力量。
论构建可扩展的控制平面
AWS工程师Zak van der Merwe分享了他14年来为EC2和DSQL构建控制平面的见解,讨论了大规模运行基础设施时面临的分布式系统挑战。
@larsencc: https://x.com/larsencc/status/2053862900289470765
本文详解了开源 browser-use 库的生产架构,阐述了如何利用 AWS Lambda、SQS 和 S3 扩展浏览器代理,实现状态管理与重试机制。
@jhleath: https://x.com/jhleath/status/2065408690992148698
作者解释了如何构建一个能够在恒定时间内每秒启动数百万个沙箱的计算平台,重点介绍了使用Cassandra和S3进行解耦调度和能力聚合。