Docker生产环境实践指南(txt+pdf+epub+mobi电子书下载)


发布时间:2021-08-04 08:02:55

点击下载

作者:[美]乔?约翰斯顿(Joe Johnston) [西]安东尼?巴彻勒(Antoni Batchelli)

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Docker生产环境实践指南

Docker生产环境实践指南试读:

前言

Docker是基础设施的新成员。很少有新兴技术能像它这样,在DevOps和基础设施领域中快速风靡起来。在不到两年的时间内,Google、亚马逊、微软、IBM以及几乎所有云供应商都宣布支持运行Docker容器。大量与Docker相关的创业公司在2014年和2015年年初都获得了风险资本的投资。Docker开源技术背后的同名公司——Docker公司,在2015年第一季度的D轮融资中估值为10亿美元左右。

大大小小的公司都在转换其应用,使之运行于容器内,以此实现面向服务架构(SOA)和微服务。不论是参加从旧金山到柏林的任何DevOps聚会,还是阅读最热门的公司工程博客,都可以看出全世界的运维领导者们如今都在云上运行Docker。

毫无疑问,容器已经成为应用程序打包和基础设施自动化的重要组成部分。但有一个棘手的问题,促使本书作者和同僚们创作了另一本Docker图书。本书面向的读者

具有中高级DevOps和运维背景的读者将从本书获益最多。因而,强烈建议读者应具备在生产环境中运行服务器以及创建和管理容器这两方面的基本经验。

很多图书和博客文章已经涵盖了与Docker安装及运行相关的话题,但能把在生产环境中运行Docker时产生的大量甚至是令人挠头的关注点结合在一起的材料则少之又少。不用担心,如果你很喜欢《盗梦空间》(Inception)这部电影,在云服务器的虚拟机中运行容器会让你感觉很自然。

本书将带读者深入理解生产环境中架构的组成部分、关注点,以及如何运行基于Docker的基础设施。谁真的在生产环境中使用Docker

换个更深刻的说法,对于在真实生产环境中使用Docker遇到的问题,如何找到解决之道?本书综合了访谈、真实公司端到端的生产环境实例,以及来自DevOps杰出专家的参考文献,以此来解答这些问题。虽然本书包含了一些有用的示例,但它并不是一本复制粘贴的“教程式”参考书。相反,本书侧重于生产环境中对前沿技术进行评估、风险抵御及运维所需的实践理论和经验。

作为作者,我们希望这本书所包含的内容能够为那些正在评估如何及何时将Docker相关技术引入其DevOps栈的团队提供一个可靠的决策指南,这远比代码片段要来得长久。

生产环境中运行的Docker为企业提供了多个新的运行和管理服务器端软件的方式。很多现成的用例讲解了如何使用Docker,但很少有公司公开分享过他们的全栈生产环境经验。本书汇集了作者在生产环境中运行Docker的多个实例和一组选定的友好公司分享的使用经验。为什么使用Docker

Docker所使用的底层容器技术已经存在了很多年,甚至早于dotCloud这家平台即服务(PaaS)创业公司,即后来我们所熟知的Docker。在dotCloud之前,许多知名的公司(如Heroku和Iron.io)已经在生产环境中运行大型容器集群,以获取额外的超越虚拟机的性能优势。与虚拟机相比,在容器中运行软件赋予了这些公司秒级而非分钟级的实例启动与停止的能力,同时能使用更少的机器运行更多实例。

既然这项技术并不新鲜,为什么Docker能获得如此巨大的成功呢?主要是因为它的易用性。Docker创造了一种统一的方式,通过简便的命令行及HTTP API工具来打包、运行和维护容器。这种简化降低了将应用程序及其运行时环境打包成一个自包含镜像的入门门槛,使之变得可行且有趣,而不需要类似Chef、Puppet及Capistrano之类的配置管理和发布系统。

Docker提供了一种统一手段,将应用程序及其运行时环境打包到一个简单的Dockerfile里,这从根本上改变了开发人员与DevOps团队之间的交互界面。从而极大简化了开发团队与DevOps之间的沟通需求与责任边界。

在Docker出现之前,各个公司的开发与运维团队之间经常会爆发史诗般的战争。开发团队想要快速前进,整合最新版的软件及依赖,以及持续部署。运维团队则以保证稳定为己任,他们负责把关可以运行于生产环境中的内容。如果运维团队对新的依赖或需求感到不适,他们通常会站在保守的立场上,要求开发人员使用旧版软件以确保糟糕的代码不会搞垮整台服务器。

Docker一下子改变了DevOps的决策思维,从“基本上说不”变成了“好的,只要运行在Docker中就可以”,因为糟糕的代码只会让容器崩溃,而不会影响到同一服务器上的其他服务。在这种泛型中,DevOps有效地负责为开发人员提供PaaS,而开发人员负责保证其代码能正常运行。如今,很多团队将开发人员加入到PagerDuty中,以监控他们在生产环境中的代码,让DevOps和运维人员专注于平台的稳定运行及安全。开发环境与生产环境

对大多数团队而言,采用Docker是受开发人员更快的迭代和发布周期需求推动的。这对于开发环境是非常有益的,但对于生产环境,在单台宿主机上运行多个Docker容器可能会导致安全漏洞,这一点我们将在第6章“安全”中讲述。事实上,几乎所有关于在生产环境中运行Docker的话题都是围绕着将开发环境与生产环境区分开的两个关注点进行的:一是编排,二是安全。

有些团队试图让开发环境和生产环境尽可能保持一致。这种方法看起来很好,但是限于开发环境这样做所需定制工具的数量又或者说模拟云服务(如AWS)的复杂度,这种方法并不实际。

为了简化这本书的范畴,我们将介绍一些部署代码的用例,但判定最佳开发环境设置的实践机会将留给读者。作为基本原则之一,尽量保持生产环境和开发环境的相似性,并使用一个持续集成/持续交付(CI/CD)系统以获取最佳结果。我们所说的“生产环境”

对于不同的团队,生产环境意味着不同的东西。在本书中,我们所说的生产环境是指真实客户用于运行代码的环境。这是相对于开发环境、预演环境及测试环境而言的,后者的停机时间不会被客户感知到。

在生产环境中,Docker有时是用于接收公共网络流量的容器,有时则是用于处理来自队列负荷的异步的后台作业。不管哪种用途,在生产环境中运行Docker与在其他环境中运行相比,最主要的差异都是需要在其安全性与稳定性上投入较多的注意力。

编写本书的动力之一是,与Docker相关的文档和博客文章中缺乏对实际生产环境与其他环境的明确区分。我们认为,80%的Docker博客文章中的建议在尝试在生产环境中运行6个月之后会被放弃(或至少修改)。为什么?因为大多数博客文章中举的都是理想化的例子,使用了最新、最好用的工具,一旦某个极端的情况变成了致命缺陷,这些工具将被遗弃(或延期),被更简单的方法所取代。这是Docker技术生态系统现状的一个反映,而非技术博客的缺陷。

总的来说,生产环境很难管理。Docker简化了从开发到生产的工作流程,但同时增加了安全和编排的复杂度(更多关于编排的内容参见第4章)。

为了节省时间,下面给出本书的重点综述。

所有在生产环境中运行Docker的团队,都会在传统的安全最佳实践上做出一项或多项妥协。如果无法完全信任容器内运行的代码,那么就只得选用容器与虚拟机一对一的拓扑方式。对于很多团队而言,在生产环境中运行Docker的优势远远大于其带来的安全与编排问题。如果遇到工具方面的问题,请等待一到两个月,以便Docker社区对其进行修复,不要浪费时间去修补其他人的工具。保持Docker设置最小化。让一切自动化。最后,对成熟的编排工具(如Mesos、Kubernates等)的需求远比想象的要少得多。功能内置与组合工具

Docker社区一个常见的口头禅是“电池内置但可移除”,指的是将很多功能捆绑在一起的单体二进制文件,这有别于传统Unix哲学下相对较小、功能单一、管道化的二进制文件。

这种单体式的做法是由两个主要因素决定的:(1)使Docker易于开箱即用;(2)Golang缺少动态链接。Docker及多数相关工具都是用Google的Go编程语言编写的,该语言可以简化高并发代码的编写与部署。虽然Go是一门出色的编程语言,但用它来构建的Docker生态系统中也因此迟迟无法实现一个可插拔的架构,在这种架构中可以很容易用替代品对工具进行更换。

如果读者有Unix系统背景,最好是编译自己的精简版Docker守护进程,以符合生产环境的需求。如果读者有开发背景,预计到2015[1]年下半年,Docker插件将成为现实。在此期间,估计Docker生态系统中的工具将会出现明显的重叠现象,某些情况下甚至是相互排斥的。

换句话说,要让Docker运行于生产环境中,用户的一半工作将是决定哪些工具对自己的技术栈最有意义。与DevOps所有事情一样,先从最简单的解决方案入手,然后在必要时增加其复杂性。

2015年5月,Docker公司发布了Compose、Machine及Swarm,与Docker生态系统内的同类工具进行竞争。所有这些工具都是可选的,请根据实际情况对其进行评估,而不要认为Docker公司提供的工具就一定是最佳解决方案。

探索Docker生态系统时的另一项关键建议是:评估每个开源工具的资金来源及其商业目标。目前,Docker公司和CoreOS经常发布工具,以争夺关注度和市场份额。一个新工具发布后,最好等上几个月,看看社区的反应,不要因为它看起来很酷就切换到最新、最好用的工具上。哪些东西不要Docker化

最后一个关键点是,不要期望能在Docker容器中运行所有东西。Heroku风格的“十二要素”(12 factor)应用是最容易Docker化的,因为它们不维护状态。在理想的微服务环境中,容器能在几毫秒内启动、停止而不影响集群的健康或应用程序的状态。

类似ClusterHQ这样的创业公司正着手实现Docker化数据库和有状态的应用程序,但眼下,由于编排和性能方面的原因,可能需要继续直接在虚拟机或裸机上运行数据库。[2]

Docker还不适用于任何需要动态调整CPU和内存要求的应用。允许动态调整的代码已经完成,但尚不清楚何时才能在一般的生产环境中投入使用。目前,若对容器的CPU和内存的限制进行调整,需要停止并重新启动容器。

另外,对网络吞吐量有高要求的应用进行最佳优化时不要使用Docker,因为Docker使用iptables来完成宿主机IP到容器IP的NAT转换。通过禁用Docker的NAT来提升网络性能是可行的,但这是一个高级的使用场景,很少有团队会在生产环境中这么做。技术审稿人

