Docker best practices for data scientists

Author | Thushan Ganegedara

Translator | meniscus, Zebian | TANG lead

Head Figure | CSDN download from Eastern IC

Exhibition | CSDN (ID: CSDNnews)

As a data scientist, every day I have to deal with Docker. Create a mirror, update content and Python scripting has become my daily work. In the process, I often lamented: "How I wish I had known method before."

Therefore, I will explore some of Docker best practices for data science project will be used in this article. Although unable to do exhaustive, but my work covers most of the data scientist.

This article assumes that you have some understanding of the basics of Docker's. For example, you know the purpose of Docker, and can easily write Dockerfile, and understand Docker command (such as RUN, CMD, etc.). If you lack of understanding of the basics, then read the documentation on Docker official website.

Why Docker?

Since its launch, Docker has been sweeping the globe. In the days before the Docker appears, the virtual machine to fill the gaps in this field. However, Docker provides functionality far exceeds the virtual machine.

Docker advantage

  • Isolation: regardless of the underlying operating system / infrastructure, installed software, updates, how to change, Docker can provide an isolated environment.

  • Lightweight: Sharing OS kernel, do not need to assign each container OS kernel.

  • Performance: lightweight, you can run multiple containers at the same time on the same operating system.

Docker entry

Docker contains three important concepts.

Mirroring: This is caused by libraries and binaries can be run consisting of a collection on behalf of a development / production / test environment required. You can download / create a mirror in the following manner.

  • 从镜像仓库中提取,例如:docker pull alpine。这个命令将在计算机的本地查找一个名为 alpine 的镜像,如果找不到,就去 Dockerhub 中查找。

  • 使用 Dockerfile 在本地构建镜像,例如:docker build . -t <image_name>:<image_version>。这个命令不是下载/拉取镜像,而是构建自己的镜像。这种说法也不完全,因为 Dockerfile 包含以 FROM <base-image>开头的行,该行会查找以其开头的基本镜像,如果找不到,则从 Dockerhub 中提取。

容器:容器是镜像的运行实例。你可以通过如下命令建立一个容器:`docker container run <arguments> <image> <command> 。例如,根据 alpine 创建一个容器的命令为:docker container run -it alpine /bin/bash。

卷:我们利用卷来存储容器使用的数据(例如日志、下载的数据等)。此外,卷可以在多个容器之间共享。你可以通过以下几种方式使用卷。

  • 创建卷:卷的创建命令为:docker volume create <volume_name>。请注意,如果删除卷,则存储在其中的信息会丢失。

  • 绑定挂载卷:将宿主上现有的卷绑定挂载到容器的命令为:-v <source>:<target>。例如,如果你需要将卷/my_data 挂载到容器,并作为卷/data,则可以执行以下命令:docker container run -it -v /my_data:/data alpine /bin/bash。你在挂载的时候所做的更改将反映在宿主上。

一、创建镜像

1. 镜像不宜过大,应避免缓存

构建镜像时,首先你必须做两件事:

  • 安装 Linux 软件包

  • 安装 Python 库

在安装这些软件包和库时,软件包管理器将缓存数据,以便你再次安装时能够使用本地的数据。但这会增加镜像的大小,完全没有必要。Docker 镜像应该尽可能保持轻量级。

在安装 Linux 软件包时,请记住在 apt-get installl 命令中添加一行,就可以删除所有的缓存数据。

RUN apt-get update && apt-get install tini && \
 rm -rf /var/lib/apt/lists/*

在安装 Python 软件包时,为避免缓存,请按照以下操作:

RUN pip3 install <library-1> <library-2> --no-cache-dir`

2. 在 requirements.txt 中单独指定 Python 库

最好在 requirements.txt 中单独指定 Python 库,然后通过以下命令安装库。

RUN pip3 install -r requirements.txt --no-cache-dir

这种做法可以很好地区分 Dockerfile 和 Python 的库。此外,如果你有多个 Dockerfile(例如用于生产/开发/测试),而且希望它们都安装相同的库,则可以轻松地重用这个命令。requirements.txt 文件中只包含了一堆库名。

