
本文最初发布于 towards data science 博客,经原作者授权由 InfoQ 中文站翻译并分享。
前言
在一个快速变化的环境中工作,越来越多的工具以开源的方式发布,我的笔记本电脑已被我搞得一团糟。我安装了大量的应用程序,却忘了清理它们。类似地,我也用 Python 做过一些事情——没有虚拟环境,所以基本上,很多库分散在笔记本电脑上的各个地方。
最初,我的解决方案是从笔记本电脑中删除 Python 和所有其他杂七杂八的东西。但后来我发现,如果我为一篇博文或与工作相关的事情做 PoC,最终也会遇到同样的情况。然后我找到了 Docker。我花了一段时间弄明白了它的工作原理,以及自己该如何利用它。
准备工作
如果想跟着我做,那么你应该做好以下准备工作:
从官网下载 Docker
某种 IDE,或者使用终端或 Jupyter Notebooks
理解 Docker 是什么
那么,Docker 是什么?这个魔术大师是怎么解决我的问题的?
Docker 是一个平台。你可以创建一个特定的配置包,并在你自己的隔离环境(我们称之为容器)中运行它。在某种程度上,容器与虚拟机非常相似。你可以轻松创建一个轻量级容器,其中仅包含一些相关的库和应用程序,并在其上运行代码。如果你决定与别人分享代码,则只需要分享 Docker 镜像。
那么镜像又是什么?一些用来设置容器的指令,其中包含所有的配置,以及工作目录中的相关文件。
因为它是一个平台,所以我们有一个服务器-客户端关系。
客户端——最有可能是 CLI,用于和 Docker 引擎交互;
服务器——在 Docker 引擎中编排和映射不同的东西;
注册中心——存储 Docker 镜像的地方,后续可以被其他用户拉取。
好了,理论讲够了,我们来实践一下。
开始
好了,首先检查下 Docker 版本:

如果看到任何 Docker 版本输出,就可以开始了。
如果你多少了解一些终端的基本知识,那么你可能很熟悉下面这条命令:
这条命令会列出所有运行中的进程及其 PID。Docker 也提供了类似的命令:

可以看到,正在运行的 Docker 进程数为 0。如何让 Docker 进程运行起来呢?可以像下面这样:

让我们分别做下说明:
docker—— 在 Docker 上运行;
run—— 创建/拉取并运行容器;
hello-world—— 要加载到容器的镜像。
Postgres 数据库
假如我们要运行 Postgres DB 来做一些 PoC。为此,我们需要完成以下工作:
在 Docker Hub 或我们的私有库中找到相应的 Docker 镜像;
确定我们将要使用的版本;
在 Docker 上运行它。
步骤 1
如果我们在谷歌上搜索 docker Postgres,那么最上面就是指向 Postgres 镜像的 Docker Hub链接。从中我们可以看到关于该镜像的大量信息及其用法。
步骤 2

我们看到,这里有 13.2、13、latest、13-alpine。这是什么意思呢?
举例来说,数字 13.2 表示一个特定的 Postgres 版本。Latest 指最新版本,如果有新版本,就会更新。最后但同样重要的是 alpine 版本。Alpine 版本是应用程序运行所需的最基本的框架。Alpine 版本会尽可能轻量化,它只包含必要的部分,比其他版本小。使用 alpine 的好处是可以最低限度地添加相关的东西。这样可以保证 Docker 容器尽可能小,易于移动和部署。
我们选 13.2。
步骤 3
执行以下命令:

好吧,有东西没运行,让我们看下出了什么问题——我们需要通过**-e**(是指环境变量)传入密码:

我们看到,终端进入运行进程模式,要在同一个会话中执行某些操作,就需要终止 Postgres 进程。有一种方法可以让我们在同一个终端会话上运行 Postgress docker 进程,并做一些事情。我们可以使用**-d**来运行分离式进程。

上述命令返回了一个长字符串。该字符串是 Docker 容器的 ID。如果运行 docker ps,就可以看到 Docker 容器已经启动并运行:

可以看到,命令 docker ps 返回的容器 id 和 docker run 返回的容器 id 不一样。仔细看下可以发现,docker ps 返回的字符串是那个长字符串的子串。Docker 很酷的一点是,它匹配容器 id 的开头,你不需要粘贴其完整版本!
但等一下,我不是说 Docker 是个隔离的环境吗?是的。我们不会对数据库做任何事情,因为我们没有公开任何可以用来与它交互的端口。要做到这一点,我们需要借助**-p**:
Dockerfile
好了,现在你可能会问,为什么这个家伙要向我解释如何输入这么一长串命令来运行一些隔离的东西,重复而乏味。我如何分享它?把命令发给下一个人?那并不是十分有效…
是的,玩一下可以,但要创建一个环境,这就不合适了。这就是为什么要有 Dockerfile。
基本上,Dockerfile 就是所有这些环境变量映射出来的指令。因此,对于 Postgres,可以创建这样一个 Dockerfile:

要进行构建,在 Dockerfile 所在的文件夹下运行如下命令:
输入如下所示:

bd9416c1457a 是新构建的 Docker 镜像的 id。现在,我们可像下面这样运行容器了:

值得一提的是,Docker 在每一步构建 Dockerfile 时,都会创建一个新的 Docker 镜像并将其传递给下一步。所有这些镜像都保存在缓存中,如果有需要就可以重用它们,例如,在末尾添加一个新的环境变量:

它的好处是,如果我们只在末尾做了修改,就可以更快地构建镜像。我们只需要从头开始构建新添加的部分!不过,如果我们要改变密码和用户名的位置,就不得不重新构建镜像了:

与容器交互
如果你希望优雅地关闭容器,可以使用如下命令:
你也可以使用如下命令杀掉它:
稍后,你可以使用如下命令查看已经停掉的进程:

如果要恢复容器,则可以通过以下命令:

运行多个 Docker 进程
好了,运行一个 Docker 进程很简单;我们创建一个 Dockerfile 并运行它。如果我另外还需要一个进程,我也可以创建并运行它。但这很快就会失去控制,因为每个 docker run 都需要设置端口和其他信息。
对于这种情况,可以使用 docker-compose。
Docker compose
这是一个 YAML 文件,关于构建内容以及如何使用多个不同的 Dockerfile,其中包含了更多的信息。

让我来介绍一下:
version—— Compose 文件格式的版本。对照Docker文档,看下你的 Docker 引擎是否兼容;
app —— 使用一些预定义 Docker 镜像(即 airflow)的应用程序/服务,8080 端口映射到本机的 8080 端口;
db —— 一个数据库应用程序,我们将使用数据库目录下的 Dockerfile 把它容器化。
如果我们已经有这个文件了,该如何构建呢?如果已经在这个目录下,则可以执行以下命令:
此外,你也可以指定这个文件的完整路径:
总结
Docker 简化了开发工作。创建一个镜像,在上面运行你的应用,看看它是否可以工作。
我之所以使用 Docker,有以下几个方面的考虑:
需要一个隔离的环境来进行基准测试/测试等(借助 GitHub Actions 和 Travis,你可以在 Docker 镜像中测试你的应用);
测试版本升级,看看是否造成了什么破坏;
如果你懒得清理本机环境,它就会变得一团糟。
顺便说一下,代码托管在GitHub(https://github.com/TomasPel/workshops/tree/main/docker_101)上,你可以在 Jupyter Notebook 中交互式地运行它并查看结果。
原文链接:
评论