衷心感谢以下技术审稿人提供的早期反馈及细致的评论:Mika Turunen、Xavier Bruhiere和Felix Rabe。

[1] Docker 1.7版中正式引入了插件系统。——译者注

[2] Docker 1.10版中新增的docker update命令可实现CPU和内存的动态调整。——译者注第1章入门

建立Docker生产环境系统的首要任务,是以一个有助于想象各组件如何相互配合的方式来理解其术语。与其他快速发展的技术生态系统一样,我们可以预见,Docker野心勃勃的市场营销、不完善的文档以及过时的博客文章将造成使用者对各个工具职责理解上的混乱。

我们将在本章中定义贯穿全书的术语和概念,而非提供一份统一的Docker百科全书。通常情况下,我们的定义与生态系统中的大体一致,但如果你所阅读的博客文章中使用了不同的术语也不用太过惊讶。

在本章中,我们将介绍在生产环境中运行Docker的核心概念以及不涉及具体技术的容器常识。在随后的章节中,我们将讨论真实世界的生产环境用例,并详细说明其组件和供应商信息。1.1 术语

下面让我们来看一下本书所采用的Docker术语。1.1.1 镜像与容器● 镜像是指文件系统快照或tar包。● 容器是指镜像的运行态。1.1.2 容器与虚拟机● 虚拟机持有整个操作系统和应用程序的快照。● 虚拟机运行着自己的内核。● 虚拟机可以运行Linux之外的其他操作系统。● 容器只持有应用程序,不过应用程序的概念可以延伸到整个

Linux发行版。● 容器共享宿主机的内核。● 容器只能运行Linux,不过在同一宿主机上运行的每个容器都可

包含不同的发行版。1.1.3 持续集成/持续交付

在应用程序新代码提交或触发其他条件时,系统自动构建新镜像并进行部署。1.1.4 宿主机管理

设置/配备一台物理服务器或虚拟机以便用于运行Docker容器的过程。1.1.5 编排

编排(orchestration,也称编配)这个术语在Docker生态系统中有多种含义。通常情况下,它包括调度和集群管理,不过有时也包括了宿主机管理。

在本书中,我们将编排作为一个松散的总称,包括容器调度的过程、集群的管理、容器的链接(发现),以及网络流量路由。或者换句话说,编排是个控制器进程,用于决定在哪里运行容器,以及如何让集群知道可用的服务。1.1.6 调度

用于决定哪些容器可以以给定的资源约束(如CPU、内存和IO)运行在哪些宿主机上。1.1.7 发现

容器如何公开服务给集群,以及发现如何查找其他服务并与之通信的过程。举个简单的用例:一个网站应用容器发现如何连接到数据库服务。

Docker文档中的发现是指将容器链接在一起,不过在生产级系统中,通常使用的是更复杂的发现机制。1.1.8 配置管理

配置管理过去常常指的是Docker出现之前的自动化工具,如Chef和Puppet。大多数的DevOps团队正在转移到Docker上,以消除这类配置管理系统的复杂度。

在本书的示例中,配置管理工具只用于配备具有Docker和少量其他东西的宿主机。1.2 从开发环境到生产环境

本书着重于生产环境或非开发环境中的Docker,这意味着我们不会花太多的篇幅在开发环境中Docker的配置和运行上。但由于所有服务器都在运行代码,如何看待在Docker和非Docker系统中的应用程序代码还是值得简单讨论一下的。

与Chef、Puppet和Ansible这类传统配置系统不同,Docker最好的使用方式是将应用程序代码预先打包成一个Docker镜像。镜像通常包含所有的应用程序代码、运行时的依赖以及系统的需求。而包含数据库凭证和其他敏感信息的配置文件通常在运行时添加,而非内建到镜像中。

有些团队会在开发机上手工构建Docker镜像,然后推送到镜像仓库,之后再从仓库中拉取镜像到生产环境宿主机中。这是个很简单的用例。虽然行得通,但从工作流和安全角度考虑并不理想。

一个更常见的生产环境示例是,使用持续集成/持续交付系统在应用程序代码或Dockerfile文件发生变更时自动构建新镜像。1.3 使用Docker的多种方式

过去的几年时间,科技发生了巨大变化,从物理服务器到虚拟服务器,再到拥有PaaS环境的云计算。不论是否采用了全新架构,Docker镜像都可以在当前环境中很容易地被使用。要使用Docker,并不需要立即从单体应用程序迁移到面向服务架构。有很多用例允许在不同层次上集成Docker。

Docker常用于以下场景。● 使用以镜像为基础的部署方式取代类似Capistrano的代码部署系

统。● 安全地在同一台服务器中运行遗留应用和新应用。● 使用一个工具链循序渐进地迁移到面向服务架构。● 管理云端或裸机上的水平扩展性和弹性。● 确保从开发环境到预演环境到生产环境跨环境的一致性。● 简化开发人员的机器设置和一致性。

将应用的后台程序迁移到Docker集群中,同时保持网页服务器和数据库服务器不变是开始使用Docker的常见示例。另一示例是将应用的部分REST API迁移到Docker中运行,前端使用Nginx代理在遗留服务和Docker集群之间路由通信。通过使用此类技术,团队可以渐进式地从单体应用无缝地迁移到面向服务架构。

如今的应用程序往往需要几十个第三方库,用于加速功能开发或连接第三方SaaS和数据库服务。每个库都可能产生bug,或是让用户陷入版本依赖的泥沼。再加上库的频繁更改,要在基础设施上完成工作代码的持续部署而不引起失败,压力巨大。