numpy==1.18.0
scikit-learn==0.20.2
pandas==0.25.0

3. 修改库版本

请注意,你需要在 requirements.txt 中冻结要安装的版本,这非常重要。因为否则,每次构建 Docker 镜像时,你都有可能安装不同的版本,那么就会面临“依赖性地狱”。

二、容器的运行

1. 多使用非 root 用户

在运行容器时,如果你不指定运行的用户身份,则会默认使用 root 用户。我曾经很天真,特别喜欢使用 sudo 或 root。然而,我遭到了惨痛的教训,拥有不必要的特权会引发不必要的麻烦。

如果你想以非 root 用户身份运行容器,只需运行如下命令:

docker run -it -u <user-id>:<group-id> <image-name> <command>

如果你想进入现有的容器中,则可以运行如下命令:

docker exec -it -u <user-id>:<group-id> <container-id> <command>

例如,你可以通过将<user-id>指定为$(id -u),<group-id>指定为$(id -g),使容器内的用户 ID 和组 ID 与宿主匹配。

注意,不同的操作系统分配用户 ID 和组 ID 的方式也不同。例如,MacOS 上的用户 ID/组 ID 可能是 Ubuntu 容器内预先分配/保留的用户 ID /组 ID。

2. 创建一个非特权用户

我们能够以非 root 用户身份从宿主登录到容器。但是,如果你以这种方式登录,那么你在容器中没有用户名。因为,很明显容器不知道该用户 ID 来自何处。而且,每次你想启动或 exec 容器的时候,都需要记住并输入这些用户 ID 和组 ID。为了避免这些麻烦,你可以在 Dockerfile 中创建用户/组。

ARG UID=1000
ARG GID=1000
  • 首先将 ARG UID=1000 和 ARG GID=1000 添加到 Dockerfile 中。UID 和 GID 是容器中的环境变量,你可以在 docker build 阶段将值传递给该容器(默认为 1000)。

  • 然后通过 RUN groupadd -g $GID john-group,在镜像中添加带有组 ID GID 的 Linux 组。

  • 接下来,通过 useradd -N -l -u $UID -g john-group -G sudo john,在镜像中添加带有用户 ID UID 的 Linux 用户。在此,我们将 john 添加到 sudo 组中。但这是可选的。如果你百分百确定你不需要 sudo 权限,则可以忽略这一步。

然后,在镜像构建期间,你可以为这些参数传递值,例如:

docker build <build_dir> -t <image>:<image_tag> --build-arg UID=<uid-value> --build-arg GID=<gid-value>

举个例子:

docker build . -t docker-tut:latest --build-arg UID=$(id -u) --build-arg GID=$(id -g)

拥有一个非特权用户在运行不需要 root 权限的进程时非常有用。例如,如果你的 Python 脚本只是从目录中读写数据,那么完全没必要用 root 身份运行。另外,还有一个好处,如果容器内的用户 ID 和组 ID 完全与宿主匹配,则你创建的所有文件都将拥有宿主用户的所有权。因此,如果你挂载这些文件(或创建新文件),那么就像在宿主上创建的一样。

三、创建卷

1. 利用卷做分离

作为数据科学家,你需要处理各种数据、模型和代码。你可以将代码放在一个卷中(比如/app),将数据放在另一个卷中(比如/data)。这样你的 Docker 镜像就能拥有一个很好的结构,且可以摆脱所有宿主级别的依赖关系。

我所说的这种依赖是什么意思?假设你将代码放在/home/<user>/code/src 中。如果你将/home/<user>/code/src 复制/挂载到卷/app 中,而/home/<user>/code/data 挂载到卷/data 中,那么即便宿主上的代码和数据位置发生变化,也没有关系。一旦挂载到 Docker 镜像后,它们的位置就不会发生变化。因此,你可以按照如下步骤在 Python 脚本中定义这些路径。

data_dir = "/data"
model_dir = "/models"
src_dir = "/app"

