在过去的 5 年里,我听到了很多关于 docker 容器的嗡嗡声。似乎我所有的软件工程朋友都在使用它们来开发应用程序。我想弄清楚这项技术如何使我更有效率,但我发现网上的教程要么太详细:阐明我作为数据科学家永远不会使用的功能,要么太肤浅:没有给我足够的信息来帮助我了解如何快速有效地使用 Docker。
我写了这个快速入门,所以你不必解析所有的信息,而是可以学习你需要知道的事情,以便快速入门。
您可以将 Docker 视为轻量级虚拟机,其中包含运行应用程序所需的一切。docker 容器可以捕获系统状态的快照,以便其他人可以快速重新创建您的计算环境。这就是本教程需要了解的全部内容,但有关更多详细信息,您可以前往此处。
增强您的工程能力:熟悉 Docker 可以让您将模型或分析部署为应用程序(例如,作为可以提供预测服务的 REST API 端点),从而使其他人可以访问您的工作。此外,作为数据科学工作流程的一部分,您可能需要与之交互的其他应用程序可能存在于 Docker 容器中,例如数据库或其他应用程序。
在我们深入之前,熟悉 Docker 术语很有帮助:
Dockerfile
我将在帖子的其余部分使用此术语,因此如果您迷路了,请参阅此列表!这些术语之间很容易混淆,尤其是在图像和容器之间——所以在阅读时要保持警惕!
在创建 docker 容器之前,创建一个将定义映像的 Dockerfile 非常有用。让我们慢慢地浏览下面的 Dockerfile。可以在本教程随附的 Github 存储库中找到此文件。
# reference: https://hub.docker.com/_/ubuntu/
FROM ubuntu:16.04
# Adds metadata to the image as a key value pair example LABEL version="1.0"
LABEL maintainer="Hamel Husain "
##Set environment variables
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
RUN apt-get update --fix-missing && apt-get install -y wget bzip2 ca-certificates \
build-essential \
byobu \
curl \
git-core \
htop \
pkg-config \
python3-dev \
python-pip \
python-setuptools \
python-virtualenv \
unzip \
&& \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN echo 'export PATH=/opt/conda/bin:$PATH' > /etc/profile.d/conda.sh && \
wget --quiet https://repo.continuum.io/archive/Anaconda3-5.0.0.1-Linux-x86_64.sh -O ~/anaconda.sh && \
/bin/bash ~/anaconda.sh -b -p /opt/conda && \
rm ~/anaconda.sh
ENV PATH /opt/conda/bin:$PATH
RUN pip --no-cache-dir install --upgrade \
altair \
sklearn-pandas
# Open Ports for Jupyter
EXPOSE 7745
#Setup File System
RUN mkdir ds
ENV HOME=/ds
ENV SHELL=/bin/bash
VOLUME /ds
WORKDIR /ds
ADD run_jupyter.sh /ds/run_jupyter.sh
RUN chmod +x /ds/run_jupyter.sh
# Run a shell script
CMD ["./run_jupyter.sh"]
FROM ubuntu:16.04
FROM 语句封装了 Docker 最神奇的部分。此语句指定要在其上构建的基础映像。使用 FROM 指定基本映像后,Docker 将在本地环境中查找名为 ubuntu:16.04 的映像 — 如果在本地找不到它,它将搜索您指定的 Docker 注册表,默认情况下为 DockerHub。这种分层机制很方便,因为您经常希望在 Ubuntu 等操作系统之上安装程序。与其担心如何从头开始安装 Ubuntu,不如简单地在官方 Ubuntu 映像之上构建!Dockerhub上托管了各种各样的Docker镜像,包括那些提供的不仅仅是操作系统的镜像,例如,如果你想要一个已经安装了Anaconda的容器,你可以在官方的anaconda docker镜像之上构建一个容器。最重要的是,您还可以随时发布您构建的映像,即使该映像是通过在另一个映像上分层创建的!可能性是无穷无尽的。
在这个例子中,我们指定我们的基础映像是 ubuntu:16.04,它将查找一个名为 ubuntu 的 DockerHub 存储库。冒号 — 16.04 之后的映像名称部分是允许您指定要安装的基础映像版本的标记。如果你导航到 Ubuntu DockerHub 存储库,你会注意到不同版本的 Ubuntu 对应着不同的标签:
例如,在撰写本文时,ubuntu:16.04、ubuntu:xenial-20171201、ubuntu:xenial 和 ubuntu:latest 都引用了 Ubuntu 版本 16.04,并且都是同一映像的别名。此外,此存储库中提供的链接将您链接到用于为每个版本构建映像的相应 Dockerfile。您不会总是在 DockerHub 存储库中找到 Dockerfiles,因为维护者可以选择包含他们如何制作映像的 Dockerfile。我个人发现查看其中几个 Dockerfile 以更多地了解 Dockerfile 很有用(但请等到您完成本教程!
有一个标签值得特别提及 — :latest 标签。此标记指定如果未在 FROM 语句中指定标记,则默认情况下将提取的内容。例如,如果 FROM 语句如下所示:
FROM ubuntu
然后你最终会拉出 ubuntu:16.04 映像。为什么?— 如果你仔细看上面的截图,你会看到 :latest 标签与 16.04 相关联
关于 Docker 镜像的最后一点:从 DockerHub 随机提取 Docker 镜像时,要做出明智的判断。由恶意行为者创建的 Docker 映像可能包含恶意软件。
此语句将元数据添加到映像中,并且是完全可选的。我添加它是为了让其他人知道该联系谁来了解该映像,也可以这样我就可以搜索我的 docker 容器,尤其是当服务器上有许多容器同时运行时。
LABEL maintainer="Hamel Husain "
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
这允许您更改环境变量,并且非常简单。您可以在此处阅读有关此内容的更多信息。
这通常是完成构建 Docker 映像所需的工作的主力。您可以运行任意 shell 命令(如 apt-get 和 pip install)来安装所需的包和依赖项。
RUN apt-get update --fix-missing && apt-get install -y wget bzip2
build-essential \
ca-certificates \
git-core \
...
在这种情况下,我正在安装一些我喜欢的实用程序,例如curl,htop,byobu,然后安装anaconda,然后安装基本anaconda安装中没有的其他库(向上滚动到完整的Dockerfile以查看所有RUN语句)。
RUN 语句后面的命令与 Docker 无关,而是您自己安装这些软件包时会运行的普通 linux 命令,因此如果您不熟悉其中一些软件包或 linux 命令,请不要担心。另外,作为进一步的建议——当我第一次开始学习 docker 时,我查看了 Github 或 DockerHub 上的其他 Dockerfile,并将我想要的相关部分复制并粘贴到我的 Dockerfile 中。
关于 RUN 语句,您可能会注意到的一件事是格式设置。每个库或包都整齐地缩进并按字母顺序排列,以提高可读性。这是 Dockerfiles 的普遍约定,所以我建议你采用它,因为它会简化协作。
如果您尝试公开端口,则此语句很有用 - 例如,如果您从容器或某种 Web 服务内部提供 jupyter 笔记本。Docker的文档在解释EXPOSE 语句方面非常好:
该指令实际上并不发布端口。它充当构建映像的人和运行容器的人员之间的一种文档类型,关于要发布的端口。若要在运行容器时实际发布端口,请使用标志 on 发布和映射一个或多个端口,或使用标志发布所有公开的端口并将其映射到高阶端口。
EXPOSE
-p
docker run
-P
VOLUME /ds
此语句允许您在 docker 容器和主机之间共享数据。VOLUME 语句允许您装入外部装入的卷。主机目录仅在运行容器时声明(因为您可能在不同的计算机上运行此容器),而不是在定义映像时声明*。目前,您只需指定要与主机容器共享的 docker 容器中文件夹的名称。
从 docker 用户指南:
*主机目录在容器运行时声明:主机目录(挂载点)本质上依赖于主机。这是为了保持图像的可移植性。因为无法保证给定的主机目录在所有主机上都可用。因此,您无法从 Dockerfile 中挂载主机目录。该指令不支持指定参数。创建或运行容器时必须指定装入点。
VOLUME
host-dir
此外,这些卷旨在将数据保存在容器的文件系统之外,如果您正在处理不希望使 docker 映像膨胀的大量数据,这通常很有用。保存 docker 映像时,此 VOLUME 目录中的任何数据都不会保存为映像的一部分,但将保存容器中此目录之外的数据。
WORKDIR /ds
此语句设置工作目录,以防要在另一个命令中引用没有绝对路径的特定文件。例如,Dockerfile 中的最后一条语句是
CMD [“./run_jupyter.sh”]
假设工作目录为 /ds
编辑 8/24/2020:您现在应该使用 COPY 语句而不是 ADD 语句。
ADD run_jupyter.sh /ds/run_jupyter.sh
此命令允许您在运行 docker 容器时将文件从主机复制到 docker 容器中。我使用它来执行 bash 脚本并将有用的东西导入容器中,例如 .bashrc 文件。
请注意,此处未完全指定主机容器的路径,因为主机路径相对于运行容器时指定的上下文目录(稍后将讨论)。
碰巧的是,当我运行这个容器时,我会将文件run_jupyter.sh在上下文目录的根目录中,所以这就是为什么源文件前面没有路径的原因。
从用户指南:
ADD
... 该指令从中复制新文件、目录或远程文件 URL,并将它们添加到路径 中的映像文件系统中。
ADD
Docker 容器的设计理念是它们是短暂的,只会保持足够长的时间来完成要运行的应用程序。但是,对于数据科学,我们通常希望保持这些容器运行,即使其中没有任何活动运行。许多人通过简单地运行一个 bash shell(除非你杀死它,否则不会终止)来实现这一点的一种方式。
CMD [“./run_jupyter.sh”]
在上面的命令中,我正在运行一个实例化 Jupyter 笔记本服务器的 shell 脚本。但是,如果您没有任何要运行的特定应用程序,但希望容器在不退出的情况下运行 — 则可以使用以下命令简单地运行 bash shell:
CMD ["/bin/bash"]
这是有效的,因为 bash shell 在您退出之前不会终止,因此容器保持正常运行。
从用户指南:
a 中只能有一个指令。如果您列出多个,则只有最后一个会生效。
CMD
Dockerfile
CMD
CMD
a 的主要目的是 为执行容器提供默认值。这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定指令。
CMD
ENTRYPOINT
docker commit new_image_name:tag_name(optional)
例如,如果我想将名为 container1 的容器的状态保存为名为 hamelsmu/tutorial:v2 的图像,我只需运行以下命令:
docker commit container_1 hamelsmu/tutorial:v2
你可能想知道为什么hamelsmu/在镜像名称的前面——这只是让以后更容易地将这个容器推送到DockerHub,因为hamelsmu是我的DockerHub用户名(稍后会详细介绍)。如果您在工作中使用 Docker,则很可能有一个内部私有 Docker 存储库,您可以将 Docker 映像推送到该存储库。
docker ps -a -f status=running
如果您在没有 status=running 标志的情况下运行上述命令,那么您将看到系统上所有容器的列表(即使它们不再运行)。这对于跟踪旧容器很有用。
docker images
首先创建一个 DockerHub 存储库并适当地命名您的映像,如此处所述。这将涉及运行命令docker login以首先连接到DockerHub或其他注册表上的帐户。例如,要将图像推送到此容器,我首先必须将我的本地映像命名为 hamelsmu/tutorial(我可以选择任何标签名称) 例如,CLI 命令:
docker push hamelsmu/tutorial:v2
将上述 docker 映像推送到带有标记 v2 的此存储库。应该注意的是,如果你让你的镜像公开可用,其他人可以简单地在你的镜像上分层,就像我们在本教程中向 ubuntu 镜像添加层一样。这对于寻求复制或扩展您的研究的其他人非常有用。
现在您已经知道如何操作 Docker,您可以执行以下任务:
我们只是触及了 Docker 的表面,您可以做的还有很多。我专注于Docker的领域,我认为作为数据科学家,你最常遇到的领域,并希望给你足够的信心开始使用它。以下是一些在 Docker 之旅中帮助我的资源: