Dockerfile 中 COPY 和 ADD 命令有什么区别?
COPY 可从构建上下文、阶段或镜像等来源复制;ADD 还支持远程 URL/Git 与本地 tar 解包。普通复制用 COPY,需要 ADD 能力时核对其语义及版本。
一句话区别:COPY 默认从构建上下文复制,也可通过 --from 从构建阶段、镜像或命名上下文复制,语义单一可预期;ADD 多两个能力——源可以是远程 URL、本地 tar 包会自动解压到目标目录。正因为 ADD 的"隐式魔法"(解压与否取决于文件类型)容易写出意外行为,普通文件复制优先用 COPY,确需 ADD 能力时按其语义使用。
语法相同,行为不同
COPY app.tar.gz /app/ # 原样拷贝,压缩包还是压缩包
ADD app.tar.gz /app/ # 自动解压成目录内容
ADD https://example.com/data.csv /data/ # 从 URL 下载
为什么推荐 COPY
- 行为透明:COPY 只有一种语义,镜像构建结果可预期
- 避免意外解压:本想拷个压缩包,ADD 却给解开了,镜像里凭空多一堆文件
- 缓存需核对:远程ADD有自身缓存与校验机制,不能说只看URL;可使用受支持的--checksum固定内容,RUN curl同样可能命中构建缓存
ADD 的合理使用场景
合理场景包括本地 tar 自动解压及受支持的远程URL/Git获取:
ADD nginx-1.25.tar.gz /usr/local/
下载文件的正确姿势
若需显式下载、解压与清理,可用RUN;远程ADD本身也是支持的方式:
RUN curl -fsSL https://example.com/tool.tar.gz -o /tmp/tool.tar.gz \
&& tar -xzf /tmp/tool.tar.gz -C /opt \
&& rm /tmp/tool.tar.gz
下载、解压、清理在同一层完成,镜像不留下中间文件,失败也好排查。
常见问题(FAQ)
Q:COPY 能拷贝构建上下文之外的文件吗?
A:普通COPY不能越过构建上下文;--from可从阶段、镜像或命名上下文取文件,按Dockerfile frontend版本核对能力。
Q:.dockerignore 对 COPY/ADD 有效吗?
A:有效。被 .dockerignore 排除的文件两者都拷不进去,用它排除 .git、node_modules、日志目录能显著加速构建。
Q:ADD URL 形式为什么没有缓存校验?
A:这个前提不成立,远程ADD有缓存校验机制;本地tar按内容识别是否解压,普通远程tar默认不自动解压,新的解压选项需核对frontend版本。RUN curl也不会无条件重新下载。