构建 Docker 镜像的日常体验是这样的:敲下 docker build,然后盯着终端看几十层镜像逐层下载、逐层执行,GiB 级的数据在本地磁盘和远程 registry 之间来回搬运。CI 环境里更慢——冷启动时拉基础镜像就能吃掉两三分钟。开发者 Octavio Agagavia 最近做了一个研究原型,把整个流程搬进了浏览器:选基础镜像、写启动脚本、构建镜像,全部在浏览器 tab 里完成,延迟压到秒级。
传统构建的瓶颈在哪
一次典型的 docker build 耗时主要分布在三个阶段:
- 拉基础镜像:即使有层缓存,首次拉取
ubuntu:22.04也要 70–120 MB,网络不好时更久。 - 执行指令层:每条
RUN都会创建新层,涉及文件系统变更和进程执行。 - 推送镜像:构建完推到 registry,又是网络 I/O。
本地构建至少有 Docker daemon 和 overlayFS 做加速;CI 环境经常是冷启动,缓存命中率低。浏览器里没有 Docker daemon,没有 overlayFS,甚至没有真正的 Linux 内核——这正是这个原型要解决的核心难题。
浏览器里怎么"构建镜像"
Agagavia 的原型思路并不神秘,关键在于用 WebAssembly 模拟容器构建所需的三个核心能力:
- 文件系统模拟:用 WASM 版本的文件系统(如 browser-fs-access 或基于 Emscripten 的 MEMFS)在内存中搭建镜像层结构。
- 指令执行:把 Dockerfile 的
RUN、COPY等指令映射为 WASM 环境下的操作——RUN apt-get install变成在 WASM 编译的轻量 Linux 工具链里执行对应命令。 - 层打包与导出:将内存中的层文件打包成 OCI 格式的 tar 包,直接下载或推到远程 registry。
整个流程不需要安装任何本地工具,打开网页就能开始。
下面是一个传统 docker build 的对比示例,帮助理解浏览器方案省掉了什么:
# 传统方式:本地需要 Docker daemon,首次构建耗时分钟级
cat > Dockerfile <<'EOF'
FROM alpine:3.19
RUN apk add --no-cache curl jq
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
EOF
echo '#!/bin/sh' > entrypoint.sh
echo 'curl -s https://api.github.com/repos/moby/moby | jq .description' >> entrypoint.sh
# 构建并推送
docker build -t my-utility:latest .
docker push my-utility:latest
# 首次构建:拉 alpine ~7MB + apk install ~15s + push ~10s
# CI 冷启动:可能 2-3 分钟
在浏览器原型中,同样的流程变成:网页上选 alpine:3.19 作为基础镜像(镜像层从 CDN 预加载到内存),在编辑器里写几行脚本,点击 Build,几秒后拿到 OCI 格式的 tar 包下载链接。没有 Docker daemon,没有本地磁盘写入,没有 CI 冷启动。
实际能构建什么
目前这个原型是研究性质,能构建的范围有明确边界:
- 适合的镜像类型:小体积基础镜像 + 少量包安装 + 简单脚本,比如 Alpine + 几个 CLI 工具、轻量 API 服务、静态站点打包。
- 不适合的镜像类型:需要完整 systemd 的复杂服务、依赖 GPU 驱动的镜像、编译大型 C/C++ 项目的镜像(WASM 环境的工具链和性能还撑不住)。
一个典型的适用场景是快速构建"一次性工具镜像"——比如你需要一个带 curl + jq + yq 的 Alpine 镜像来做 API 调试,传统方式要等本地 Docker 拉镜像、装包;浏览器里几秒就能拿到。
下面是一个用 OCI 规范手动组装最小镜像层的示例,展示了浏览器原型在内存中做的事情的本质——你可以本地跑这段脚本来理解原理:
# 手动组装一个最小 OCI 镜像层(模拟浏览器原型在内存中做的事)
mkdir -p /tmp/oci-demo/rootfs /tmp/oci-demo/blobs
# 1. 创建 rootfs 内容
echo '#!/bin/sh' > /tmp/oci-demo/rootfs/entrypoint.sh
echo 'echo "Hello from browser-built container"' >> /tmp/oci-demo/rootfs/entrypoint.sh
chmod +x /tmp/oci-demo/rootfs/entrypoint.sh
# 2. 打包为层 tar
tar -C /tmp/oci-demo/rootfs -cf /tmp/oci-demo/blobs/layer.tar .
# 3. 计算层 digest(OCI 规范要求 sha256)
LAYER_DIGEST=$(sha256sum /tmp/oci-demo/blobs/layer.tar | cut -d' ' -f1)
mv /tmp/oci-demo/blobs/layer.tar /tmp/oci-demo/blobs/sha256-${LAYER_DIGEST}.tar
# 4. 生成层 descriptor 的 config
cat > /tmp/oci-demo/blobs/config.json <<EOF
{
"architecture": "amd64",
"os": "linux",
"config": {
"Entrypoint": ["/entrypoint.sh"]
},
"rootfs": {
"type": "layers",
"diff_ids": ["sha256:${LAYER_DIGEST}"]
}
}
EOF
CONFIG_DIGEST=$(sha256sum /tmp/oci-demo/blobs/config.json | cut -d' ' -f1)
mv /tmp/oci-demo/blobs/config.json /tmp/oci-demo/blobs/sha256-${CONFIG_DIGEST}.json
echo "层 digest: sha256:${LAYER_DIGEST}"
echo "配置 digest: sha256:${CONFIG_DIGEST}"
echo "查看 blobs 目录:"
ls -lh /tmp/oci-demo/blobs/
这段脚本做的事情,就是浏览器原型在 WASM 内存里完成的:把文件打包成层、计算 sha256 digest、按 OCI 规范组织 blobs。区别只是浏览器版用内存文件系统替代了 /tmp,用 WASM 工具替代了 tar 和 sha256sum。
限制与取舍
把构建搬进浏览器不是万能方案,目前有几个硬限制:
- 包生态不完整:WASM 环境下能跑的包管理器有限,
apk的部分包可以,apt和yum大量包依赖原生动态链接,跑不了。 - 性能天花板:WASM 的计算性能接近原生但 I/O 受限,大文件操作(编译、数据库初始化)会比本地慢。
- 安全边界:浏览器沙箱本身是安全优势,但也意味着无法访问外部网络资源(除非通过 CORS 代理),
RUN curl ...这类指令需要特殊处理。 - 镜像体积:内存文件系统意味着构建过程中所有层数据都在 JS 堆内存里,镜像超过几百 MB 就可能触发浏览器内存限制。
什么时候值得尝试
现阶段这个原型更适合以下场景:
- 教学与演示:在培训或文档中让读者零安装体验容器构建流程。
- 轻量工具镜像:快速组装带 2-3 个 CLI 工具的 Alpine 镜像,省掉本地 Docker 安装。
- CI 预览层:在 CI pipeline 之前快速验证 Dockerfile 语法和层结构是否合理。
如果你已经在本地有成熟的 Docker 环境、构建的镜像体积大或依赖复杂,浏览器方案暂时没有优势。但作为"零安装、秒级出结果"的方向,它指向了一个有趣的未来:容器构建可能不再需要"先装 Docker",而是打开一个网页就够了。