Dockerfile 中的 ADD 和 COPY 详解
在 Dockerfile 中,ADD 和 COPY 都是用于将文件或目录从构建上下文(以及某些额外源)复制到镜像文件系统中的指令。虽然它们功能有重叠,但设计初衷和适用场景不同。理解它们的区别有助于编写更安全、高效的 Dockerfile。
1. 基本语法
两者语法非常相似,支持两种形式:
- Exec 形式(推荐):
["源", "目标"] - Shell 形式:
源 目标
dockerfile
COPY --chown=<user>:<group> <源路径>... <目标路径>
ADD --chown=<user>:<group> <源路径>... <目标路径><源路径>:可以是一个或多个文件/目录,也可以是通配符匹配的路径。<目标路径>:容器内的绝对路径或相对于WORKDIR的相对路径。
2. COPY – 简单、明确的文件复制
作用:仅将本地构建上下文中的文件/目录复制到镜像中。没有额外功能。
特点
- 支持基本通配符(如
COPY *.txt /dest/)。 - 可以结合
--chown更改复制后文件的属主。 - 如果目标目录不存在,会自动创建(包括所有缺失的父目录)。
- 保持文件的元数据(如权限、修改时间等)。
推荐使用场景
- 复制代码、配置文件、静态资源等常规文件。
- 几乎所有需要将本地文件放入镜像的场景,除非需要
ADD的特殊功能。
3. ADD – 增强版复制(但需谨慎使用)
作用:除了 COPY 的所有功能外,还支持:
额外功能
1. 自动解压压缩包
如果源文件是本地的 tar 压缩包(
gzip、bzip2、xz、lzip、lz4、lzop、zstd、tar等),ADD会自动将其解压到目标路径(仅限 tar 格式)。dockerfileADD archive.tar.gz /usr/local/ # 自动解压到 /usr/local/
2. 从远程 URL 获取文件
源可以是一个 URL,Docker 会下载该文件并复制到镜像中。
dockerfileADD https://example.com/file.tar.gz /tmp/- 如果 URL 指向一个 tar 包,不会自动解压(仅当源是本地 tar 包时自动解压)。
- 下载的文件默认权限为 600(除非使用
--chown修改)。
缺点与注意事项
- 行为不透明:
ADD的自动解压和 URL 下载可能造成意外结果。 - 构建缓存失效:URL 文件的修改时间等可能导致缓存失效。
- 安全风险:从 URL 下载文件不推荐,因为无法控制外部资源的可用性和完整性(建议用
curl或wget并验证校验和)。 - 效率问题:自动解压会增加镜像层大小;对于不明确需要解压的场景,
COPY更可控。
推荐使用场景
- 仅在确实需要自动解压本地 tar 包时使用(例如复制一个预打包的应用包并希望直接解压到目录)。
- 避免使用
ADD下载 URL,应改用RUN curl或RUN wget,因为后者可以处理下载失败、校验和、删除临时文件等。
4. 详细对比表
| 特性 | COPY | ADD |
|---|---|---|
| 复制本地文件/目录 | ✅ | ✅ |
| 支持通配符 | ✅ | ✅ |
支持 --chown | ✅ | ✅ |
| 自动创建目标目录 | ✅ | ✅ |
| 保持文件元数据 | ✅ | ✅ |
| 从远程 URL 下载 | ❌ | ✅(不推荐) |
| 自动解压本地 tar 包 | ❌ | ✅(仅 tar 格式) |
| 可预测性 | 高 | 中(可能有副作用) |
| 官方最佳实践推荐 | ✅ 首选 | ⚠️ 仅在特定需求下使用 |
5. 最佳实践建议
优先使用
COPY
除非你明确需要ADD的自动解压功能,否则一律使用COPY。它更清晰、更可预测。使用
ADD解压 tar 包时
确保目标目录为空或不存在,避免覆盖已有文件。同时注意解压后可能产生多余的文件权限问题。不要用
ADD下载 URL
理由:- 无法在下载失败时自动重试。
- 无法轻易删除中间文件(因为
ADD是一个独立层,下载的文件会永久保留在该层中)。 - 无法验证文件完整性(如 SHA256)。
正确做法:
dockerfileRUN curl -fsSL https://example.com/file.tar.gz -o /tmp/file.tar.gz \ && echo "expected_hash /tmp/file.tar.gz" | sha256sum -c - \ && tar -xzf /tmp/file.tar.gz -C /usr/local \ && rm /tmp/file.tar.gz注意构建上下文大小
ADD和COPY都只会复制构建上下文内的文件。不要将大文件或不必要的文件放在上下文中(使用.dockerignore排除)。使用
--chown指定属主
避免容器以 root 运行导致安全风险,复制文件时指定非 root 用户。
6. 示例对比
场景:复制配置文件
dockerfile
# 推荐:使用 COPY
COPY nginx.conf /etc/nginx/nginx.conf场景:复制并解压本地 tar 包
dockerfile
# 使用 ADD 自动解压
ADD app.tar.gz /opt/app/场景:下载远程脚本(不推荐 ADD)
dockerfile
# 不推荐:
ADD https://raw.githubusercontent.com/.../script.sh /tmp/script.sh
# 推荐:
RUN curl -fsSL https://.../script.sh -o /tmp/script.sh \
&& chmod +x /tmp/script.sh \
&& /tmp/script.sh \
&& rm /tmp/script.sh总结
COPY是简单、明确的文件复制工具,90% 的场景应使用它。ADD是增强版,主要价值在于自动解压本地 tar 包,但应避免用于下载 URL。- 遵循 “COPY 优先,ADD 谨慎” 的原则,能让 Dockerfile 更清晰、更安全、更高效。
