Skip to content

Dockerfile 中的 ADD 和 COPY 详解

在 Dockerfile 中,ADDCOPY 都是用于将文件或目录从构建上下文(以及某些额外源)复制到镜像文件系统中的指令。虽然它们功能有重叠,但设计初衷和适用场景不同。理解它们的区别有助于编写更安全、高效的 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 压缩包(gzipbzip2xzlziplz4lzopzstdtar 等),ADD 会自动将其解压到目标路径(仅限 tar 格式)。

    dockerfile
    ADD archive.tar.gz /usr/local/   # 自动解压到 /usr/local/

2. 从远程 URL 获取文件

  • 源可以是一个 URL,Docker 会下载该文件并复制到镜像中。

    dockerfile
    ADD https://example.com/file.tar.gz /tmp/
    • 如果 URL 指向一个 tar 包,不会自动解压(仅当源是本地 tar 包时自动解压)。
    • 下载的文件默认权限为 600(除非使用 --chown 修改)。

缺点与注意事项

  • 行为不透明ADD 的自动解压和 URL 下载可能造成意外结果。
  • 构建缓存失效:URL 文件的修改时间等可能导致缓存失效。
  • 安全风险:从 URL 下载文件不推荐,因为无法控制外部资源的可用性和完整性(建议用 curlwget 并验证校验和)。
  • 效率问题:自动解压会增加镜像层大小;对于不明确需要解压的场景,COPY 更可控。

推荐使用场景

  • 仅在确实需要自动解压本地 tar 包时使用(例如复制一个预打包的应用包并希望直接解压到目录)。
  • 避免使用 ADD 下载 URL,应改用 RUN curlRUN wget,因为后者可以处理下载失败、校验和、删除临时文件等。

4. 详细对比表

特性COPYADD
复制本地文件/目录
支持通配符
支持 --chown
自动创建目标目录
保持文件元数据
从远程 URL 下载✅(不推荐)
自动解压本地 tar 包✅(仅 tar 格式)
可预测性中(可能有副作用)
官方最佳实践推荐✅ 首选⚠️ 仅在特定需求下使用

5. 最佳实践建议

  1. 优先使用 COPY
    除非你明确需要 ADD 的自动解压功能,否则一律使用 COPY。它更清晰、更可预测。

  2. 使用 ADD 解压 tar 包时
    确保目标目录为空或不存在,避免覆盖已有文件。同时注意解压后可能产生多余的文件权限问题。

  3. 不要用 ADD 下载 URL
    理由:

    • 无法在下载失败时自动重试。
    • 无法轻易删除中间文件(因为 ADD 是一个独立层,下载的文件会永久保留在该层中)。
    • 无法验证文件完整性(如 SHA256)。
      正确做法:
    dockerfile
    RUN 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
  4. 注意构建上下文大小
    ADDCOPY 都只会复制构建上下文内的文件。不要将大文件或不必要的文件放在上下文中(使用 .dockerignore 排除)。

  5. 使用 --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 更清晰、更安全、更高效。