1. 基本语法#
两者语法几乎一样,但行为差异很大:
1
2
3
4
5
|
# COPY - 纯粹的文件复制,不做任何额外处理
COPY [--chown=<user>:<group>] <src>... <dest>
# ADD - 支持自动解压和远程 URL,但行为不可预测
ADD [--chown=<user>:<group>] <src>... <dest>
|
2. 自动解压压缩文件#
ADD 会自动解压本地压缩包,COPY 不会:
1
2
3
4
5
6
7
|
# ADD 自动解压 .tar.gz
ADD app.tar.gz /opt/app/
# 结果:/opt/app/ 下是解压后的内容
# COPY 只复制文件本身
COPY app.tar.gz /opt/app/
# 结果:/opt/app/app.tar.gz 是原始压缩包
|
支持的压缩格式:.tar、.tar.gz/.tgz、.tar.bz2、.tar.xz
如果你想复制压缩包本身而不解压,必须用 COPY。
3. 远程 URL 下载#
ADD 可以直接从 URL 下载文件,COPY 不支持:
1
2
3
4
5
|
# ADD 可以下载远程文件(但不推荐)
ADD https://example.com/app.tar.gz /opt/
# COPY 不支持 URL
COPY https://example.com/app.tar.gz /opt/ # 报错!
|
推荐用 RUN + curl 替代 ADD 下载远程文件,因为 ADD 下载无法利用构建缓存、无法做错误处理、无法验证完整性。
1
2
3
4
|
# 推荐:RUN + curl
RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \
&& tar -xzf /tmp/app.tar.gz -C /opt/ \
&& rm /tmp/app.tar.gz
|
4. 自动创建目录#
两者都会自动创建不存在的中间目录:
1
2
3
4
5
6
|
# 如果 /etc/myapp/config/ 不存在,会自动创建
COPY app.conf /etc/myapp/config/
ADD app.conf /etc/myapp/config/
# 不需要手动 mkdir
# RUN mkdir -p /etc/myapp/config ← 多余
|
5. 构建缓存行为#
1
2
3
4
5
|
# COPY - 严格匹配文件内容,内容变了才缓存失效
COPY config/app.conf /etc/app/
# ADD - 压缩文件内容相同时缓存仍然有效
ADD app.tar.gz /opt/app/
|
| 场景 |
COPY |
ADD |
| 文件内容变化 |
缓存失效 |
缓存失效 |
| 文件元数据变化 |
缓存失效 |
缓存失效 |
| 压缩文件内容相同 |
N/A |
缓存有效 |
6. 功能对比#
| 特性 |
COPY |
ADD |
| 复制本地文件 |
✅ |
✅ |
| 复制目录 |
✅ |
✅ |
| 支持通配符 |
✅ |
✅ |
| 自动创建目录 |
✅ |
✅ |
| 自动解压压缩文件 |
❌ |
✅ |
| 支持远程 URL |
❌ |
✅ |
| 语义清晰度 |
✅ 高 |
❌ 低 |
| 推荐使用 |
✅ 首选 |
⚠️ 特定场景 |
7. 使用场景#
COPY 适用于:
1
2
3
4
5
6
7
8
9
|
# 复制配置文件
COPY nginx.conf /etc/nginx/nginx.conf
# 复制应用代码
COPY src/ /app/src/
COPY package.json /app/
# 多阶段构建中复制二进制
COPY --from=builder /app/build/app /usr/local/bin/
|
ADD 仅适用于需要自动解压的场景:
1
2
3
4
5
|
# 合理使用 ADD
ADD node-v18.17.0-linux-x64.tar.gz /usr/local/
# 普通文件不要用 ADD
ADD app.conf /etc/app/ # ← 用 COPY 代替
|
8. 最佳实践#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# 规则 1:默认用 COPY
COPY requirements.txt /app/ # ✅
ADD requirements.txt /app/ # ❌ 没必要
# 规则 2:只在需要解压时用 ADD
ADD node-v18.tar.gz /usr/local/ # ✅ 需要解压
# 规则 3:远程文件用 RUN + curl
RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \
&& tar -xzf /tmp/app.tar.gz -C /opt/ \
&& rm /tmp/app.tar.gz # ✅
ADD https://example.com/app.tar.gz /opt/ # ❌
# 规则 4:用 .dockerignore 减少上下文
# .dockerignore
# .git
# node_modules
# *.md
# .env
# 规则 5:合理安排 COPY 顺序利用缓存
COPY package.json package-lock.json ./ # 先复制依赖(变化少)
RUN npm ci --production
COPY . . # 再复制代码(变化多)
|
9. 常见陷阱#
1
2
3
4
5
6
7
8
9
10
|
# 陷阱 1:意外的自动解压
ADD app.tar.gz /opt/app/
# 你以为复制了文件,实际解压成了目录
# 陷阱 2:远程文件无法缓存
ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/
# 每次构建都重新下载
# 陷阱 3:权限问题
COPY --chown=app:app src/ /app/src/ # 指定所有者
|
10. 选择决策#
1
2
3
4
5
6
7
|
需要复制文件?
├── 本地压缩文件且需要解压?
│ └── 用 ADD
├── 本地普通文件?
│ └── 用 COPY
└── 远程文件?
└── 用 RUN + curl
|