Alan Hou的个人博客

精通Docker第三版 – 第十三章 Docker工作流

《精通Docker第三版》完整目录:

第一章 Docker概览
第二章 创建容器镜像
第三章 存储和发布镜像
第四章 管理容器
第五章 Docker Compose
第六章 Windows容器
第七章 Docker Machine
第八章 Docker Swarm
第九章 Docker和Kubernetes
第十章 在公有云上运行Docker
第十一章 Portainer – 一个Docker的GUI
第十二章 Docker安全
第十三章 Docker工作流
第十四章 Docker进阶

本章中,我们将来学习Docker以及Docker的不同工作流。一起学习不同的部分来在生产环境无忧地使用Docker。首先来一览本章将涵盖的内容:

技术准备

本章中,我们将使用桌面Docker。和此前章节一样,我使用个人偏好的操作系统macOS。我们将运行的Docker命令可以在至此安装的三种操作系统上使用。但有一些极少数的支持命令,可能仅用于macOS和Linux系统。

本章的完整代码参见GitHub仓库:https://github.com/alanhou/mastering-docker

观看如下视频来查看代码实时操作:

Docker用于开发

我们将通过讨论如何使用Docker协助开发人员来看工作流。在第一章 Docker概览的开头,我们在了解Docker部分讨论的第一件事是开发人员和在本地上开发的问题。到此,我们还没有完全地解决这个问题,下面就来看如何做。

这一部分,我们来了解开发者如何使用Docker for macOS或Docker for Windows以及Docker Compose来在本机开发WordPress项目。

目标是启动WordPress软件,我们要完成如下步骤:

  1. 下载并安装WordPress。
  2. 允许从桌面编辑器访问WordPress文件,如本机上的Atom,Visual或Sublime Text。
  3. 使用WordPress命令行工具(WPCLI)配置和管理WordPress。这允许你停止、启动甚至删除容器而不丢失任何开发内容。

在我们启动WordPress安装之后,先来看看Docker Compose文件以及所运行的服务:

我们可以使用PMSIpilot的docker-compose-viz工具来可视化Docker Compose文件。在dockercompose.yml文件的同一文件夹下运行如下命令来实现:

这会输出一个名为docker-compose.png的文件,你应该会得到下面这样的文件:

你可以使用docker-compose-viz来给出任意Docker Compose文件的可视化展现。从这张图中可以看出,我们定义了4个服务。

第一个服务称为web。该服务是四个中唯一一个对主机网络进行暴露的,它作为WordPress应用的前端。它从https://store.docker.com/images/nginx/运行官方nginx镜像,并充当两个角色。在看它们之前,先来看如下的nginx配置:

可以看到我们使用nginx从/var/www/html/来提供 PHP 以外的所有内容,它通过nginx来在宿主机进行挂载,所有对 PHP 文件的请求被代理到第二个服务上,称为wordpress,端口为9000。nginx配置本身从主机挂载到/etc/nginx/conf.d/default.conf。

这表示nginx容器作为静态内容的网页服务器,即第一个角色,同时也作为动态内容的WordPress容器的代理 ,这是容器所承担的第二个角色。

第二个服务是wordpress,这是官方WordPress镜像:https:// store.docker.com/images/wordpress,我使用了php7.2-fpm-alpine标签。这样我们的WordPress软件在PHP 7.2上运行在使用构建在Alpine Linux之上的PHP-FPM。

ℹ️FastCGI进程管理器(PHP-FPM)是一个有强大功能的PHP FastCGI实现。我们这里它允许PHP以可以绑定端口和传递请求的服务运行,这与在每个服务运行在一个容器的Docker之道相符。

我们在挂载与网页服务相同的web根路径,在主机上是wordpress/web,在服务上为/var/www/html/。一开始主机上的文件夹是空的,但是在WordPress服务启动时,它会监测到没有核心WordPress软件并拷贝一份到这个位置,有效的初始化我们的WordPress软件并拷贝到我们的主机,使我们开始操作的内容就绪。