Docker可贵的镜像思想使得技术团队在部署工作代码时,不论是单体架构、面向服务或是二者的混合,由于代码及其依赖项捆绑在同一个镜像中,所使用的方式对每次部署都是可测试、可重复、文档化且一致的。一旦一个镜像构建完毕,就可以部署到任意多个运行着Docker守护进程的服务器上。

另外一个常见的Docker用例是跨环境部署一个单一容器,其典型的代码路径是从开发环境到预演环境再到生产环境。容器为整个代码路径提供了一个一致的、可测试的环境。

作为一个开发人员,Docker模型允许在其个人电脑上调试与生产环境完全一致的代码。开发人员可以很容易地下载、运行和调试有问题的生产环境镜像,且无需事先对本地开发环境进行修改。1.4 可预期的情况

在生产环境中运行Docker容器困难不小,但还是能实现的。每天都有越来越多公司开始在生产环境中运行Docker。如同所有的基础设施一样,我们建议以小规模入手,然后渐进式地完成迁移。为什么Docker在生产环境如此困难

对生产环境有很多要求:安全可靠的部署、健康检查、最小或零停机时间、从失败中恢复的能力(回滚)、一个集中存储日志的方式、一种分析或调试应用的方式,以及一种聚合监控参数的方式。类似Docker这样的新技术虽然使用起来非常有趣,但还需要时间来完善。

Docker在可移植性、一致性以及打包具有众多依赖的服务这些方面非常有优势。多数团队会因为以下一个或多个痛点而坚持使用Docker。● 一个应用的不同部分使用大量不同的依赖。● 支持使用旧依赖的遗留应用程序。● 开发团队与DevOps之间的工作流问题。

本书中我们所采访的团队,有一个共同的警示:切勿尝试在一个组织内让采用Docker这事一蹴而就。即便运维团队已经为采用Docker做好了充分的准备,也请记住,过渡到Docker通常意味着将管理依赖的重任推给了开发人员。虽然很多开发人员都渴求这种自主权,以便加快迭代,但并非每位开发人员都有能力或兴趣将其列入自己的责任范围。为了能有一个良好的Docker工作流,还是需要花些时间来转变企业文化。

在第2章中,我们将阐述Docker的技术栈。第2章技术栈

生产环境的Docker设置包括了一些基本的架构组件,这些组件对运行容器化的及传统的服务器集群来说是通用的。在很多方面,可以简单地认为构建和运行容器的方式与当前构建和运行虚拟机的方式是一样的,只是使用了一套新的工具和技术。(1)构建并保存镜像快照。(2)将镜像上传到仓库中。(3)下载镜像到某台宿主机中。(4)以容器方式运行镜像。(5)将容器连接到其他服务上。(6)路由流量到容器中。(7)将容器日志发送到指定位置。(8)监控容器。

与虚拟机不同的是,容器通过将宿主机(裸机或虚拟机)与应用程序服务隔离,从而提供了更高的灵活性。这为构建和配备流程带来了直接的改善,但由于额外的容器嵌入层,会增加一些开销。

典型的Docker技术栈将包括用于解决以下关注点的组件:● 构建系统;● 镜像仓库;● 宿主机管理;● 配置管理;● 部署;● 编排;● 日志;● 监控。2.1 构建系统● 如何构建镜像,并将其推送到镜像仓库中?● Dockerfile位于何处?

构建Docker镜像通常有以下两种方式。(1)在开发人员电脑上手工构建,然后推送到到仓库中。(2)使用CI/CD系统在代码提交时自动构建。

理想的Docker生产环境将使用类似Jenkins或Codeship这样的CI/CD(配置集成/持续部署)系统,在代码提交时自动构建镜像。一旦容器构建完毕,它将被发送到镜像仓库中,自动化测试系统就可以从中下载并运行该镜像。2.2 镜像仓库● Docker镜像保存在哪里?

当前的Docker镜像仓库可靠性比较差,但是每个月都在改善。Docker官方的镜像仓库中心是众所周知的不可靠,需要额外的重试和故障保护措施。多数团队一般会在自己的基础设施上运行私有的镜像仓库,以减少网络传输成本和延迟。2.3 宿主机管理● 如何配备宿主机?● 如何升级宿主机?

由于Docker镜像包含了应用及其依赖,宿主机管理系统通常只需要添加新服务器,配置访问权限和防火墙,并安装Docker守护进程即可。

类似亚马逊的EC2 Container Service这类服务将消除对传统宿主机管理的依赖。2.4 配置管理● 如何定义容器的集群?● 如何处理宿主机和容器运行时的配置?● 如何管理密钥和机密信息?

一个基本规则是:尽量避免使用传统的配置管理系统。其增加的复杂性往往会造成故障。Ansible、SaltStack、Chef或Puppet这类工具仅用于配备带有Docker守护进程的宿主机。尽可能试着摆脱对旧的配置管理系统的依赖,并使用本书所述的发现和集群技术转移到自我配置的容器上。2.5 部署● 如何将容器放置在宿主机上?

