趋近智
docker run 运行训练任务docker-compose.yml中定义服务将项目代码和相关文件引入 Docker 镜像,可以确保容器具备执行机器学习任务所需的一切,例如训练模型或运行推理服务。此步骤通常在 Dockerfile 中定义好基础环境和安装依赖之后进行。完成此操作的主要指令是 COPY 和 ADD。
COPY 指令是最直接、最常用的一种方法,用于将文件从本地机器(特别是构建上下文)复制到镜像文件系统中。它的语法很简单:
COPY <src>... <dest>
<src>: 指定您要复制的本地机器上的文件或目录(相对于构建上下文根目录)。您可以指定多个源。支持通配符。<dest>: 指定源文件/目录应复制到容器镜像内部的路径。如果目标路径不存在,Docker 将创建它。如果源是目录,则目标路径必须以 / 结尾或是一个已存在的目录。对于一个典型的机器学习项目,结构上包含一个存放 Python 代码的 src 目录并且根目录下有一个 requirements.txt 文件,您可以像这样使用 COPY:
# 定义工作目录(前面已介绍)
WORKDIR /app
# 先复制 requirements 文件以利用缓存
COPY requirements.txt .
# 安装依赖(前面已介绍)
RUN pip install --no-cache-dir -r requirements.txt
# 复制项目其余代码
COPY ./src ./src
在此示例中:
/app。requirements.txt 从构建上下文根目录复制到镜像内的 /app 目录。pip install。src 目录的内容复制到镜像内 /app 目录下的一个名为 src 的目录中(即 /app/src)。这种顺序对于构建性能很重要,我们稍后在讨论构建缓存时会提到。
Docker 也提供了 ADD 指令,它的语法与 COPY 类似:
ADD <src>... <dest>
ADD 对于本地文件和目录执行与 COPY 相同的功能,但包含两个额外特性:
<src> 是一个 URL,Docker 将从该 URL 下载文件并将其复制到 <dest>。权限设置为 600。<src> 是一个识别出的压缩归档格式(例如 tar, gzip, bzip2, xz),Docker 将自动将其解压到 <dest> 中作为一个目录。尽管这些特性可能看起来很便利,但它们也可能使您的构建较难预测。例如,在构建过程中从 URL 下载会将镜像构建过程与网络可用性和远程源的稳定性耦合起来。自动解压有时会根据归档结构产生意外结果。
建议: 为清晰和可预测性,对于传输本地文件和目录,优先选择 COPY 而非 ADD。仅在您特别需要其 URL 下载或自动解压功能时才使用 ADD,并理解潜在影响。对于下载文件,通常更好的做法是在 RUN 指令中使用 curl 或 wget 等工具,这样可以对下载过程有更多的控制(例如,错误处理、重试)。
当您运行 docker build 时,Docker 客户端首先将指定为构建上下文的目录(通常是 .)打包并发送到 Docker 守护进程。此上下文包含所有文件和子目录。发送大型、不必要的文件(例如数据集、虚拟环境、Git 历史、IDE 配置)会减慢构建过程,如果意外复制,还可能使您的镜像变得臃肿。
为防止这种情况,在您的构建上下文根目录(与 Dockerfile 相同的目录)中创建一个名为 .dockerignore 的文件。列出您要排除的文件或目录,使用类似于 .gitignore 的语法。
这是一个典型机器学习项目的 .dockerignore 示例:
# Git 文件
.git
.gitignore
# Python 虚拟环境
venv/
*.pyc
__pycache__/
# IDE / 编辑器特定文件
.vscode/
.idea/
*.swp
# 大型数据文件(稍后通过卷/挂载管理)
data/
datasets/
# 模型文件(如果较大或单独管理)
models/
*.pt
*.h5
*.onnx
*.pkl
# Docker 文件
Dockerfile
.dockerignore
# 其他临时或本地文件
*.log
notebooks/output/
通过使用 .dockerignore,您可以确保只有必要的代码和配置文件被发送到守护进程,并通过 COPY 或 ADD 可用于复制到您的镜像中。这会带来更快的构建速度和更小、更安全的镜像。
Docker 分层构建镜像。Dockerfile 中的每个指令(如 RUN, COPY, ADD)都会创建一个新层。Docker 会使用构建缓存:如果与 COPY 指令相关的文件自上次构建以来没有更改,并且之前的层也已缓存,Docker 会重用现有层,而不是再次执行该指令。
这对于耗时步骤(如依赖安装)尤为重要。考虑以下两种方法:
不太理想:
WORKDIR /app
COPY . . # 一次性复制所有内容
RUN pip install -r requirements.txt
# ... Dockerfile 的其余部分
如果您的项目中有任何文件发生更改(即使是微小的代码修改),COPY . . 层就会失效,Docker 必须重新运行可能耗时的 pip install 命令,即使 requirements.txt 没有更改。
更优:
WORKDIR /app
# 1. 仅复制 requirements 文件
COPY requirements.txt .
# 2. 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 3. 复制应用程序代码的其余部分
COPY . .
# ... Dockerfile 的其余部分
在这个改进版本中:
.py 文件)更改,Docker 会重用包括 RUN pip install 步骤在内的所有缓存层(因为 requirements.txt 没有更改)。它只需要重新执行最后的 COPY . . 指令,这非常快。requirements.txt 文件本身被修改时,依赖安装层才会被重新构建。经过周全考虑地组织 COPY 指令,将依赖定义与应用程序代码分开,可以显著加快迭代开发周期。
您应该将预训练模型、数据集或其他大型文件直接 COPY 到您的镜像中吗?
通常,对于数据集以及较大或频繁更新的模型文件,将它们直接复制到镜像中不是推荐的做法。使用 Docker 卷或绑定挂载的替代策略提供更大的灵活性和更高的效率。这些技术允许您将数据和文件与镜像本身分离,使更新更简便,并保持镜像更小。我们将在第 3 章“容器中的数据和模型管理”中详细介绍这些数据管理技术。
目前请您理解,COPY 和 ADD 对于将您的应用程序代码和配置放入镜像不可或缺。使用 .dockerignore 排除不必要的文件,并组织您的 COPY 操作,以最大限度地发挥 Docker 构建缓存的优势。
这部分内容有帮助吗?
COPY 和 ADD 指令、其语法和操作差异。© 2026 ApX Machine Learning用心打造