builder 阶段是 `COPY . .` 一把梭,而 docker 的层缓存是链式的:那一层的校验和 覆盖整个仓库,改任何一个文件都会把它作废,后面 vite build 和 bun compile 全部 重跑。前端那 160s 一次都躲不掉,改一篇 markdown 也要陪着编。 按各自的输入分开拷,慢的那条放前面(缓存链上越靠前越不容易被碰到): 只改 apps/api 前端两层命中缓存,只重编后端 ~1s 只改 apps/web 前端重编,后端被链条带着 ~160s 改 packages/contract 两边都重编(本来就都依赖它,是对的) 改 docs / 根上的杂项 builder 根本看不见 0 代价是 builder 只看得见这里点名拷进来的四样,新加顶层目录要同步加 COPY。 顺带堵掉两个同源的洞: · apps/web/docs、tests、**/CLAUDE.md 进了 .dockerignore。它们在 apps/web 里面, 整个目录拷进去的话,改一篇文档也会让 vite build 那层失效。 · .dockerignore 末尾为 --prebuilt 开的例外(!dist/oj2-api、!apps/web/dist)让本地 产物照样进构建上下文 —— 这是比 `COPY . .` 更隐蔽的一条:在本机编一次后端, 80MB 的 dist/oj2-api 变了,rsync 上去,服务器就重编一次前端,源码一行没动。 dist/oj2-api 现在不再被 builder 拷贝;apps/web/dist 仍在 COPY apps/web 里面, 所以 deploy.sh 头上那条手动 rsync 补了 --exclude dist。CI 走 --prebuilt, builder 整个不进构建图,不受影响。 验证:用老 Dockerfile 和新 Dockerfile 各构建一次 --target artifacts 导出对比, 后端二进制 sha256 相同,前端 dist 500 个文件逐个 sha256 一致。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,14 @@ docker/.env.*
|
||||
# 前端 dist 由构建阶段自己生成,本地那份不要带进去污染
|
||||
apps/web/dist/
|
||||
|
||||
# apps/ 下的非构建输入。Dockerfile 里是整个目录拷进去的(`COPY apps/web apps/web`),
|
||||
# 所以改一篇 docs/ 里的 markdown 也会让 `RUN vite build` 那层失效,白编 160s。
|
||||
# 这几样都不被源码 import,挡在上下文外面最省事。
|
||||
apps/web/docs/
|
||||
apps/web/tests/
|
||||
**/CLAUDE.md
|
||||
**/README.md
|
||||
|
||||
# —— 例外:ARTIFACTS=prebuilt 时,产物本身就是构建输入 ——
|
||||
#
|
||||
# 这几条必须放在文件最后:dockerignore 是**最后一条匹配的规则说了算**,
|
||||
@@ -35,6 +43,11 @@ apps/web/dist/
|
||||
#
|
||||
# ARTIFACTS=build(默认,服务器手动部署走的那条)时它们只是让上下文多传
|
||||
# 几十 MB,镜像里用不到;本地根本没编过的话这两个路径不存在,也不影响。
|
||||
#
|
||||
# ⚠️ 一个代价:`apps/web/dist` 因此会进 builder 的 `COPY apps/web` 那一层,
|
||||
# 也就进了它的缓存键 —— 手边有一份内容不同的本地 dist,就会白重编一次前端。
|
||||
# 所以 docker/deploy.sh 头上那条手动 rsync 用 --exclude dist 把它挡在服务器外。
|
||||
# (CI 走 --prebuilt,builder 整个不进构建图,不受这条影响。)
|
||||
!dist/oj2-api
|
||||
!apps/web/dist
|
||||
!apps/web/dist/**
|
||||
|
||||
Reference in New Issue
Block a user