镜像部署有以下两种基本方法。(1)推送 ——部署或编排系统将镜像推送给相关宿主机。(2)拉取 ——事先或按需从镜像仓库拉取镜像。2.6 编排● 如何将容器组织成集群?● 在哪些服务器上运行容器?● 如何调度服务器资源?● 如何运行容器?● 如何将流量路由给容器?● 如何让容器公开和发现服务?“编排 = 强力胶带”。至少多数情况下可以这么认为。

市面上有很多处于早期阶段的全功能容器编排系统,如Docker Swarm、Kubernetes、Mesos和Flynn。但对大多数团队而言,这些系统通常过于强大,增加了在生产环境中出现问题时调试的复杂度。决定使用哪个工具来完成编排常常是设置和运行Docker中最艰难的部分。

在第3章中,我们将讲述Peerspace所采取的一种构建Docker系统的简约方法。第3章示例:极简环境

一说起生产环境中容器的使用,大家的第一反应是那些在同样量级的宿主机上部署成千上万容器的大型公司。但实际上恰恰相反,要发挥容器的作用,并不需要构建如此庞大的系统。小规模的团队反而能从容器中获得最大收益,因为容器使构建和部署服务不仅变得简单,而且可重复、可扩展。

本章描述的就是一家名为PeerSpace的小规模公司构建系统时采取的一种极简方式。这种极简方式使他们能在短时间内使用有限的资源开辟一个新市场,并自始至终保持着极高的开发速度。

PeerSpace构建系统时的目标是既要易于开发,又要在生产环境中足够稳定。这两个目标通常是相互矛盾的,因为高速开发引起的大量变化反过来会对系统的构建和配置产生很大影响。任何一个有经验的系统管理员都知道,这样的变化率必然导致不稳定性。

Docker看起来非常适合用在刚起步的时候,因为它既对开发人员友好,又支持以敏捷的方式构建和运维系统。Docker简化了开发和系统配置的某些方面,但有时却过于简单化了。在易于开发和稳健运维之间取得平衡不是件容易的事。3.1 保持各部分的简单

PeerSpace实现开发速度和稳定的生产环境这两个目标的方法之一是拥抱简单。这里所说的简单是指系统的每个部分——容器——有且只有一个目标。这个目标就是:相同的过程,如日志收集,在任何地方都以相同的方式完成,而各部分连接的方法也是明确、静态地定义在配置文件中的。

在这种简单的系统中,开发人员可以同步地、独立地构建系统的不同部分,并确信构建的容器可组装在一起。另外,在生产环境出现问题时,简单性也让问题的排查与解决变得非常简单。

要长期保持系统的简单,需要大量的思考、折中和坚持,但最终这种简单将物有所值。

PeerSpace的系统由20个零散的微服务组成,其中有部分使用了MongoDB数据库和/或ElasticSearch搜索引擎。该系统设计遵循下列指导原则。(1)倾向无状态服务。这可能是简化PeerSpace生产环境时最大

的决策:大部分服务都是无状态的。除了用于处理当前进行中的

请求的临时信息,无状态服务不需要保持任何需要持久化的数

据。无状态服务的优势在于可以非常容易地对他们进行销毁、重

启、复制及伸缩,所有这一切都无需考虑任何数据处理方面的逻

辑。并且,无状态服务更易于编写。(2)倾向静态配置。所有宿主机和服务的配置都是静态的:一

旦给服务器推送一项配置,该配置就会一直生效,直至显式地推

送来新配置。与之相对的是那些动态配置的系统,其系统的实际

配置是实时生成的,并会根据不同因素(如可用宿主机和即将到

达的负载)进行自主修改。尽管动态系统的伸缩性更好,并且具

有一些有趣的属性,如在出现某些故障时自动恢复等,但静态配

置更易于理解和排错。(3)倾向静态的网络布局。如果在一台宿主机中找到一项服

务,除非新配置被确定并提交,否则总能在那台宿主机中找到该

服务。(4)区别对待无状态和有状态服务。尽管PeerSpace的多数服务

是无状态的,他们还是使用MongoDB和ElasticSearch来持久化数

据。这两种类型的服务在本质上是非常不同的,应该区别处理。

例如,将一个无状态服务从一台宿主机移动到另一台上非常简

单,只需要启动新服务,然后停止旧服务即可。但要对一个数据

库进行移动,数据也要跟着移动。移动数据可能会花费很长时间,

要求在迁移过程中停止服务,或通过设备方法进行在线迁移。在

开发领域,通常将无状态服务比做“牲口”,它们没有名字,很

容易被代替和伸缩,而将有状态服务比做“宠物”,它们是唯一

的、具名的,需要维护,并且难以伸缩。幸运的是,PeerSpace

正如一个农场一样,其“牲口”数量要远远多于“宠物”。

以上这些设计原则是简化PeerSpace系统的基础。将有状态服务与无状态服务分离,可以对本质上完全不同的服务进行区别处理(如图3-1所示),因此可以对每一种情况的处理方式进行优化和尽可能地简化。使用静态配置运行无状态服务使得操作系统的流程变得非常简单:多数情况下流程被简化成文件复制和容器重启,完全不需要考虑其他因素,如对第三方系统的依赖。图3-1

上述设计准则能否产生一个简单的系统,完全取决于系统操作是否同样简单。3.2 保持流程的简单

在设计业务流程时,PeerSpace基于观察做出了如下假定:在他们的基础设施中离硬件越近的层变更越少,而越接近终端用户的层变更越频繁(如图3-2所示)。图3-2

