Docker 文件拷贝实战指南:深入解析 `docker cp`、Dockerfile `COPY` 与 `ADD`
2026/9/10 20:54:55 网站建设 项目流程

Docker 文件拷贝实战指南:深入解析docker cp、DockerfileCOPYADD

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

本篇技术指南以 Docker 文件拷贝为主题,系统讲解docker cp命令、Dockerfile 中的COPY指令以及ADD指令的完整用法、底层工作原理与各自适用场景。读完本文,你将掌握在宿主机与容器之间传递文件、在构建镜像时注入文件、以及在多阶段构建中跨阶段拷贝产物的全套实战方案,并能正确区分COPYADD的差异、规避常见陷阱。

引言:Docker 中文件拷贝的三种途径

在容器化开发与部署流程中,拷贝关键文件和目录是构建、调试容器的必备技能。在 Docker 生态里,文件拷贝主要有三种方式:

  1. docker cp命令:在宿主机文件系统与容器之间实时拷贝文件,对正在运行或已停止的容器都有效;
  2. DockerfileCOPY指令:在镜像构建过程中,将宿主机的文件或目录写入镜像层;
  3. Volume 挂载:通过数据卷(volume)或 bind mount 将宿主目录直接挂载到容器内,实现持续同步。

其中,docker cp命令是容器运行时拷贝文件最快捷的方式,用法与 Unix 的cp命令非常相似,且同时支持文件和目录。本文将以docker cp为主线,同时讲解 DockerfileCOPY指令及其与ADD指令的区别。

一、docker cp命令:宿主与容器之间的实时文件搬运

1. 基本语法

docker cp的基本语法非常简洁:

docker cp <src> <dest>

<src><dest>中必须至少有一方位于容器内部。当路径属于容器时,需要在路径前加上容器名或容器 ID,格式为:

CONTAINER:SRC_PATH CONTAINER:DEST_PATH

2. 用docker ps定位容器

首先执行docker ps查看当前正在运行的容器列表,记录目标容器的 ID 或名称:

docker ps

输出中每一行对应一个运行中的容器,其中第一列是容器 ID(例如613a1a58b70d),最后一列是容器名称。如果你已经通过终端/CLI 进入了某个容器(即容器内交互会话),则无需再显式携带容器 ID。

3. 从容器拷贝文件到宿主机

先进入容器,用ls命令确认容器内文件的位置,然后执行:

docker cp 613a1a58b70d:/app/app.js D:/DockerCopy

这条命令将容器613a1a58b70d内的app.js拷贝到宿主机的D:/DockerCopy目录(在 Windows 提示符下运行时,容器名/ID 必须作为源路径的一部分)。注意,容器路径相对于容器根目录/解析,/app/app.js即容器根目录下的app/app.js

4. 用容器名称替代容器 ID

容器 ID 是随机短哈希,记忆不便,Docker 同样允许使用容器名称:

docker cp my-container:/app/app.js D:/DockerCopy

实际使用中优先使用容器名称,可读性更强,也避免了 ID 输错的风险。

5. 从宿主机拷贝文件到容器

将命令的源与目标对调,即可完成反向拷贝:

docker cp tocopy.txt 613a1a58b70d:/app/

该命令把宿主机的文本文件tocopy.txt拷入容器的/app/目录。执行后,在容器终端运行ls即可看到拷贝成功的文件,验证过程与 Unix 文件操作完全一致。

6. 整目录拷贝

docker cp不只处理单个文件,整个文件夹同样适用:

docker cp 613a1a58b70d:/var/www/html D:/DockerCopy/site docker cp ./dist 613a1a58b70d:/app/public

拷贝完成后同样可以用ls在容器内确认目录结构是否完整。

7. 两个关键行为约定

  • 容器路径始终相对容器根目录/解析,即使路径中没有显式写出/
  • docker cp不要求容器处于运行状态,容器已停止时依然可以拷贝,因为底层读取的是容器所对应镜像层中的数据。

8. 仅有的两个可选参数

docker cp的参数极简,只有两个可选标志:

