默认在 Docker 中运行 QEMU/KVM,Compiler Explorer(CE)在 Ubuntu guest 内以非 root 用户运行,并用 nsjail 隔离编译。Kata Containers 是备选路径。部署本身不提供认证和 TLS,入口应交给外部 nginx。
nginx → 127.0.0.1:10240 → QEMU/KVM → CE + nsjail
├─ /opt/compiler-explorer(宿主工具链,只读)
├─ /var/lib/compiler-explorer/storage(短链接,持久化)
└─ /mnt/ce-repo(配置与装配脚本,只读)
宿主机需要 Docker Compose 和 /dev/kvm;运行完整工具链更新还需要 python3(解析 Lean release)和 zstd(解压 Lean .tar.zst)。若宿主机本身是 VM,需开启嵌套虚拟化。
cp .env.example .env
# 将 CE_COMPILERS_ROOT 改为宿主机绝对路径。
scripts/update-toolchains.sh
docker compose up -d --build
docker compose logs -f首次启动会下载 Ubuntu 26.04 云镜像,并在 guest 内安装 Node、nsjail 和 CE。健康后验证:
curl http://127.0.0.1:10240/api/compilers外部代理可使用 nginx/ce.conf,部署前修改 server_name。
本部署有五个独立层次,不能把“重建 Docker”和“重建 VM”混为一件事:
| 层次 | 内容 | 什么时候需要或会重建 |
|---|---|---|
QEMU Docker 镜像 ssct/ce-qemu:local |
QEMU、curl、socat 等运行环境 | 仅 vm/Dockerfile 或其中安装的软件变化时需要重新 build;普通脚本和配置通过 bind mount 提供,不在镜像内 |
Docker 容器 ce-vm |
端口、挂载、资源限制和传给入口脚本的环境变量 | compose.yaml 或相关 .env 参数变化时需要 recreate;recreate 不会删除 named volume |
Ubuntu 基础镜像 base.img |
未装配 CE 的 Ubuntu 26.04 cloud image | 文件不存在,或 VM_IMAGE_URL、VM_IMAGE_SHA256、VM_IMAGE_SHA256_URL 变化时自动重新下载;来源标记缺失也会重新下载 |
CE overlay ce-vm.qcow2 |
Node、nsjail、CE checkout、npm 依赖和构建结果 | 装配指纹变化、上次装配失败、基础镜像缺失或收到新的强制令牌时自动重建 |
短链接存储 ce-shortlinks |
storageSolution=local 生成的短链接内容 |
独立于 overlay;只有显式删除 volume 时才会删除 |
基础镜像和 overlay 都位于 Docker volume ce-vm_vm-disk。短链接默认位于独立 Docker volume ce-shortlinks,工具链位于宿主机 CE_COMPILERS_ROOT;两者都不属于 VM 磁盘,因此重建 overlay 不会删除短链接或重新下载工具链。
CE 的 local storage 仍写入上游目录 lib/storage/data,但 QEMU guest 中该目录会链接到 /var/lib/compiler-explorer/storage。后者通过 9p 映射到独立 Docker volume:
CE lib/storage/data → guest /var/lib/compiler-explorer/storage
→ QEMU /share/storage
→ Docker volume ce-shortlinks
如需将内容直接放在可备份的宿主目录,在 .env 中设置绝对路径:
CE_STORAGE_ROOT=/srv/ce/storage该目录应专供 CE 使用,并支持扩展属性(xattr)。由于 QEMU 容器丢弃了全部 capabilities,bind mount 应在启动前执行 sudo install -d -o root -g root -m 0700 /srv/ce/storage;guest 权限由 9p mapped-xattr 映射为 uid/gid 10001。若不设置,默认使用 named volume;也可用 CE_STORAGE_VOLUME_NAME 修改 volume 名称。
首次从旧版本切换时,VM 装配输入变化会重建 overlay。若旧部署中已经有需要保留的短链接,必须在首次重启前将 guest 的 /opt/ce/lib/storage/data/ 复制到新的 CE_STORAGE_ROOT,或导入 ce-shortlinks volume。
docker compose restart qemu 只重启现有容器:不会重新 build 镜像、不会重新读取 Compose 环境、不会删除磁盘。仓库挂载内容会立即可见,入口脚本会在容器启动时判断是否需要重建 overlay。
以下变化只需要 recreate 容器,不需要重建 QEMU Docker 镜像:
- VM CPU、内存、Docker 资源限制或端口变化。
.env中传入容器的变量变化。- Compose 的挂载或安全设置变化。
docker compose up -d --force-recreate qemu只有 vm/Dockerfile 或其中的软件依赖变化时才需要同时 rebuild Docker 镜像:
docker compose up -d --build --force-recreate qemu基础镜像不是本地构建的,而是下载并校验的。修改镜像 URL 或校验参数后,需 recreate 容器让新环境变量生效;入口脚本随后自动替换基础镜像,并同时重建依赖它的 overlay。
只有明确需要删除全部 VM 磁盘、基础镜像缓存和默认短链接 volume 时才执行:
docker compose down -v使用 CE_STORAGE_ROOT bind mount 时,down -v 不会删除宿主目录中的短链接。
以下任一变化都会在下次 QEMU 启动时自动重建 overlay,并完整重装 Node、nsjail、CE,重新执行 npm ci、webpack 和 TypeScript 编译:
CE_REF、VM_DISK_SIZE、NODE_VERSION或NODE_SHA256变化。- 注入的 SSH 公钥内容变化。
vm/cloud-init/meta-data或vm/cloud-init/user-data变化。vm/provision-ce.sh、vm/setup-nsjail-cgroups.sh、vm/ce.service或scripts/apply-ce-patches.sh变化。vm/patches/*.patch新增、删除或内容变化。- 上次装配没有通过 CE 健康检查,缺少完成标记。
- Ubuntu 基础镜像被替换或丢失。
- 使用尚未执行过的
FORCE_REPROVISION令牌。
FORCE_REPROVISION="$(date +%s%N)" docker compose up -d --force-recreate qemu以下变化不会重建 overlay:修改 config/*.local.properties 或 P4 的 *.local.properties.template、更新外部工具链、调整 VM CPU/内存、普通重启、仅 recreate 容器或修改短链接 volume 中的内容。配置变化只需重启 ce.service;工具链更新脚本也只重启 CE。
guest 只读取仓库的 config/、scripts/ 和 vm/,不会读取 .env 或 .git。
| 修改内容 | 最小操作 | 会重建什么 |
|---|---|---|
| README 或其它纯文档 | 无 | 无 |
nginx/ce.conf |
检查配置并 reload nginx | 无 |
config/*.local.properties 或 P4 配置模板 |
重启 ce.service |
仅 CE 进程,不重建 Docker 或磁盘 |
CE_COMPILERS_ROOT 中的工具链内容或 *-latest 软链 |
重启 ce.service;工具链更新脚本会自动处理 |
仅 CE 进程 |
vm/sync-ce-config.sh |
重启 ce.service |
仅 CE 进程 |
vm/entrypoint.sh |
docker compose restart qemu |
重启容器和 guest;是否重建 overlay 由新入口逻辑判断 |
| VM CPU/内存、端口、Docker 资源限制或工具链挂载路径 | docker compose up -d --force-recreate qemu |
仅 recreate 容器,不重建镜像或磁盘 |
.env 中的 CE_REF、Node、磁盘大小或 SSH 公钥配置 |
docker compose up -d --force-recreate qemu |
自动重建 overlay,并重新构建 CE |
| cloud-init、装配脚本、CE unit 或 patch | docker compose restart qemu |
自动重建 overlay,并重新构建 CE |
vm/Dockerfile 或 QEMU 镜像依赖 |
docker compose up -d --build --force-recreate qemu |
重建 Docker 镜像并 recreate 容器;磁盘默认保留 |
| Ubuntu 镜像 URL 或校验参数 | recreate QEMU 容器 | 自动替换 base.img 并重建 overlay |
重启 CE 的管理命令为:
ssh -i "$CE_VM_SSH_KEY" -p "${CE_VM_SSH_PORT:-2223}" \
ce@127.0.0.1 'sudo systemctl restart ce.service'若未配置 SSH 管理密钥,可执行 docker compose restart qemu,代价是整个 guest 会重启,但正常情况下不会重建 overlay。
| 内容 | 命令 |
|---|---|
| 全部标准工具链 | scripts/update-toolchains.sh [Lean版本号|latest] |
| Clang/LLVM | scripts/toolchains/update-clang.sh |
| GCC | scripts/toolchains/update-gcc.sh [x86_64|riscv64|all] |
| Lean 4 | scripts/toolchains/update-lean4.sh [版本号|latest] |
| 自研 P4 工具链 | scripts/toolchains/deploy-p4.sh <p4mlir-yyyyMMddHHmm-buildNumber-commit.tar.zst> |
| CE 本体 | scripts/update-ce.sh gh-<release> |
工具链使用版本目录和相对 *-latest 软链。更新器会从真实二进制读取版本并同步 CE 配置,因此版本升级产生配置 Git diff 是预期行为。统一入口在全部更新后只重启一次 CE。
P4 工具链默认使用 P4_TOOLCHAIN_RETENTION_DAYS=7:每个自然日只保留时间戳最新的 build,保留最新构建日期起 7 天内的 dated build,再用旧格式 build 填满最多 7 个真实目录。p4-latest 只是别名,不计入数量;设为 0 时不限日期和总数,但仍执行每日去重。部署脚本会将实际策略写入工具链根的 .p4-retention-days,供 VM 内的配置生成器读取。
Jenkins 与部署机分离时,自研工具链的 CI 自动发布流程(最小权限用户、密钥模型、Jenkinsfile)见 docs/jenkins-toolchain-deploy.md。
若 .env 配置了管理密钥,更新器会通过 SSH 重启 CE:
ssh-keygen -t ed25519 -f ~/.ssh/ce_vm_key -N ''
# 写入 .env:
CE_VM_SSH_PORT=2223
CE_VM_SSH_KEY=/path/to/ce_vm_key
CE_VM_SSH_PUBKEY=/path/to/ce_vm_key.pub没有密钥时按脚本提示执行 docker compose restart qemu。
默认加载 c,c++,lean,llvm,llvm_mir,llvm_mir_p4,llvm_p4,mlir,mlir_p4,p4:
- Clang 包提供 C/C++、LLVM IR 的
clang/opt/llc和 LLVM MIR 的llc。 - 标准 MLIR 使用 Clang/LLVM 包中的
mlir-opt与mlir-translate。 - GCC 包提供 x86_64 与 riscv64 工具链。
- Lean 更新器安装并验证
lean与leanc。 - 新版 P4 工具链以
p4mlir-<yyyyMMddHHmm>-<buildNumber>-<commit>.tar.zst发布为同名版本目录;旧的<buildNumber>-<commit>.tar.gz|zst仍兼容。工具链包含 p4c、p4mlir 系列工具与 P4 修改版 LLVM;不完整 build 不会注册。 - CE 启动时按同一保留策略扫描 P4 build,为 P4、MLIR P4、LLVM P4、LLVM MIR P4 生成编译器项:dated build 显示
(YYYY-MM-DD),旧格式显示(<build-number>),并保持(latest)置顶。 - 历史
p4mlir-translate的 include 路径和后续p4mlir-opt、mlir-translate、opt、llc流水线均绑定所选 build,不会混用p4-latest。 - Alive2 只预配置
/opt/compiler-explorer/alive2-latest/bin/alive-tv;缺少时菜单隐藏且启动 warning 属于预期。 - P4 patch 提供语言、图标、语法高亮和同 build 链式流水线。
普通语言配置集中在 config/<语言>.local.properties;四种 P4 语言使用同目录下的 .local.properties.template,由 generate-p4-config.sh 在 CE 启动前生成最终配置。全局资源与安全限制位于 compiler-explorer.local.properties,nsjail 入口位于 execution.local.properties。
C、C++、Lean 4 与 LLVM IR(clang-ir)支持在线执行用户程序,运行由 nsjail 沙箱隔离;riscv64 交叉产物因 VM 内无 qemu-user 仅可编译,其余语言仅编译。源码定制位于 vm/patches/,scripts/apply-ce-patches.sh 按四位数字前缀依次应用;升级 CE_REF 时需确认补丁仍可应用。
在 P4 语言中选择 p4mlir-translate (latest)、任一 dated 项如 p4mlir-translate (2026-09-03),或旧格式项如 p4mlir-translate (108),然后从编译器面板的 Add tool 只添加根工具 p4mlir-opt。后续面板从直接父 Tool 的 Next tools 菜单依次打开,所有阶段自动使用与所选编译器相同的 build:
P4 / p4mlir-translate
└─ p4mlir-opt # Web 参数:定制 lowering/pass
└─ mlir-translate # 固定 --mlir-to-llvmir
└─ opt # 固定 -S;Web 参数:-passes=...
└─ llc -> LLVM MIR # Web 必填 -stop-before=... 或 -stop-after=...
└─ llc -> Assembly # 固定 -filetype=asm;Web 参数:target/CPU/features
每个 Tool 只读取直接父级生成的完整文件。修改任一父级参数会重新执行并更新全部下游;父级失败或没有生成文件时,下游保持空白。关闭中间面板会级联关闭其全部下游。面板文本超过 max-asm-size 时只截断显示,磁盘上的完整文件仍传给下一阶段。
p4mlir-opt 默认不添加 pass;如果工具默认行为没有 lower 到 LLVM dialect,需要在它的参数栏填写项目所需 pass。MIR 阶段没有默认截点,未填写 -stop-before 或 -stop-after 时该面板显示错误,最终 Assembly 阶段不会执行。用户参数不能覆盖各阶段由 CE 管理的 -o。
部署脚本、保留策略和动态配置生成可用 bash tests/test-p4-toolchains.sh 做本地回归测试。
sudo kata/setup.sh
docker compose -f compose.kata.yaml up -d --build该路径固定使用 runtime: kata,不再启用容器内 nsjail;配置或工具链变化后执行 docker compose -f compose.kata.yaml restart ce。根文件系统只读,编译缓存位于 tmpfs,短链接位于独立持久化 volume ce-shortlinks-kata。如改用 CE_KATA_STORAGE_ROOT bind mount,宿主目录需预先设置为 uid/gid 10001、权限 0700。
/dev/kvm不存在:启用 KVM 或嵌套虚拟化。- 端口冲突:释放
10240;SSH 端口可用CE_VM_SSH_PORT修改。 - 装配日志:
docker compose logs -f qemu。 - CE 服务:VM 内执行
journalctl -u ce -e。 - 工具链在 QEMU 容器内是
/share/compilers,在 guest 内是/opt/compiler-explorer。 - 编译器未出现:检查对应相对
*-latest软链和必要二进制,再重启ce.service;不完整的 P4 build 会在日志中显示“跳过不完整”。 - 配置未生效:普通配置应链接到
/mnt/ce-repo/config/;四个 P4 配置应是/opt/ce/etc/config/中动态生成的普通文件。 - nsjail 失败:检查
ce-cgroups.service,并确认/sys/fs/cgroup/ce-{compile,sandbox}与/cefs存在;装配自检会输出详细 errno。 - SELinux Enforcing 阻止读取:按 Compose 注释给只读 bind mount 添加
z标签。
端口只绑定回环,Compose 丢弃 capabilities 并启用 no-new-privileges。生产环境仍应固定已核对的镜像/工具链哈希,并在 nginx 层提供认证、TLS 和限流。