根据这一观察,生产环境中的服务器数量很少变更,通常是由于缩放问题或硬件故障。而这些服务器的配置变更频次可能更高一些,通常是由于性能补丁、系统错误修复或安全问题等原因。

在这些服务器上运行的服务数量和类别变更更为频繁。通常是指移动服务、添加新类型服务或对数据进行操作。这个层级上的其他修改可能与要求重新配置或变更第三方服务的新版本部署有关。不过,这类变更仍然不是很常见。

在这样的基础设施中,多数的变更与多个服务的新版本推送有关。每天,PeerSpace都会执行很多次新版服务的部署。多数情况下,新版本的推送只是简单地将现有版本替换成运行新镜像的新版本。有时也会使用相同镜像,但对配置参数进行变更。

PeerSpace的流程建立是为了让最频繁的变更最容易也最简单进行,即便这样会造成基础设施更难以变更(实际上并未发生)。3.3 系统细节

PeerSpace运行着3个类生产环境集群:集成环境、预演环境与生产环境。每个集群包含了相同数量的服务,并使用相同的方式进行配置,唯一不同的是它们的原始性能(CPU、内存等)。开发人员同样会在自己的电脑上运行全部或部分集群。

每个集群由以下几个部分组成:● 几台运行着CentOS 7的Docker宿主机,使用systemd作为系统管

理程序;● 一台MongoDB服务器或一个复制集合;● 一台ElasticSearch服务器或一个集群。

MongoDB和/或ElasticSearch服务器可能在某些环境中是Docker化的,而在其他环境中不是Docker化的(如图3-3所示)。它们也会在多个环境中共享。在生产环境中,出于运维和性能的原因,这些数据服务是不做Docker化的。图3-3

每个Docker宿主机运行着一个服务的静态集合,所有这些服务都会遵循如下模式进行构建:● 所有配置都通过环境变量进行设置,包括其他服务的地址(和端

口);● 不将数据写入磁盘;● 将日志发送到标准输出(stdout)中;● 生命周期由systemd管理,并定义在一个systemd单元文件中。利用systemd

所有服务都由systemd管理。systemd是一个借鉴了OSX launchd的服务管理程序,此外,systemd使用普通数据文件命名单元来定义每个服务的生命周期(如图3-4所示),这与其他使用shell脚本完成这类事务的传统管理程序完全不同。图3-4

PeerSpace的服务只将Docker进程当作唯一的运行时的依赖。systemd的依赖管理只用来确保Docker处于运行状态,但不确保其拥有的服务以正确顺序启动。服务构建时要求它们可以以任何顺序启动。

所有服务都由以下部分组成(如图3-5所示):● 一个容器镜像;● 一个systemd单元文件;● 一个该容器专用的环境变量文件;● 一组用于全局配置参数的共享环境变量文件。图3-5

所有单元都遵循相同的结构。在服务启动之前,一系列包含环境变量的文件将被加载:EnvironmentFile=/usr/etc/service-locations.env EnvironmentFile=/usr/etc/service-config.env EnvironmentFile=/usr/etc/cluster.env EnvironmentFile=/usr/etc/secrets.env EnvironmentFile=/usr/etc/%n.env

这确保了每个服务会加载一系列通用环境文件(service-locations.env、service-config.env、cluster.env及secrets.env),外加一个专用于该服务的文件:%n.env,此处的%n在运行时将被替换成该单元的全称。例如,一个名为docker-search的服务单元将被替换成docker-search.service。

接下来的条目是确保在启动新容器前旧容器被正确删除的:ExecStartPre=-/bin/docker kill %n ExecStartPre=-/bin/docker rm -f %n

通过使用%n,将容器命名为单元的全称。使用变量进行容器命名能让单元文件更通用并且可移植。在docker程序路径之前使用“-”可防止单元在命令失败时中止启动。这里需要忽略潜在的错误,因为如果此前不存在该容器,这些命令将执行失败,而这种情况又是合法的。

单元中主要的条目是ExecStart,它将告之systemd如何启动该容器。这里内容较多,但我们只关注一下其最重要的部分:ExecStart=/bin/docker \ run \ -p "${APP_PORT}:${APP_PORT}" \ -e "APP_PORT=${APP_PORT}" \ -e "SERVICE_C_HOST=${SERVICE_C_HOST}" \ -e "SERVICE_D_HOST=${SERIVCE_D_HOST}" \ -e "SERVICE_M_HOST=${SERVICE_M_HOST}" \ --add-host docker01:${DOCKER01_IP} \ --add-host docker02:${DOCKER02_IP} \ --volume /usr/local/docker-data/%n/db:/data/data \ --volume /usr/local/docker-data/%n/logs:/data/logs \ --name %n \ ${IMAGE_NAME}:${IMAGE_TAG}(1)使用EnvironmentFile加载的环境变量来配置容器(如通过-p

公开的端口)。(2)将集群中的其他宿主机地址添加到容器的/etc/hosts文件中(--add-host)。(3)映射用于日志和数据的数据卷。这主要是作为一个“蜜罐”[1](honey pot),以便检查这些目录并确保无人对其进行写入。(4)镜像自身(名称和版本)来自于从/usr/etc/%n.evn中加载的

环境变量,在本示例中它将映射到/usr/etc/docker-

search.service.env中。

最后,是一些定义如何停止容器及其他生命周期要素的条目:ExecStop=-/bin/docker stop %n Restart=on-failure RestartSec=1s TimeoutStartSec=120 TimeoutStopSec=303.4 集群范围的配置、通用配置及本地配置