下一个服务是MySQL,使用官方MySQL镜像(https://store.docker. com/images/mysql/),是我们所使用的四个镜像中唯一一个不使用Alpine Linux的(MySQL,加把劲啊,发布一个基于Alpine Linux的镜像吧)。它使用了debian:stretch-slim。我们传递了一些环境变量,这样在容器初次运行时数据库、用户名和密码都会被创建,如果你使用这个作为项目的一部分请记得修改密码。

如同网页和wordpress容器,我们从主机挂载了一个文件夹。此处为wordpress/mysql,并且我们将其挂载至 /var/lib/mysql/,即MySQL存储数据库和关联文件的默认文件夹。

你会注意到在容器启动时,wordpress/mysql中会产生几个文件,我不推荐使用本地IDE来编辑这些文件。

最后一个服务名称简短,叫wp。它与其它三个服务不同,这个服务在执行时会即刻退出,因为在容器中没有长进程运行。取代长进程,它提供对与我们主wordpress容器完全匹配的环境WordPress命令行的访问。

你会注意到我们与 web和WordPress同样挂载的网页根目录,以及第二个名为/export的挂载,我们会在WordPress配置后了解更多详情。

要启动WordPress,我们仅需运行如下命令来拉取镜像:

这会拉取镜像并启动web, wordpress和mysql服务以及准备好wp服务。在服务启动之前,我们的wordpress文件长这样:

可以看到,我们仅有nginx.conf文件,这是Git仓库的一部分。然后我们使用如下命令来启动容器并查看它们的状态:

你应该可以看到在wordpress文件夹中创建了三个文件夹:export, mysql和web。同时,请记得dockerwordpress_wp_1处于exit状态是意料之中的,不必担心:

打开浏览器交访问http://localhost:8080/,应该会进入标准的WordPress预安装欢迎页面,可以选择你想要安装的语言:

先不要点击Continue,因为那会进入到图形化安装的下一页面。请回到Terminal。

这里我们不使用图形界面来完成安装,转而使用WP-CLI。这么做需要两步。第一步是创建一个wp-config.php文件。运行如下命令来进行创建:

在下面的Terminal输出中可以看到,在运行该命令之前,我们只有一个wp-config-sample.php文件,是核心WordPress自带的。然后在运行该命令之后,我们有了自己的wp-config.php文件:

注意在这个命令中,我们传入了Docker Compose中定义的数据库详情并告知WordPress它要可以在mysql的地址连接数据库服务。

我们已配置了数据库连接的详情,还需要配置WordPress站点来创建管理用户并设置密码。运行如下命令来进行配置:

运行这条命令会产生一个有关email服务的报错,不必担心这条消息,因为这只是本地开发环境。我们不必担忧WordPress软件所发送的邮件:

我们使用了WP-CLI来在WordPress中进行了如下配置:

再次回到浏览器并输入http://localhost:8080/应该会返回一个普通的WordPress站点:

在进一步的操作之前,我们先通过安装和启用来自定义应用的标题:

该命令的输出如下:

然后安装并启用sydney主题:

该命令的输出如下:

通过http://localhost:8080/刷新WordPress页面,应该会看到如下页面:

在打开我们的IDE之前,我们先使用如下命令来销毁这一运行WordPress的容器:

该命令的输出如下:

因为我们的整个WordPress安装,包含所有文件和数据库,都存储在本机上,我们应该可以运行如下命令来返回此前的WordPress站点:

通过访问http:// localhost:8080/来确定它如预期启动并运行了,在桌面编辑器中打开docker-wordpress 文件夹。我使用的是Sublime Text。在你的编辑器中,打开wordpress/web/wp-blog-header.php文件并在打开的 PHP 语句中添加如下行后保存:

该文件如下面这样:

保存之后,刷新你的浏览器,应该在页面的最下端看到IDE中添加的Testing editing in the IDE(以下截图进行了放大,如果你照着操作的话,可能会因为文本较小而不易发现):

我们来看的最后一件事是为什么我们要在wp容器上挂载wordpress/export文件夹。

本章前面已经提到,你不应碰 wordpress/mysql文件夹下的实际内容,这也包含对其的分享。如果你将其打包并发给同事虽然可能可以运行,但不被认为是最佳实践。因此,我们挂载了export文件夹来让我们可以使用WPCLI来做一个数据库转储并导入它。

运行如下命令来实现:

以下Terminal输出显示了导出以及导出前后wordpress/export中的内容,以及最后的MySQL导出内容的前几行:

如果我需要,比如因开发过程的失误,我可以通过运行如下命令来回滚数据库的版本:

该命令的输出如下:

如你所见,我们安装了WordPress,通过使用WP-CLI和浏览器与其进行了交互,编辑了代码并且备份和恢复了数据,都没有安装或配置nginx, PHP, MySQL或WP-CLI。我们也没有登录容器。通过从主机挂载数据卷,在我们关闭WordPress容器时内容是安全的,没有丢失任何修改。

同时,如果需要,我们可以轻易地传送一个项目的拷贝给安装了Docker的同事,在知晓它运行在与我们的安装完全相同的环境上时,只需一条命令他们就可以操作我们的代码了。

最后,我们从 Docker商店使用了官方镜像,我们知道可以安全地将其部署到生产环境,因为它们是以Docker的最佳实践标准来构建的。

小贴士:不要忘记通过运行docker-compose down命令停止并删除WordPress容器。

监控

接下来我们学习监控容器和Docker主机。在第四章 管理容器中,我们讨论了docker container top和docker container stats命令。你可能还记得这两个命令都只显示实时的信息,没有保留历史数据。

如果你在尝试在其运行时调试一个问题或者是想要快速获取一个容器内部发生了什么的概念时这会很棒,但如果要回顾一个问题它的帮助不大;你可能配置了容器在不响应时进行重启。这对应用的可用性非常有用,而如果你想要了解容器为什么不响应的话就没有什么帮助了。

在GitHub仓库的/Chapter13文件夹中,有一个名为prometheus的文件夹,其中有一个Docker Compose文件会在两个网络上启动三个不同的容器。我们不去看Docker Compose文件本身,而是来看其可视化内容:

可以看到,发生了许多事情。我们运行的三个服务是:

在启动和配置我们的Docker Compose服务之前,我们应讨论下为什么需要每个服务,先从cadvisor开始。

cadvisor是一个由Google发布的项目。从我们所使用镜像的 Docker Hub用户名可以看出来这一点,Docker Compose 文件该服务部分的内容如下:

我们在挂载主机文件系统的不同部分来允许cadvisor以第十一章 Portainer: 一个Docker的GUI相同的方式访问我们的Docker。本例中这么做的原因是我们将使用cadvisor来收集容器的统计信息。虽然它可以作为独立的容器监控服务,我们不希望将cadvisor容器对外暴露。而是仅仅在back网络上Docker Compose栈的容器中可用。

cadvisor是一个独立的Docker容器在stat命令的网页前端,显示图形并允许你以易于使用的界面从Docker主机深挖至容器。但是,它只保留5分钟以内的指标。

因为我们想要记录指标并在几小时甚至是几天后使用,只有5分钟的数据表示我们需要有其它工具来记录它所处理的指标。cadvisor以结构化数据暴露所记录的容器的信息,端点地址为http://cadvisor:8080/metrics/。

一会儿我们会来看为什么这很重要。cadvisor端点由我们下面一个服务prometheus来自动抓取。大部分的重量级工作在这里发生。prometheus是由SoundCloud编写并开源的监控工具:

由以上的服务定义可以看到,我们挂载了一个名为./prometheus/prometheus.yml的配置文件以及一人名为prometheus_data的数据卷。配置文件包含抓取源的信息,如下配置中可以看到:

我们在配置Prometheus每15秒从我们的端点抓取数据。端点在scrape_configs版块中进行定义,可以看到这个版块的定义中有cadvisor和Prometheus自身。我们创建并挂载prometheus_data数据卷的原因是Prometheus将存储所有的指标数据,因此我们需要对其安全保存。

Prometheus的核心是一个时间序列数据库。它获取所抓取的数据,对其处理并查找指标名和值,然后和时间戳一起进行存储。

Prometheus还带有强大的查询引擎和 API,使用成为这类数据的完美数据库。虽然它确实有一些基础绘图功能,推荐你使用Grafana,这也是我们的最后一个且唯一对外暴露的服务。

Grafana是一个用于显示监控图形和指标分析的开源工具,让我们可以使用时间序列数据库(如Graphite, InfluxDB以及Prometheus)创建仪表盘。还有更多的后端数据库选项作为插件提供。

Grafana的 Docker Compose定义与其它服务的模式相近:

我使用grafana_data数据卷来存储Grafana自己的内部配置数据库,没有在Docker Compose文件中以环境变量存储,我们从名为./grafana/grafana.config的外部文件中进行加载。

这些变量如下:

可以看到,我们在这里设置了用户名和密码,将其放到外部文件中表示我们可以在无需编辑核心Docker Compose文件的情况下修改它们的值。

现在我们已经知道了这4个服务分别充当的角色,下面来启动它们吧。只需在prometheus文件夹中运行如下命令来进行启动:

这会完成网络和数据卷的创建,并从Docker Hub拉取镜像。然后会启动这4个服务:

你可能迫不及待地要去看Grafana仪表盘。但会发现没有内容,因为Grafana需要花几分钟来进行初始化。可以通过如下命令来查看日志的进度:

该命令的输出如下:

在看到HTTP Server Listen消息后,Grafana就可用了。Grafana 5(译者注:Alan 测试使用的版本为6)现在可以导入数据源和仪表盘了,这也是将./grafana/provisioning/挂载到/etc/grafana/provisioning/的原因。这个文件夹包含配置,会自动配置Grafana来与Prometheus服务对话,并且也导入仪表盘,显示Prometheus从cadvisor所抓取的数据。

打开浏览器并输入http://localhost:3000/,你会看到如下的登录欢迎页面:

用户名输入admin,密码输入password。登录后如果你配置了数据源,你应该会看到如下页面:

可以看到,初始步骤中的Install Grafana | Create your first data source | Create your first dashboard都已执行,仅剩下两个步骤。现在我们会忽略这些。点击左上角的Home按钮会进入显示可用仪表盘的菜单:

可以看到,有一个名为Docker Monitoring。点击后会进入如下页面:

可以从页面右上角的计时信息,默认显示过去5分钟的数据。点击它后可以修改显示的时间区间。例如,下图显示了过去15分钟的数据,显然要多于cadvisor所记录的5分钟数据:

我说过这是一个复杂的方案,最终Docker会扩展最近发布的内置端点,当前它仅暴露Docker引擎而非容器自身的信息。有关内置端点的更多信息,请查看Docker官方文档:https://docs.docker.com/config/thirdparty/prometheus/。

还有其它的监控方案,大部都以软件即服务(SaaS)的形式存在。在扩展阅读的服务列表中可以看到,列出了一些非常好的监控方案。事实上,你可能已经在使用它们了,所以会在监控你的容器时扩展配置来使用会更为容易。

在完成了Prometheus的探索之后,请记得运行如下命令来进行删除:

这会删除所有的容器、数据卷、镜像和网络。

护展到外部平台

我们学习过如何使用人工具来扩展到外部平台,如Docker Machine、, Docker Swarm, Docker for AWS 和Rancher来启动集群以及公有云的容器服务,如Amazon Web Services, Microsoft Azure和DigitalOcean。

Heroku

Heroku与其它的云服务有些不同,它被认为是平台即服务(PaaS)。我们不是在上面部署容器,而是将容器关联到Heroku平台,通过它运行服务,如PHP, Java, Node.js或Python。因此,你可以在Heroku上运行Rails应用,然后将你的Docker容器关联到这一平台。

ℹ️我们不会讲解Heroku的安装,因为这和本章的主题有些无关。请参见本章扩展阅读版块来了解Heroku的更多详情。

将Docker和Heroku一起使用的方式是在Heroku平台上创建应用,然后在你的代码中加入类似如下的内容:

先回退一步,我们首先需要安装插件来让这一功能可用。只需运行如下命令:

现在,如你在想能或应该使用Docker Hub的哪个镜像的话,Heroku维护大量的可在前面代码中使用的镜像:

生产环境是什么样的?

本章的最后一部分,我们将讨论生产环境应该是什么样的。这一部分并不会如你所想的那样长。这是因为可选的数量级较大,不可能一一讲解到。同时,通过前面部分及章节你应该已了解对你来说什么会是最好。

因此我们将了解一些你在规划环境时可能会向自己提的一些问题。

Docker主机

Docker主机是你环境的重要组成部分。没有它们,你就没有地方运行容器了。我们在前面章节中已经看到,在运行Docker主机这个问题上有一些考量。第一个需考虑的是,如果你的主机运行Docker,就不应该运行其它服务。

进程的混用

你应该抵挡住在已有主机上安装Docker并运行容器的诱惑。这不仅会因在同一主机上混用隔离和非隔离进程带来安全威胁,还会因无法对非容器化应用添加资源限制而导致性能问题,也即它们会潜在地对你运行中的容器有负面影响。

多个独立Docker主机

如果你有多个Docker主机,该如何管理它们呢?Portainer这样的工具会是不错的选择,但如果管理更多的主机时就会有麻烦。此外,如果你运行多台隔离的Docker主机,就无法选择在主机间迁移容器。

当然,你可以使用Weave Net这样的工具来在多个独立Docker主机上分布容器网络。取决于你的托管环境,你可能还可以在外部存储上创建数据卷并在需要时将它们展现给Docker主机,但你是在创建一个手动进程来管理主机间容器的迁移,

路由到你的容器

如果你有多台主机的话,需要考虑如何将请求路由到不同的容器中。

例如,如果你有外部负载均衡器,像AWS中的ELB,或自有集群之上的一个独立设备,你是否可以动态的添加路由将抵达负载均衡器端口 x 的访问路由到Docker主机的 y 端口上,以让访问在容器间路由呢?

如果你有多个需要访问相同外部端口的容器,该如何进行处理?

你是否需要安装一个Traefik, HAProxy或nginx这样的代理来接收请求,然后根据基于域名或子域名的虚拟主机来进行路径由而不使用基于端口的路由?

例如,你会仅使用网站的端口,80和443端口的所有内容交由Docker配置的容器接收这些端口的访问。使用虚拟主机路由表示你可以将domain-a.com路由到容器 a,然后将domain-b.com路由到容器 b。domain-a.com和domain-b.com可以指向相同的 IP 地址和端口。

集群

我们在前一部分中讨论的很多问题可以通过引入集群工具来解决,如Docker Swarm和Kubernetes。

兼容性

虽然应用在开发人员的Docker的本地安装上运行正常,你需要确保在拿到应用并将其它部署到Kubernetes集群等处时,运行效果相同。

十有八九你不会遇到问题,但你需要考虑在同一应用集中应用是如何与内部其它容器间进行通讯的。

参考架构

在选择集群技术时有没有参考架构呢?在部署集群时进行查看总是好的。有最佳实践指南接近或符合你所追求的环境。毕竟,没有人想要创建一个单点出错整体崩溃的集群。

还有推荐的资源是什么?没有必要部署一个有5个管理节点和单台Docker主机的集群,正如部署5台Docker主机和单个管理服务器一样没有什么意义,因为你有一个单点出错整体崩溃集群。

译者注:Single Point of Failure(SPoF)这里统一译为单点出错整体崩溃

集群技术需要什么样的支持技术(例如远程存储、负载均衡器和防火墙)?

集群通讯

集群与管理或Docker机间进行通讯时有哪些要求?你是否需要一个内部或分离的网络来隔离集群流量?

你是否可以轻易将集群成员锁定到仅在你的集群中?集群通讯是否加密了?你的集群可以暴露哪些信息?这是否会让它成为黑客攻击的目标?

集群需要对API怎样的外部访问,比如你的公有云提供商?API和访问认证信息的保存是否安全?

镜像仓库

你的应用是如何打包的?是你否将代码拷至镜像中了?如果是的话,你是否需要托管一个私有本地镜像仓库,或可以使用像Docker Hub, Docker Trusted Registry (DTR)或Quay这样的外部服务?

如果需要托管自己的私有仓库,应该放在什么样的环境中?谁拥有或需要访问权限?是否可以绑入你的目录提供商,比如Active Directory?

总结

本章中,我们学习了几个不同的Docker工作流,以及如何对容器和Docker启动和运行做一些监控。

你能为自己环境做的最好的事是构建一个概念证明(POC)并尽力尝试穷举你所能想到的灾难场景。你可以通过使用云提供商的容器服务开始或寻找一个好的参考架构,它会减少你的试错。

下一章中,我们将来看容器世界中你的下一步是什么。

课后问题

  1. 哪个容器为我们的WordPress网站提供服务?
  2. 为什么wp容器不保持运行?
  3. cadvisor保留多少分钟的指标数据?
  4. 哪个Docker Compose命令可用于删除与应用相关的所有内容?

扩展阅读

你可以在哪下网站上找到本章所使用软件的更多详情:

其它外部托管的Docker监控平台有:

还有一些其它自托管选项,如下:

退出移动版