Docker Basics for Machine Learning Applications

探讨 Docker 在机器学习(ML)场景中的使用。本文是系列文章的第一篇,将介绍一些 Docker 的基础知识,这些知识适用于 ML 应用程序。

安装 Docker

Docker 可以在多个平台上运行,包括适用于 Windows 和 macOS 的 Docker Desktop,以及适用于各种 Linux 发行版的 Docker Server,无论是在 Intel/AMD 还是 ARM 处理器上。可以在 Docker 官网找到适合平台的安装指南。

为什么选择 Docker

Docker 是一种轻量级技术,用于在隔离环境中打包和执行软件组件。这样的包被称为“容器镜像”(或简称“镜像”),而镜像代码执行的环境被称为“容器”。

Docker 技术确保每个容器的 Python 和系统依赖关系完全隔离,这比虚拟环境提供的隔离程度更高。同时,这项技术允许在运行时环境中实现最大的可移植性。在许多情况下,同一个容器可以在本地工作站或服务器上运行,无论是在本地还是云端。

为了更好地理解在后续文章中将做出的一些设计选择,值得花时间了解 Docker 的基础知识。

层(Layers)

Docker 镜像由多个只读层组成。每一层只包含当前层与前一层之间的差异。构建镜像时,只有发生变化的层以及随后的层会被刷新。这就是为什么定义 Docker 层时,应该按照从最静态到最“动态”的顺序排列,这可以大大减少构建镜像所需的时间。

层的数量会影响镜像大小(和构建时间),因此建议使用单个 RUN 语句执行多个 Linux 命令(单个 RUN = 单个层)。

Dockerfile

Dockerfile 定义了镜像。让考虑一个非常简单的例子:

FROM python:3.7-slim RUN apt-get update & amp; apt-get install python3-numpy COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY app /app WORKDIR app ENTRYPOINT ["python", "app.py"] CMD ["--input", "1234"]

上述语句的含义如下:

  • FROM 定义基础镜像,格式为 :。如果本地没有这样的镜像,Docker 会尝试从已知的仓库(默认是 https://hub.docker.com)中拉取。
  • RUN 执行 Linux 命令。
  • COPY 将本地文件从主机上下文复制到镜像中。
  • WORKDIR 切换当前文件夹(如果需要,则创建它)。
  • ENTRYPOINTCMD 定义每次容器启动时执行的命令。如果这两个语句一起使用,ENTRYPOINT 反映命令的常量部分,而 CMD 则是其参数。

以下语句用于构建镜像:

$ docker build -t : .

注意,末尾的 "."(点)表示当前文件夹作为 Docker 上下文(简而言之,是复制到容器的文件和 Dockerfile 的根文件夹位置)。

“最新”并非最佳选择

默认情况下,如果未明确提供标签,Docker 会使用“最新”标签作为基础镜像的标签。类似于为 Pip 或 Conda 定义 Python 依赖时指定包版本,应该始终为基镜像添加一个预定的标签,而不是使用“最新”默认标签。这可以在生产环境中节省很多麻烦。虽然这并不能保证每次构建的镜像100%相同,但它显著降低了引入有害更改的风险。

不同平台上的进程隔离

Docker 根据宿主操作系统略有不同。在内部,Docker 依赖于四个核心 Linux 特性:联合文件系统、Linux 进程、命名空间和 cgroups。

联合文件系统(UnionFS)是一种用于处理镜像层的技术。在 Linux 上的 Docker Server 的情况下,所有容器与宿主共享单个内核。

剩下的三个特性——进程、命名空间和 cgroups——确保了容器的正确隔离。在 Windows 和 Mac 上的 Docker Desktop 的情况下,会在宿主机器上安装 Linux VM,所有运行的容器共享其内核。

考虑到上述情况,很容易理解为什么在 Linux 服务器上使用默认的 root 用户运行容器是一个非常糟糕的主意。如果只有单个容器在单个专用机器上运行(这通常是云部署的情况),问题稍微不那么危险。

无论如何,即使在使用 Docker Desktop 的情况下,也不应该以 root 用户身份运行容器代码。

带数据运行容器

容器执行速度极快的秘诀在于,当它启动时,不会复制任何数据。只会在只读镜像层的堆栈上添加一个单一的(最初非常薄的)可读写容器层。容器执行期间对文件的所有更改都存储在这个新层中,作为“增量”使用联合文件系统。

当容器被移除时,这个可读写层也会随之被删除。这就是为什么应该始终将容器数据视为临时的。如果关心容器处理的数据,需要一个卷。根据需求和宿主环境,它可能由 Docker 实例持久化,或者映射到本地或云文件夹。

架构问题

Docker 不是魔法——它依赖于运行它的硬件。这意味着可能仍然需要为 Intel/AMD 和 ARM 处理器使用略有不同的镜像。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485