PeerSpace将集群配置分成两种类型文件:环境变量文件和systemd单元文件。上面已经讲述了单元文件及其加载环境变量文件的方式,接下来看一下环境文件。

将环境变量分解到不同文件中的主要原因在于,这些文件在跨集群时是否需要修改以及如何修改,不过也有其他操作层面的原因。● service-locations.env:集群中所有服务的宿主机名。这个文件在

不同集群里通常是一样,不过也有例外。● service-config.env:与服务自身相关的配置。如果不同集群运行

的是服务的兼容性版本,这个文件应该是一样的。● secrets.env:密钥信息。因其内容关系,这个文件被处理的方法

与其他文件不同,而且在不同集群上也有差异。● cluster.env:包括了集群间的所有不同之处,如所使用的数据库

前缀、是测试还是生产环境、外部地址等。这个文件中最重要的

信息是属于该集群的所有宿主机的IP地址。

下面是某些示例集群中的文件。这是cluster.env文件:CLUSTER_ID=alpha CLUSTER_TYPE="test" DOCKER01_IP=x.x.x.226 DOCKER02_IP=x.x.x.144 EXTERNAL_ADDRESS=https://somethingorother.com LOG_STORE_HOST=x.x.x.201 LOG_STORE_PORT=9200 MONGODB_PREFIX=alpha MONGODB_HOST_01=x.x.x.177 MONGODB_HOST_02=x.x.x.299 MONGODB_REPLICA_SET_ID=rs001

这是service-locations.env文件:SERVICE_A_HOST=docker01 SERVICE_B_HOST=docker03 CLIENTLOG_HOST=docker02 SERIVCE_D_HOST=docker01 ... SERVICE_Y_HOST=docker03 SERVICE_Z_HOST=docker01

每个systemd单元都包含集群中其他宿主机的引用,而这些引用来自于环境变量。包含服务宿主机名的变量会被装配到Docker命令中,以便容器进程使用。这是通过-e参数实现的,如-e "SERVICE_D_HOST=${SERIVCE_D_HOST}"。

Docker宿主机的IP地址也同样通过--add-host docker01:${DOCKER01_IP}注入到容器中。这样,只需要修改这两个文件并且保持单元文件的完好无损,就可以将容器扩散到不同数量的宿主机中。3.5 部署服务

容器级别或配置级别的修改通过3个步骤完成:第1步,在配置仓库(Git)上做修改;第2步,将配置文件复制到宿主机的预演区域(ssh);第3步,运行宿主机上的一个脚本来逐一部署每个服务,使得配置修改生效。这种方法提供了版本化配置,一次只推送一项相关配置,以及让推送配置生效的一种灵活方式。

如果需要针对一组服务进行修改,首先在Git上做修改并提交。然后运行脚本,将这个配置推送到所有宿主机的预演区域。一旦配置被推送过去,在每台宿主机上运行一个脚本来部署或重部署该宿主机上的所有容器集合。这个脚本会对在列的所有服务执行如下命令。(1)将配置文件从预演区域复制到其最终位置:● systemd单元文件;● 共享的配置文件;● 当前服务的配置文件;● 密钥文件(解密后的)。(2)需要的话下载镜像文件(镜像定义在服务自身的配置文件

中)。(3)重载systemd的配置,以便读取新的单元文件。(4)重启容器对应的systemd单元。

PeerSpace具有两个部署工作流,理解这一点有助于阐述其部署流程:一个用于开发环境,另一个用于生产环境,而后者是前者的一个超集。

在开发过程中,他们会通过以下步骤将临时构建联署到集成服务器中。(1)使用最新代码库创建一个新的容器镜像。(2)将镜像推送到镜像仓库中。(3)在运行该镜像的容器宿主机上运行部署脚本。

开发环境的systemd单元会追踪镜像的最新版本,所以只要配置不做修改,那我们只需推送镜像并重新部署即可。

类生产环境的服务器(生产环境和预演环境)与开发环境配置方式大体相同,主要区别在于生产环境中的容器镜像都打上了版本标签,而非latest。部署发布镜像到类生产环境容器的流程如下。(1)在仓库中为容器镜像运行发布脚本。该脚本将为Git仓库打

上新版本标签,然后使用这个版本号构建并推送镜像。(2)更新每个服务环境变量文件以引用新镜像标签。(3)将新的配置推送到各宿主机中。(4)在运行该镜像的容器宿主机上运行部署脚本。

他们通常会批次地将服务从开发环境转移到生产环境(一般是两周一次)。在推送发行版到生产环境时,开发环境中用于该发行版的配置文件会被复制到生产目录中。多数文件可以完全照搬,因为它们是从集群的具体细节(IP地址、宿主机数量等)抽象出来的,不过cluster.env和secrets.env文件在各个集群中是不一样的,在发行时也对其进行更新。一般情况下,会一次性推送所有新版本服务。3.6 支撑服务

PeerSpace使用了一组服务来支撑自己的服务。这些服务包括以下两个。● 日志聚合:fluentd+kibana以及docker-gen的组合。docker-gen可

根据宿主机中运行的容器创建和重创建一个配置文件。docker-

gen为每个运行中的容器生成一个fluentd条目,用于发送日志给