参数说明
-a归档模式(archive mode),保留被拷贝文件的 uid/gid 所有者信息
-L始终跟随SRC中的符号链接(symbolic link)

在需要保留文件权限与属主信息(如拷贝日志、配置或需要保持原权限的可执行文件)时,务必使用-a

9. 容器与容器之间的拷贝

docker cp还可以直接在两个容器之间搬运文件——只需在源路径和目标路径中都携带各自的容器名或 ID:

docker cp containerA:/app/data.txt containerB:/tmp/

不过官方对这种方式持保守态度(详见下文"限制"小节),生产环境更推荐通过宿主机中转或多阶段构建完成。

二、DockerfileCOPY指令:构建镜像时注入文件

Dockerfile 是构建 Docker 镜像的蓝图,镜像随后被实例化为容器。Dockerfile 中可包含多条指令,COPY是其中用于文件注入的核心指令之一。

1. 语法与语义

COPY的语法与docker cp一致:

COPY <source> <destination>

但二者的执行时机完全不同:COPY镜像构建阶段将宿主机(更准确地说,是构建上下文 build context)中的文件或文件夹写入镜像,拷贝进镜像的文件会成为由该镜像创建的每一个容器的一部分。

2. 经典示例:替换默认页面

以下片段把自定义的index.html拷入 Nginx 容器镜像的默认网页目录,从而替换默认首页:

FROM nginx:latest COPY index.html /var/www/html

构建并运行该镜像后,容器中的/var/www/html/index.html就是被替换后的版本。需要强调的是,COPY无法在宿主机与正在运行的容器之间拷贝文件——它的唯一职责是在镜像构建期间向镜像中添加内容。

3. 多阶段构建中的COPY --from

COPY更强大的形态出现在多阶段构建(multi-stage build)中。COPY --from=<stage>可以从指定构建阶段甚至外部镜像中拷贝产物,这是构建瘦身镜像的标准手段。

在 refine 仓库的 examples/store/Dockerfile 中可以看到完整的实战用法:

FROM refinedev/node:18 AS base ... FROM base AS deps COPY package.json pnpm-lock.yaml .npmrc ./ RUN ... pnpm install --frozen-lockfile --ignore-scripts FROM base AS builder COPY --from=deps /app/refine/node_modules ./node_modules COPY . . RUN pnpm run build --scope store --skipNxCache FROM base AS runner ENV NODE_ENV production COPY --from=builder /app/refine/examples/store/public ./public COPY --from=builder --chown=refine:nodejs /app/refine/examples/store/.next/standalone ./ COPY --from=builder --chown=refine:nodejs /app/refine/examples/store/.next/static ./examples/store/.next/static

这段 Dockerfile 展示了三个核心技巧:

  • COPY package.json pnpm-lock.yaml .npmrc ./:先只拷贝依赖清单文件并安装依赖,利用 Docker 层缓存避免源码变更后重复安装依赖;
  • COPY --from=deps ...:从deps阶段直接拷贝已安装的node_modules,无需在当前阶段重新安装;
  • COPY --from=builder --chown=refine:nodejs ...:从builder阶段拷贝构建产物,同时用--chown指定文件属主,配合后续USER refine实现以非 root 用户运行容器。

同样的模式也出现在 examples/with-material-ui-vite/Dockerfile 中,其通过COPY --from=deps /app/refine/node_modules ./node_modulesCOPY --from=builder /app/refine/dist ./组合,将 Vite 构建产物拷贝进基于serve的轻量运行镜像。

4. 官方文档中的典型写法

在 refine 文档的 Devtools 容器化示例(documentation/docs/enterprise-edition/devtools/docker.tsx)中,同样能看到COPY的标准用法:先用COPY package*.json ./拷贝依赖清单,执行RUN npm install,再用COPY . .拷贝应用源码。这种"先依赖、后源码"的顺序充分利用了 Docker 层缓存,是值得推广的规范写法。

三、docker cp的底层工作原理

理解docker cp为什么能工作、为什么对已停止的容器也有效,需要回到 Docker 的镜像分层机制。

