服务端阅读 02026年6月20日 00:02
Docker COPY 和 ADD 有什么区别?什么时候该用 ADD?
Dockerfile 里复制文件时,默认优先用 COPY。它的语义很单纯:把构建上下文里的文件或目录复制到镜像指定路径。ADD 也能复制文件,但它多了几个“隐式动作”,尤其是本地 tar 包自动解压和远程 URL 下载。正因为这些行为不够直观,日常构建里更推荐 COPY,只有明确需要 ADD 的特殊能力时再用它。COPY 做什么?COPY 只负责复制本地文件、目录,行为可预测:COPY package.json package-lock.json ./COPY src/ /app/src/注意目标路径的斜杠:COPY file.txt /appCOPY file.txt /app/如果 /app 不存在,第一种可能把文件复制成名为 /app 的文件;第二种明确表示复制到 /app/ 目录下。写 Dockerfile 时建议目录目标都带上 /,减少歧义。COPY 也支持通配符,例如:COPY *.json ./但通配符匹配的是构建上下文里的文件,不是容器里的路径。构建上下文会受 .dockerignore 影响,所以别把 node_modules、日志、临时文件、密钥文件一起送进镜像构建,否则不仅镜像变大,还可能泄露敏感信息。ADD 多了哪些能力?ADD 可以做 COPY 能做的事,还多了两类常见行为。第一,本地 tar 包会自动解压:ADD app.tar.gz /app/如果 app.tar.gz 是本地构建上下文里的 tar 归档,Docker 会把它解压到 /app/。这个功能适合你明确想把本地归档展开进镜像的场景。第二,ADD 可以从远程 URL 下载文件:ADD https://example.com/tool.tar.gz /tmp/tool.tar.gz但这通常不推荐。远程下载会让构建结果依赖网络、服务端响应和缓存规则,可复现性变差。使用 BuildKit 时,HTTP(S) URL 可以配合 --checksum 校验内容:ADD --checksum=sha256:<hash> https://example.com/tool.tar.gz /tmp/tool.tar.gz有校验比裸下载安全,但大多数场景仍然更适合用 RUN curl 或 wget,因为你可以在同一层里校验、解压、删除缓存文件。为什么下载文件更推荐 RUN curl 或 wget?比如安装一个二进制包,更推荐这样写:RUN curl -fsSL https://example.com/tool.tar.gz -o /tmp/tool.tar.gz && echo "<hash> /tmp/tool.tar.gz" | sha256sum -c - && tar -xzf /tmp/tool.tar.gz -C /usr/local/bin && rm /tmp/tool.tar.gz好处很直接:下载、校验、解压、清理都在同一层完成,不会把临时压缩包留在镜像层里。失败时也更容易定位问题。缓存失效有什么区别?COPY 和 ADD 都会影响 Docker 构建缓存。只要被复制的源文件内容发生变化,对应层以及后面的 RUN 层通常都会失效。所以常见优化是先复制依赖描述文件,再安装依赖,最后复制业务代码:COPY package.json package-lock.json ./RUN npm ciCOPY src/ ./src/这样业务代码变了,不会轻易让依赖安装层重新执行。.dockerignore 也会影响缓存。忽略无关文件能减少构建上下文变化,避免因为日志、缓存目录、编辑器临时文件导致 Docker 反复失效。--chown、--chmod 和多阶段构建COPY 和 ADD 都可以配合权限参数使用:COPY --chown=node:node --chmod=755 app.sh /usr/local/bin/app.sh这比复制后再 RUN chown、RUN chmod 更干净,少一层,也更容易读懂。多阶段构建里还常用 COPY --from 从前一个阶段复制产物:FROM node:20 AS buildWORKDIR /appCOPY package.json package-lock.json ./RUN npm ciCOPY . .RUN npm run buildFROM nginx:alpineCOPY --from=build /app/dist/ /usr/share/nginx/html/这也是 COPY 的高频用法:只把最终产物放进运行镜像,构建工具、源码缓存、依赖中间文件都不带进去。安全上怎么选?规则可以很简单:只是复制文件:用 COPY需要本地 tar 自动解压:可以用 ADD需要下载远程文件:优先 RUN curl 或 wget,并做 checksum 校验不要把密钥、.env、SSH 私钥放进构建上下文用 .dockerignore 控制复制范围目录目标路径尽量写成 /path/,避免歧义ADD 不是不能用,而是它会“顺手多做事”。Dockerfile 越显式,镜像构建越稳定,也越容易排查问题。