kibana。这个服务运行良好,且易于调试。● 监控:Datadog——一个SaaS监控服务。Datadog代理在容器中

运行,用于监控各项性能指标、API使用情况和业务事件。

Datadog为标签提供了丰富的支持,通过fluentd可以使用多种方

式对单一事件进行标记。数据收集起来后(如跨集群的相同服务、

所有Docker服务、使用某个发行版的所有API端点等),可以利

用丰富的标签对数据进行多种方式的切割。3.7 讨论

在系统中,所有宿主机和服务的配置都非常明确,开发人员很容易理解系统的配置,并能不受干扰地工作于系统的不同部分上。每位开发人员都可以在任何时候对集成集群进行推送,并且推送到生产环境所需的协调也很少。

由于每个集群的配置都保存在Git上,很容易追踪配置的变化,并在出现配置问题时对集群进行排错。

因为配置推送的方式,一旦新配置设置妥当,该配置将保持不变。静态配置带来的是极大的稳定性。

另外,服务编写的方式,如通过环境变量进行配置、日志写入控制台、无状态等,使得它们之后可原封不动地被Mesos或Kubernetes这类集群管理工具使用。

当然,要得到这些好处是有代价的。一个最明显的缺点是配置有些繁琐、重复并且易出错。我们可以通过大量的自动化的工具来生成这些配置文件。

修改全局配置要求重启多个容器。目前是由开发人员来重启正确的容器。在生产环境中,如果推送的修改很多,通常会执行滚动重启,但这并不是一个很好的解决方法。这绝对是一个薄弱环节,但到目前为止,还是可控的。3.8 未来

PeerSpace正在考虑几个系统扩展的方式。其中之一是通过反向代理实现零停机时间部署。这将使得PeerSpace有能力对每个服务进行水平扩展。

另外一个方向是从集群的更高层级描述中生成所有的配置文件。这种方法能在配置发生改变后计算哪些容器需要重启。

在考虑这些未来的方向时,PeerSpace也在权衡使用Mesos或Kubernetes的可能性,因为他们认为,增加部署脚本的任何复杂度势必造成对简单模式的过度拉伸。3.9 小结

尽管本章讲解了一个极其简单的Docker使用方式,但我们仍希望它能成为“Docker思想”的基石。不论是使用极简方式还是集群管理系统,读者都能利用这种方式在阅读本书其他部分时获益。

当然,使用Docker还有很多其他方式,第4章将讲述RelateIQ使用Docker运行了一年多的一个真实的Web服务器生产环境。

[1] 用于隐藏宿主机的真实路径。——译者注第4章示例:Web环境

我们所知的大多数公司都曾以一个很低的容器和宿主机比例(1~2个容器对应1台宿主机)成功地使用过Docker。也就是说,要在生产环境中成功运行Docker,并不是必须要运行Apache Mesos或[1]Kubernates。在本示例中,将对RelateIQ公司使用Docker运行了一年多的一个真实Web服务器生产环境做详细的说明。这个环境在运行Ubuntu的标准亚马逊云服务(AWS)实例上,使用Docker支撑其CRM Web应用。当初使用Docker的原因有三:一是Docker能快速生成和销毁容器,从而为客户提供零停机时间部署;二是因为Docker为不同Web版本提供依赖隔离;三是Docker支持即时回滚。图4-1所示为该环境的高层次示图。图4-1

相信吗?这个Web环境提供了如下功能:稳定的零停机时间部署、回滚、集中式日志、监控及分析JVM的一种方式。所有这些都是通过bash脚本编排Docker镜像获得的。图4-2所示为主机的详细情况。图4-2

这台Web服务器运行于单台AWS服务器上,并通过Docker运行着4个容器。部分容器被链接在一起,以便与Docker网桥上的其他容器进行通信。它给宿主机公开了多个端口,用于为性能分析提供HTTP服务和JVM监控。它使用了亚马逊ELB负载均衡器(健康检查在其上进行)。所有容器都将它们的日志保存在宿主机上,这样现有的日志方案(SumoLogic)依旧适用,同时有一个简单的bash编排脚本用于部署和设置新版本Web服务。

为了便于理解很多公司在生产环境中运行Docker时会遇到的问题,我们来看一些具体细节。4.1 编排

编排归根到底就是做两件事:一是获取已安装Docker的服务器,并且使之准备好运行容器的服务器;二是在服务器上启动并运行容器。4.1.1 让服务器上的Docker进入准备运行容器的状态

该服务器使用标准的基本Ubuntu AMI(亚马逊机器镜像)在AWS上部署,并通过Chef的标准配置管理系统对宿主机进行设置。其设置过程与当下的多数环境完全相同。服务器启动之后,Chef就会运行并设置ssh用户、ssh密钥,然后通过其包安装器安装基础包(如iostat),安装并配置监控代理(本例中是Datadog),集合一些临时磁盘空间用于数据或日志存储,安装并配置日志代理(SumoLogic),安装最新版Docker,最后创建bash设置脚本,并配置一个cron任务来运行它。

Chef在服务器上运行之后,宿主机就准备好在其上运行机器所需的任何容器了。Chef还配置了监控和日志软件,用于未来的调试。这个环境可以运行任何类型的容器服务,与当下运行的大多数服务器环境,甚至是物理环境也一般无二。现在,Docker已经安装完毕,宿主机也准备好核心操作工具,下面就可以让宿主机上的容器开始运行

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载