Docker 使用层(layer)存储镜像。运行容器时,Docker 会在镜像已有分层之上再创建一个新的可写层,容器运行期间的所有变更都记录在该新层中。这个可写层不是一个可以直接挂载给容器的数据卷(volume),容器内的数据实际由**存储驱动(storage driver)**管理。

从数据位置看,容器数据位于其父镜像的**块级存储(block level)**中,docker cp正是在这个层面执行拷贝的。容器本质上是镜像的一个快照(snapshot),它本身并不持有这些块,而是持有一个指向最近父镜像中数据实际所在块的指针。执行docker cp时,Docker 从对应块中读取数据并完成拷贝。

正是这种基于镜像层与块指针的设计,使得docker cp在容器停止后依然可用——数据始终存在且可通过指针定位。

四、COPYADD的区别

ADD指令的历史比COPY更早。二者核心功能相同——向容器/镜像拷贝文件与目录——但ADD额外提供了两项增强特性:

  1. 支持 URL 作为源:指令会先从 URL 下载文件,再拷贝到目标位置。

    ADD http://website.com/folder /destination/folder

    上述命令会下载folder目录下的全部文件并拷贝到容器的/destination/folder

  2. 自动解压压缩文件:当源为本地压缩文件时,ADD会先解压再拷贝。

    ADD source.file.gzip /destination/folder

    该命令会把本地压缩文件解压后放入目标目录。注意:URL 指向的压缩文件不能作为源,被解压的压缩文件必须来自本地文件系统。这一技巧可以避免产生额外的镜像层,从而节省空间。

尽管ADD功能更多,Docker 官方仍出于安全原因不推荐使用ADD,建议:

  • 拷贝本地文件一律使用COPY
  • 需要从网络下载并拷贝文件时,使用curl+RUN指令组合;
  • ADD唯一被认可的使用场景是将本地 tar 包解压进镜像,例如:
ADD rootfs.tar.xz /

五、docker cp的已知限制

docker cp虽然强大,但存在以下边界,务必在方案设计时提前规避:

  • 无法直接在两个容器之间拷贝:官方推荐做法是"多阶段中转"——先把文件从容器 1 拷贝到宿主机,再从宿主机拷贝进容器 2;
  • 无法从 URL 下载并拷贝:源文件必须存在于本地文件系统;
  • 无法拷贝系统虚拟文件:例如/proc/sys/devtmpfs等特殊文件系统下的资源,这些路径的内容是动态生成或与内核相关的,不具备常规文件的可拷贝语义。

六、何时该使用 Docker 文件拷贝

结合开发与运维实践,docker cpCOPY的典型应用场景如下:

  • 导出容器内关键文件以定制镜像:例如先运行官方 Nginx 镜像,再用docker cp把容器内的 Nginx 配置文件拷回宿主机,基于它修改并配合 DockerfileCOPY生成自定义镜像;
  • 调试容器与临时环境:在非生产环境中,手动注入临时配置文件、或提取埋藏在容器深处的日志,docker cp比反复重建镜像快得多——每次代码更新都重新docker build的成本远高于一次即时拷贝;
  • 镜像构建时注入静态资源:通过 DockerfileCOPY将前端构建产物、配置文件、证书等写入镜像,保证每个容器开箱即用(参见 examples/store/Dockerfile 与 examples/with-material-ui-vite/Dockerfile 的完整示例);
  • 多阶段构建产物传递:使用COPY --from在构建阶段之间传递node_modules与构建产物,大幅缩小最终运行镜像体积。

总结

docker cp与 DockerfileCOPY覆盖了容器文件拷贝的两个不同阶段:前者面向运行态,负责宿主机与容器之间的即时搬运,具有"容器可停止、支持整目录、可跨容器"等特性,但无法拷贝系统虚拟文件、无法联网下载;后者面向构建态,把文件固化进镜像,配合多阶段构建的COPY --from可实现高效、轻量的镜像产出。而ADD指令尽管支持 URL 下载与自动解压,官方仍建议仅在解压本地 tar 包时使用。掌握这三者的边界与取舍,就能在容器化开发中精准选择最合适的文件拷贝方案。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询