你可以通过以下方法,将必要的代码和数据复制到镜像中:

COPY test-data /data
COPY test-code /app

请注意,test-data 和 test-code 都是宿主上的目录。

2. 在开发过程中挂载目录

挂载的好处在于,容器中的变化都会反映到宿主上。在开发和调试项目时,这种做法非常方便。我们来看一个例子。

假设你通过以下命令创建了 docker 镜像:

docker build <build-dir> <image-name>:<image-version>

那么,现在可以使用以下命令建立镜像的一个容器:

docker run -it <image-name>:<image-version> -v /home/<user>/my_code:/code

然后,你就可以在容器内运行代码,同时还能进行调试,且代码的变更都会反映到宿主上。而这主要得益于在容器中使用相同的宿主用户 ID 和组 ID。你所的所有改动都好似来自宿主上的这个用户。

3. 永远不要挂载宿主的关键目录

我曾做过一件好笑的事,我将机器的主目录挂载到 Docker 容器中,而且还想修改主目录的权限。不用说,我无法再登陆系统了,并花了好几个小时才修复。因此,你应该只挂载所需的内容。

例如,假设开发期间你有三个想要挂载的目录:

/home/<user>/my_data
/home/<user>/my_code
/home/<user>/my_model

你可能很想用一行代码直接挂载/home/<user>目录,但是编写三行代码来分别挂载这些单独的子目录,这绝对值得,因为可以为你节省数小时的艰辛工作(甚至是几天)。

四、其他小贴士

1. 了解 ADD 和 COPY 之间的区别

你可能知道 Docker 有两个命令叫做 ADD 和 COPY,那么二者究竟有何不同?

  • ADD:你可以使用 ADD <url>从 URL 下载文件。

  • ADD:当某个压缩文件(例如 tar.gz)会从指定的位置提取文件时,请使用 ADD。

  • COPY:将某个文件/文件夹复制到容器的指定位置。

2. ENTRYPOINT 和 CMD 之间的区别

我打个比方,你可以把 ENTRYPOINT 当成一辆车,而 CMD 就是车上的控制器(例如加速器、制动器、方向盘)。ENTRYPOINT 本身不会执行任何操作,它只是容纳你在容器中执行的操作的盒子。它不会处理你推送到容器的任何命令。

命令 CMD 是容器内实际执行的命令。例如,bash 会在容器内创建一个 shell,那么你就可以像在 Ubuntu 的普通终端上一样使用该容器。

3. 将文件复制到现有容器

我们经常犯这样一个错误:创建好容器后,却忘记将文件添加到镜像中。构建镜像需要花费很长时间。有什么办法可以避免这个问题,自动将文件添加到现有容器中呢?

你可以使用 docker cp 命令执行该操作。很简单:

docker cp <src> <container>:<dest>

下次进入容器时,你就可以看到复制的文件躺在<dest>中。但是,请不要忘记修改 Dockerfile,以便能在下次构建时复制必要的文件。

英文:Docker Best Practices for Data Scientists

链接:https://towardsdatascience.com/docker-best-practices-for-data-scientists-2ed7f6876dff

作者:Thushan Ganegedara,数学科学家。

本文为 CSDN 翻译,转载请注明来源出处。

【End】

推荐阅读 

百度移动平台关闭安卓渠道;苹果将推 iPhone SE 2 代;微软开源 Scalar | 极客头条

 机器学习霸占高薪榜、区块链偃旗息鼓?2020 年软件工程师状况解读!

编程语言性能实测,Go比Python更胜一筹?

Python 爬取李子柒辣椒酱 1794 条数据,有人嫌牛肉粒太小...... | 原力计划

大神如何一招完美解决Hadoop集群无法正常关闭的问题!| 博文精选

比特币技术栈的演进

你点的每一个在看,我认真当成了喜欢

猛戳“阅读原文”,填写中国远程办公-调查问卷

发布了1716 篇原创文章 · 获赞 4万+ · 访问量 1538万+

Guess you like

Origin blog.csdn.net/csdnnews/article/details/104386213