## 7.2 COPY 复制文件 ### 基本语法 具体内容如下: ```docker COPY [选项] <源路径>... <目标路径> COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"] ``` `COPY` 指令将构建上下文中的文件或目录复制到镜像内。 --- ### 基本用法 #### 复制单个文件 具体内容如下: ```docker ## 复制文件到指定目录 COPY package.json /app/ ## 复制文件并重命名 COPY config.json /app/settings.json ``` #### 复制多个文件 具体内容如下: ```docker ## 复制多个指定文件 COPY package.json package-lock.json /app/ ## 使用通配符 COPY *.json /app/ COPY src/*.js /app/src/ ``` #### 复制目录 具体内容如下: ```docker ## 复制整个目录的内容(不是目录本身) COPY src/ /app/src/ ``` > ⚠️ **注意**:复制目录时,复制的是目录的**内容**,不包含目录本身。 ``` 构建上下文: 镜像内: src/ /app/src/ ├── index.js → ├── index.js └── utils.js └── utils.js ``` --- ### 通配符规则 COPY 支持 Go 的 `filepath.Match` 通配符规则: | 通配符 | 说明 | 示例 | |--------|------|------| | `*` | 匹配任意字符序列 | `*.json` | | `?` | 匹配单个字符 | `config?.json` | | `[abc]` | 匹配括号内任一字符 | `[abc].txt` | | `[a-z]` | 匹配范围内字符 | `file[0-9].txt` | ```docker COPY hom* /mydir/ # home.txt, homework.md 等 COPY hom?.txt /mydir/ # home.txt, homy.txt 等 COPY app[0-9].js /app/ # app0.js ~ app9.js ``` --- ### 目标路径 #### 绝对路径 具体内容如下: ```docker COPY app.js /usr/src/app/ ``` #### 相对路径(基于 WORKDIR) 具体内容如下: ```docker WORKDIR /app COPY package.json ./ # 复制到 /app/package.json COPY src/ ./src/ # 复制到 /app/src/ ``` #### 自动创建目录 如果目标目录不存在,Docker 会自动创建: ```docker ## /app/config/ 不存在也会自动创建 COPY settings.json /app/config/ ``` --- ### 修改文件所有者 使用 `--chown` 选项设置文件的用户和组: ```docker ## 使用用户名和组名 COPY --chown=node:node package.json /app/ ## 使用 UID 和 GID COPY --chown=1000:1000 . /app/ ## 只指定用户 COPY --chown=node . /app/ ``` > 💡 结合 `USER` 指令使用,确保应用以非 root 用户运行。 --- ### 保留文件元数据 COPY 会保留源文件的元数据: - 读、写、执行权限 - 修改时间 这对于脚本文件特别重要: ```docker ## start.sh 的可执行权限会被保留 COPY start.sh /app/ ``` --- ### COPY vs ADD | 特性 | COPY | ADD | |------|------|-----| | 复制本地文件 | ✅ | ✅ | | 自动解压 tar | ❌ | ✅ | | 支持 URL | ❌ | ✅(不推荐) | | 推荐程度 | ✅ **推荐** | ⚠️ 特殊场景使用 | ```docker ## 推荐:使用 COPY COPY app.tar.gz /app/ RUN tar -xzf /app/app.tar.gz ## ADD 会自动解压(行为不明显,不推荐) ADD app.tar.gz /app/ ``` > 笔者建议:除非需要自动解压 tar 文件,否则始终使用 COPY。明确的行为比隐式的魔法更好。 --- ### 多阶段构建中的 COPY #### 从其他构建阶段复制 具体内容如下: ```docker ## 构建阶段 FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build ## 生产阶段 FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html ``` #### 使用 --link 优化缓存(BuildKit) 具体内容如下: ```docker ## 使用 --link 后,文件以独立层添加,不依赖前序指令 COPY --link --from=builder /app/dist /usr/share/nginx/html ``` `--link` 的优势: - 更高效利用构建缓存 - 并行化构建过程 - 加速多阶段构建 --- ### .dockerignore 使用 `.dockerignore` 排除不需要复制的文件: ```gitignore ## .dockerignore node_modules .git .env *.log Dockerfile .dockerignore ``` 这可以: - 减小构建上下文大小 - 加速构建 - 避免复制敏感文件 --- ### 最佳实践 #### 1. 利用缓存,先复制依赖文件 具体内容如下: ```docker ## ✅ 好:先复制依赖定义,再安装,最后复制代码 COPY package.json package-lock.json ./ RUN npm install COPY . . ## ❌ 差:一次性复制所有文件,代码变更会导致重新 npm install COPY . . RUN npm install ``` #### 2. 使用 .dockerignore 具体内容如下: ```docker ## 确保 node_modules 不被复制 COPY . . ## .dockerignore 中应包含 node_modules 具体内容如下: ``` #### 3. 明确复制路径 具体内容如下: ```docker ## ✅ 好:明确的路径 COPY src/ /app/src/ COPY package.json /app/ ## ❌ 差:过于宽泛 COPY . . ``` --- ### 本章小结 | 操作 | 示例 | |------|------| | 复制文件 | `COPY app.js /app/` | | 复制多个文件 | `COPY *.json /app/` | | 复制目录内容 | `COPY src/ /app/src/` | | 修改所有者 | `COPY --chown=node:node . /app/` | | 从构建阶段复制 | `COPY --from=builder /app/dist ./` | ### 延伸阅读 - [ADD 指令](add.md):复制和解压 - [WORKDIR 指令](workdir.md):设置工作目录 - [多阶段构建](../multistage-builds.md):优化镜像大小 - [最佳实践](../../16_appendix/16.1_best_practices.md):Dockerfile 编写指南