Docker 构建慢时,先看是不是把 .git 和 node_modules 打进 context

62 次浏览7 条回复

构建镜像明明没改几行,却每次上传很久,可以先看 build context 里是不是带了仓库历史和依赖目录。项目根目录放个 .dockerignore:

.git
node_modules
dist
coverage
*.log

然后再跑:

docker build --progress=plain -t demo .

环境:Docker BuildKit。.dockerignore 只影响发送给 daemon 的 context,不会替代 Dockerfile 里的 COPY 设计;需要构建产物时,最好在镜像内生成,或者明确从 context 提供。.env、证书这类文件也别顺手传进 context。

还可以先看构建日志里的 transferring context 大小,这一行能区分到底是上传 context 慢,还是后面的某个 Dockerfile 步骤慢。要是 context 已经很小但仍然慢,就继续查 COPY 层和缓存命中情况;别只盯着 .dockerignore。

HosuLv1#1

还可以先看构建日志里的 transferring context 大小,这一行能区分到底是上传 context 慢,还是后面的某个 Dockerfile 步骤慢。要是 context 已经很小但仍然慢,就继续查 COPY 层和缓存命中情况;别只盯着 .dockerignore。

还有个容易踩的点:.dockerignore 默认按 build context 根目录找,不是按 Dockerfile 所在目录。

docker build -f docker/Dockerfile .

这里用的是项目根目录的规则;如果想给某个 Dockerfile 单独配,可以用同目录的 Dockerfile.dockerignore。所以规则没生效时,先确认最后那个 . 指向的 context。

NiroLv1#2

还有个容易踩的点:.dockerignore 默认按 build context 根目录找,不是按 Dockerfile 所在目录。

docker build -f docker/Dockerfile .

这里用的是项目根目录的规则;如果想给某个 Dockerfile 单独配,可以用同目录的 Dockerfile.dockerignore。所以规则没生效时,先确认最后那个 . 指向的 context。

如果为了缩小 context 直接写了 *,记得把 Dockerfile 里要 COPY 的清单文件用 ! 放回来,比如 package.json、锁文件。还有个细节:被忽略的父目录不会因为子路径写了 ! 就自动重新开放,目录本身也要先放回;否则看着规则没问题,构建时还是会提示文件不在 context 里。

Zoe_JayLv1#3

如果为了缩小 context 直接写了 *,记得把 Dockerfile 里要 COPY 的清单文件用 ! 放回来,比如 package.json、锁文件。还有个细节:被忽略的父目录不会因为子路径写了 ! 就自动重新开放,目录本身也要先放回;否则看着规则没问题,构建时还是会提示文件不在 context 里。

我还会把 .dockerignore 当成一道安全边界来查:.env、私钥目录这类东西尽量从 context 就排除掉,不要只指望 Dockerfile 里不 COPY。确实需要传凭据时,BuildKit 可以用 secret 或 SSH mount,避免凭据落进 context 和镜像层。

NiroLv1#4

我还会把 .dockerignore 当成一道安全边界来查:.env、私钥目录这类东西尽量从 context 就排除掉,不要只指望 Dockerfile 里不 COPY。确实需要传凭据时,BuildKit 可以用 secret 或 SSH mount,避免凭据落进 context 和镜像层。

还有个定位上的小区别:--no-cache 只能绕过镜像层缓存,不会缩小 build context。可以先看 --progress=plain 里的 context 大小和耗时,再用一次 --no-cache 对照;前者异常就是传输/忽略规则问题,后者才更适合查 COPY 之后的缓存或构建步骤。

NiroLv1#4

我还会把 .dockerignore 当成一道安全边界来查:.env、私钥目录这类东西尽量从 context 就排除掉,不要只指望 Dockerfile 里不 COPY。确实需要传凭据时,BuildKit 可以用 secret 或 SSH mount,避免凭据落进 context 和镜像层。

对,凭据这块最好给个具体落点,别用 ARG 或 ENV 把 token 带进镜像层。BuildKit 下可以这样:

# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

构建时传 docker build --secret id=npmrc,src=$HOME/.npmrc .,需要的步骤能读到文件,但不会把它写进最终镜像。前提是启用 BuildKit,并确认后续步骤没有把凭据复制到别的路径。

雾很大Lv1#6

对,凭据这块最好给个具体落点,别用 ARG 或 ENV 把 token 带进镜像层。BuildKit 下可以这样:

# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

构建时传 docker build --secret id=npmrc,src=$HOME/.npmrc .,需要的步骤能读到文件,但不会把它写进最终镜像。前提是启用 BuildKit,并确认后续步骤没有把凭据复制到别的路径。

还可以把 required=true 加上,CI 没挂载 secret 时直接失败,避免最后变成一个不太直观的依赖下载或认证错误:

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc,required=true npm ci

这样更容易区分“凭据没传”和“依赖本身有问题”。