Docker实践(第2版)

978-7-115-54110-9
作者: [英]伊恩·米尔(Ian Miell)
译者: 杨锐吴佳兴梁晓勇黄博文
编辑: 杨海玲

图书目录:

详情

本书由浅入深地讲解了Docker的相关内容,涵盖从开发环境到DevOps流水线,再一路到生产环境的整个落地过程以及相关的实用技巧。书中介绍Docker的核心概念和架构,以及将Docker和开发环境有机、高效地结合起来的方法,包括背Docker用作轻量级虚拟机、构建容器、宿主机编排、配置管理、精简镜像等。不仅如此,本书还通过“问题-解决方案-讨论”的形式,将Docker如何融入DevOps流水线、如何在生产环境落地等一系列难题拆解成114个相关的实用技巧,为读者提供解决方案以及一些细节和技巧方面的实践经验。阅读本书,读者学到的不只是Docker,还包括持续集成、持续交付、构建和镜像管理、容器编排等相关领域的一线生产经验。本书编写时一些案例参考的Docker版本是Docker 1.13。 本书要求读者具备一定的容器管理和运维的基础知识,适合想要将Docker投入实践的相关技术人员阅读,尤其适合具有中高级DevOps和运维背景的读者阅读。

图书摘要

版权信息

书名:Docker实践(第2版)

ISBN:978-7-115-54110-9

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

著    [英]伊恩·米尔(Ian Miell)

     [英]艾丹·霍布森·塞耶斯(Aidan Hobson Sayers)

译     杨 锐 吴佳兴 梁晓勇 黄博文

责任编辑 杨海玲

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315


Original English language edition, entitled Docker in Practice, Second Edition by Ian Miell and Aidan Hobson Sayers published by Manning Publications Co., 209 Bruce Park Avenue, Greenwich, CT 06830. Copyright © 2019 by Manning Publications Co.

Simplified Chinese-language edition copyright © 2020 by Posts & Telecom Press. All rights reserved.

本书中文简体字版由Manning Publications Co.授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

版权所有,侵权必究。


本书由浅入深地讲解了Docker的相关内容,涵盖从开发环境到DevOps流水线,再一路到生产环境的整个落地过程以及相关的实用技巧。书中介绍Docker的核心概念和架构,以及将Docker和开发环境有机、高效地结合起来的方法,包括将Docker用作轻量级虚拟机、构建容器、宿主机编排、配置管理、精简镜像等。不仅如此,本书还通过“问题-解决方案-讨论”的模式,将Docker如何融入DevOps流水线、如何在生产环境落地等一系列难题拆解成114个相关的实用技巧,为读者提供解决方案以及一些细节和技巧方面的实践经验。阅读本书,读者学到的不只是Docker,还包括持续集成、持续交付、构建和镜像管理、容器编排等相关领域的一线生产经验。本书编写时一些案例参考的Docker版本是Docker 1.13。

本书要求读者具备一定的容器管理和运维的基础知识,适合想要将Docker投入实践的相关技术人员阅读,尤其适合具有中高级DevOps和运维背景的读者阅读。


杨锐,前ThoughtWorks咨询师,DevOps领域持续关注者,曾任某海外大型项目DevOps工程师,对其持续交付、基础设施即代码、流水线即代码等方面进行了持续推动,对云计算、容器化和持续交付等有一定经验。现供职美团点评。

吴佳兴,毕业于华东理工大学计算机系,主要研究方向有运维自动化、云原生基础设施建设和混沌工程等。2014年年底有幸加入DockOne社区,作为译者,利用闲暇时间为社区贡献一些微薄的力量。欢迎邮件联系(wjx_colstu@hotmail.com)。

梁晓勇,毕业于厦门大学,现任齐家网技术总监,DockOne社区编外人员。长期奋战在技术研发第一线,在网络管理、技术开发、架构设计等方面略有心得。热爱互联网技术,积极投身开源社区,对Docker等容器技术具有浓厚兴趣。欢迎邮件联系(sunlxy@yahoo.com)。

黄博文,ThoughtWorks资深软件工程师/咨询师,拥有丰富的敏捷团队工作经验。目前专注于DevOps技术及云端架构,在搭建持续集成及部署平台、自动化构建基础设施、虚拟化环境以及云端运维等方面有着丰富的经验。拥有AWS解决方案架构师以及开发者证书。译作有《Effective JavaScript》《响应式Web设计:HTML5和CSS3实践指南》《C#多线程编程实战》等。个人邮箱为huangbowen521@gmail.com。


提供了应用Docker解决用户当前所遇到的问题的大量实用建议。

——Docker公司Ben Firshman,来自本书第1版序

充满了高水准的技巧!

——Chad Davis, SolidFire

读完本书后,你会爱上Docker。

—— José San Leandro, OSOCO

充满了软件开发行业里能用到的Docker技巧。

——Kirk Brattkus, Net Effect Technologies

一些非常棒的使用Docker的技巧集。非常实用,可以解决现实世界里遇到的一些Docker问题。

——亚马逊客户

浅显易懂,循序渐进。阅读本书后,我对Docker的内部工作原理有了更好的理解。

——亚马逊客户


2013年9月,浏览黑客志(Hacker News)的时候,我无意中在《连线》上看到一篇介绍一项叫作“Docker”的新技术的文章。在读到这篇文章时,我便意识到Docker所拥有的革命性的潜力,并为此兴奋不已。

我工作了十余年的这家公司一直饱受软件交付速度不够快的困扰。准备环境是一件高成本、费时、需要手工操作且十分不优雅的事情。几乎没有持续集成,而且配置开发环境也是一件很考验耐心的事情。因为我的职位含有“DevOps经理”的字样,所以我有特别的动力来解决这些问题!

我通过公司的邮件列表招募了一批积极进取的同事(他们中有一位如今是我的合著者),接着我们的创新团队一起努力,将一个尚处于测试阶段的工具变为商业优势,为公司省去了高昂的虚拟机成本,并且开启了构建和部署软件的新思路。我们甚至构建并开源了一款自动化工具(ShutIt),以满足我们的组织的交付需求。

Docker为我们提供了一个打包和维护的工具,它解决了很多仅靠我们自己很难逾越的难题。这是开源技术最棒的地方,它为我们提供了利用业余时间接受挑战的机会,帮助克服技术债务,并且每天都能有收获。我们可以从中学到的不只是Docker,还包括持续集成、持续交付、打包、自动化以及人们该如何应对日新月异的技术革新。

对我们来说,Docker是一个用途异常广泛的工具。只要使用Linux系统来运行软件,Docker便有用武之地。这也使编写这一主题的图书充满了挑战,毕竟我们的视角是落在广袤的软件本身上的。为了迎合软件生产方式这样一个本质上的变化,Docker生态系统也在飞速地产出新的解决方案,这也使写书的任务变得更加艰巨。随着时间的推移,我们开始逐渐了解这些问题和解决方案的本质,而在本书里,我们将竭尽所能地传达这些经验。这可以帮助读者找出满足自己的特定技术和业务约束场景的解决方案。

在聚会上发表演讲时,我们也为Docker在愿意接纳它的组织内部如此迅速地变得高效而感到震撼。本书如实讲述了我们是怎样使用Docker的,涵盖了从桌面到DevOps流水线,再一路到生产环境的整个过程。因此,这本书可能会显得不那么正统,但是作为工程师,我们相信纯粹性有时候必须让步于实用性,尤其是当涉及节约成本方面的话题时!本书的所有内容均来源于一线生产的实际经验,我们衷心希望读者可以从我们来之不易的经验中获益。

Ian Miell


如果没有我们最亲近的人的支持、牺牲和耐心,这本书是绝对无法完成的。特别要提到的是Stephen Hazleton,本书的不少内容正是他为了使Docker能够造福我们的客户,同我们一起不懈努力的成果。

几位Docker的贡献者和Docker的员工还非常热情地在不同阶段帮忙审阅了本书的内容,并且提供了许多有价值的反馈,下面几位阅读了本书的初稿:Benoit Benedetti、Burkhard Nestmann、Chad Davis、David Moravec、Ernesto Cárdenas Cangahuala、Fernando Rodrigues、Kirk Brattkus、Pethuru Raj、Scott Bates、Steven Lembark、Stuart Woodward、Ticean Bennett、Valmiky Arquissandas和 Wil Moore III。José San Leandro担任了我们的技术审校,感谢他的敏锐目光。

最后,本书很大程度上还归功于Manning出版社的编辑团队,他们用自己的方式推动我们改进,使这本书虽不至于完美,但已然做到尽可能好。我们希望他们会为在我们身上付出的努力而感到自豪。

Ian Miell感谢Sarah、Isaac和Rachel,感谢你们忍受我深夜编程,包容一位紧盯着笔记本屏幕的父亲,并且没完没了地念叨“Docker这Docker那,Docker……”,还要感谢我的父母从小就鼓励我去质疑现状,还给我买Spectrum腕表。

Aidan Hobson Sayers感谢Mona的支持和鼓励,感谢我的父母睿智和鼓励的话,还有我的合著者决定命运的那封“有谁试过Docker这种东西?”电子邮件。


Docker可以说是目前增长速度最快的软件项目。它于2013年3月开源,到2018年它已经在GitHub上获得了近50 000个星以及超过14 000次fork。它还接受了大量像Red Hat、IBM、微软、谷歌、思科和Vmware这些厂商的拉取请求(pull request)。

Docker在这个关键时刻的出现正是为了迎合许多软件组织的一个迫切需求:以一种开放和灵活的方式来构建软件,然后在不同环境下能够可靠和一致地部署它。用户不需要学习新的编程语言、购买昂贵的硬件,也无须再为了用Docker构建、分发和运行可移植的应用而在安装或配置过程上花费过多精力。

本书将会通过我们在不同场景下用到的一些技术,带读者领略真实世界里的Docker实践案例。我们已经竭力尝试阐明这些技术,尽可能做到无须在阅读前事先具备其他相关技术的知识背景。我们假定读者了解一些基本的开发技术和概念,如开发一些结构化代码的能力,以及对软件开发和部署流程的一些了解。此外,我们认为读者还应了解一些核心的源代码管理理念,并且对像TCP/IP、HTTP和端口这样的网络基础知识有基本的了解。其他不怎么主流的技术会在我们介绍到的时候予以说明。

我们将从第一部分介绍Docker的基础知识开始,而到了第二部分,我们将把重点放在介绍如何将Docker用到单台机器的开发环境。在第三部分里,我们将介绍Docker在DevOps流水线中的用法,介绍持续集成、持续交付和测试的内容。第四部分介绍如何通过编排以一种可扩展的方式运行Docker容器。第五部分介绍在生产环境运行Docker的过程,重点介绍标准的生产环境运维的选项、可能出现的问题以及如何处理。

Docker是一个用途广泛、灵活和动态的工具,以至于没有一点儿魄力的话很难追上它快速发展的脚步。我们会尽力通过真实世界的应用和例子,让读者更好地理解其中的一些关键概念,目的是希望读者能够有实力、有信心在Docker的生态系统里审慎评估未来采用的工具和技术。我们一直在努力让阅读本书变得更像是一次愉快的旅行,即介绍我们在很多方面见证的Docker是怎样使我们的生活变得更加轻松甚至于更加有趣的。我们正沉浸在Docker以一种别致的方式为我们呈现的覆盖整个软件生命周期的许多有意思的软件技术里,而我们希望本书的读者同样也能分享这样的体验。

本书包括16章,分为5个部分。

第一部分为本书的其余部分奠定了基础,介绍Docker的概念并且教读者执行一些基本的Docker命令。第2章让读者熟悉Docker的客户-服务器架构以及如何调试它,这对在非常规的Docker配置中定位问题是非常有用的。

第二部分关注让读者熟悉Docker以及在自己的机器上如何充分利用Docker。我们将用到一个读者可能比较熟悉的相关概念——虚拟机,这是第3章的基础并为读者提供一个更容易上手使用Docker的途径。然后第 4 章、第 5 章和第 6章会详细介绍几个我们发现自己每天构建镜像、运行镜像以及管理Docker本身都在使用的Docker技巧。这一部分的最后一章则通过一些配置管理技巧,探索更为深入的镜像构建方面的主题。

第三部分从关注Docker在DevOps上下文中的使用开始,从用它完成软件构建和测试的自动化到将它迁移至不同的环境。这一部分还会花一章的篇幅来总结Docker的虚拟网络,引入Docker Compose,并且覆盖一些更为高级的网络主题,如网络模拟以及Docker网络插件等。

第四部分探讨容器编排的主题。我们将带领读者从单台主机上的单个容器穿梭到一个运行在“数据中心即操作系统”上的基于Docker的平台。第13章会展开讨论选择一个基于Docker的平台时必须考虑的方方面面,它还可以作为企业架构师在思考如何实现此类技术时的参考指南。

第五部分会介绍几个针对在生产环境中如何有效地利用Docker的主题。第14章讨论的是安全性的重要话题,阐明了如何锁定在容器里运行的进程,以及如何限制访问对外公开的Docker守护进程。最后两章则会细讲一些在生产环境中运行Docker的重要实用信息。第15章会展示如何将经典的系统管理知识应用到容器上下文中,从日志记录到资源限制,而第16章着眼于一些读者可能遇到的问题并且给出对应的调试和解决步骤。

附录里则是一些以不同方式安装、使用和配置Docker的具体细节,包括在虚拟机里以及在Windows上。

本书中用到的所有由作者创建的工具、应用以及Docker镜像的源代码都可以在Manning出版社的官方网站下载。Docker Hub上dockerinpractice用户下的镜像均是从其中一个GitHub仓库自动构建生成的。我们已经意识到读者可能会有兴趣对技术背后的一些源代码做进一步的研究,因此在技术讨论里也嵌入了相关仓库的链接。

为了方便读者阅读,本书中列出的大量代码均以终端会话的形式,与相应的命令输出一起展示。这些会话里有几件事情要注意一下。


本书的封面图片的标题是“一个来自克罗地亚赛尔切的男人”(Man from Selce, Croatia)。这张图片取自19世纪中期Nikola Arsenovic的一本克罗地亚传统服饰图集的复刻版,由克罗地亚斯普利特人种学博物馆在2003年出版。该图由人种学博物馆的一位热心的图书管理员提供。该博物馆位于中世纪时罗马帝国的核心城镇,从约公元304年起,罗马帝国皇帝戴克里先退位后居住的皇宫的遗迹就在这里。这本书中涵盖了来自克罗地亚各个地区的华丽的彩色图片,并介绍了他们的服饰和日常生活。

在这过去的200年里,服饰和生活方式都发生了巨大的变化,各地当时的特色也已随时间消逝。如今,来自不同大陆的人都已经变得难以区分,更不用说相隔仅几公里的村镇居民了。或许,文化多样性也是我们为获得丰富多彩的个人生活而付出的代价——现在的生活无疑是更多姿多彩的、快节奏的高科技生活。

Manning出版社用两个世纪前各地独具特色的生活方式来赞美计算机行业的诞生和发展,并用古老的书籍和图册中的图片让我们领略那个时代的风土人情。


本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

本书提供配套源代码下载,想要获得源代码,请在异步社区本书页面中单击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线投稿(直接访问https://www.epubit.com/contribute即可)。

如果您来自学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。

异步社区

微信服务号


本书的第一部分由第1章和第2章构成,将带领读者开始使用Docker,并讲解其基础知识。

第1章阐述Docker的起源及其核心概念,如镜像、容器和分层。在第1章的最后,读者将动手使用Dockerfile创建自己的第一个镜像。

第2章介绍一些有用的技巧,让读者深入理解Docker的架构。我们将通过依次讲解每个主要组件,阐述Docker守护进程与其客户端、Docker注册中心和Docker Hub之间的关系。

在第一部分结束时,读者将对Docker的核心概念有所了解,并能够演示一些有用的技巧,为本书的后续内容打下坚实的基础。


本章主要内容

Docker是一个允许用户“在任何地方构建、分发及运行任何应用”的平台。它在极短的时间内发展壮大,目前已经被视为解决软件中最昂贵的方面之一——部署的一个标准方法。

在Docker出现之前,开发流水线通常涉及用于管理软件活动的不同技术的组合,如虚拟机、配置管理工具、包管理系统以及各类复杂的依赖库网站。所有这些工具需要由专业的工程师管理和维护,并且多数工具都具有自己独特的配置方式。

Docker改变了这一切,允许不同的工程师参与到这个过程中,有效地使用同一门语言,让协作变得轻而易举。所有东西通过一个共同的流水线转变成可以在任何目标平台上使用的单一的产出——无须继续维护一堆让人眼花缭乱的工具配置项,如图1-1所示。

与此同时,只要现存的软件技术栈依然有效,用户就无须抛弃它——可以将其原样打包到一个Docker容器内,供其他人使用。由此获得的额外好处是,用户清楚这些容器是如何构建的,因此如果需要深挖细节,也是完全没问题的。

本书针对的是具有一定Docker知识的中级开发人员。如果读者对本书的基础部分较熟悉,可随意跳到后续章节。本书的目标是揭示Docker所带来的现实世界的挑战,并展示其解决之道。不过,首先我们将提供一个Docker自身的快速回顾。如果读者想了解更全面的Docker基础,请查阅Jeff Nickoloff编写的Docker in Action一书(Manning,2016)。

第2章将更深入地介绍Docker的架构,并通过一些技巧来演示其威力。在本章中,读者将了解到Docker是什么、为什么它很重要,并开始使用它。

图1-1 Docker如何消除了工具维护的负担

在动手实践之前,我们将对Docker稍作讨论,以便读者了解它的背景、“Docker”名字的来历以及为什么使用它。

要理解Docker是什么,从一个比喻开始会比技术性解释来得简单,而且这个Docker的比喻非常有说服力。Docker这个单词原本是指在船只停靠港口之后将商品移进或移出的码头工人。箱子和物品的大小、形状各异,而有经验的码头工人能以合算的方式手工将商品装入船只,因而他们倍受青睐(见图1-2)。雇人搬东西并不便宜,但除此之外别无他法。

对在软件行业工作的人来说,这听起来应该很熟悉。大量时间和精力被花在将奇形怪状的软件放置到装满了其他奇形怪状的软件、大小各异的船只上,以便将其卖给其他地方的用户或商业机构。

图1-3展示了使用Docker概念时如何能节省时间和金钱。在Docker出现之前,部署软件到不同环境所需的工作量巨大。即使不是采用手工运行脚本的方式在不同机器上进行软件配备(有很多人这么做),用户也不得不全力应付那些配置管理工具,它们掌管着渴求资源且快速变化的环境的状态。即便将这些工作封装到虚拟机中,还是需要花费大量时间来部署这些虚拟机,等待它们启动并管理它们所产生的额外的资源开销。

图1-2 标准化集装箱前后的航运对比

使用Docker,配置工作从资源管理中分离了出来,而部署工作则不值一提:执行docker run,环境的镜像会被拉取下来并准备运行,所消耗的资源更少并且是内含的,因此不会干扰其他环境。

读者无须担心容器是被分发到Red Hat机器、Ubuntu机器还是CentOS虚拟机镜像中,只要上面有Docker,就能良好地运行。

图1-3 使用Docker前后软件交付的对比

几个重要的实际问题出现了:为什么要使用Docker?Docker用在什么地方?针对“为什么”的简要答案是:只需要一点点付出,Docker就能快速为企业节省大量金钱。部分方法(肯定不是所有的)将在随后的几节中讨论。我们已经在实际工作环境中切身体会到所有这些益处。

1.替代虚拟机(VM)

Docker可以在很多情况下替代虚拟机。如果用户只关心应用程序而不是操作系统,可以用Docker替代虚拟机,把操作系统交给其他人去考虑。Docker不仅启动速度比虚拟机快,迁移时也更为轻量,同时得益于它的分层文件系统,与其他人共享变更时更简单、更快捷。而且,它牢牢地扎根在命令行中,非常适合脚本化。

2.软件原型

如果想快速体验软件,同时避免干扰目前的设置或配备一台虚拟机带来的麻烦,Docker可以在几毫秒内提供一个沙盒环境。在亲身体验之前,用户很难感受到这种解放的效果。

3.打包软件

因为对Linux用户而言,Docker镜像实际上没有依赖,所以非常适合用于打包软件。用户可以构建镜像,并确保它可以运行在任何现代Linux机器上——就像Java一样,但不需要JVM。

4.让微服务架构成为可能

Docker 有助于将一个复杂系统分解成一系列可组合的部分,这让用户可以用更离散的方式来思考其服务。用户可以在不影响全局的前提下重组软件,使其各部分更易于管理和可插拔。

5.网络建模

由于可以在一台机器上启动数百个(甚至数千个)相互隔离的容器,因此对网络进行建模轻而易举。这对于现实世界场景的测试非常有用,而且所费无几。

6.离线时启用全栈生产力

因为可以将系统的所有部分捆绑在Docker容器中,所以用户可以将其编排运行在笔记本电脑中移动办公,即便在离线时也毫无问题。

7.降低调试支出

不同团队之间关于软件交付的复杂谈判在业内司空见惯。我们亲身经历过不计其数的这类讨论:失效的库、有问题的依赖、更新被错误实施或是执行顺序有误,甚至可能根本没执行以及无法重现的错误等。估计读者也遇到过这些问题。Docker让用户可以清晰地说明(即便是以脚本的形式)在一个属性已知的系统上调试问题的步骤,错误和环境重现变得更简单,而且通常与所提供的宿主机环境是分离的。

8.文档化软件依赖及接触点

通过使用结构化方式构建镜像,为迁移到不同环境做好准备,Docker 强制用户从一个基本出发点开始明确地记录软件依赖。即使用户不打算在所有地方都使用Docker,这种文档记录也有助于在其他地方安装软件。

9.启用持续交付

持续交付(continuous delivery,CD)是一个基于流水线的软件交付范型,该流水线通过一个自动化(或半自动化)流程在每次变动时重新构建系统然后交付到生产环境中。

因为用户可以更准确地控制构建环境的状态,Docker 构建比传统软件构建方法更具有可重现性和可复制性。使持续交付的实现变得更容易。通过实现一个以Docker为中心的可重现的构建过程,标准的持续交付技术,如蓝/绿部署(blue/green deployment,在生产环境中维护“生产”和“最新”部署)和凤凰部署(phoenix deployment,每次发布时都重新构建整个系统)变得很简单。

现在,我们对Docker如何能够提供帮助有了一定了解。在进入一个真实示例之前,让我们来了解几个核心概念。

在本节中,我们将介绍一些关键的Docker概念,如图1-4所示。

图1-4 Docker的核心概念

在开始执行Docker命令之前,将镜像、容器及层的概念牢记于心是极其有用的。简而言之,容器运行着由镜像定义的系统。这些镜像由一个或多个(或差异集)加上一些Docker的元数据组成。

让我们来看一些核心的Docker命令。我们将把镜像转变成容器,修改它们,并添加层到我们即将提交的新镜像中。如果这一切听上去有点儿混乱,不用太担心。在本章结束时,一切都将更加清晰。

1.关键的Docker命令

Docker的中心功能是构建、分发及在任何具有Docker的地方运行软件。对终端用户而言,Docker是他们运行的一个命令行程序。就像Git(或任何源代码控制工具)一样,这个程序具有用于执行不同操作的子命令。表1-1中列出了将在宿主机上使用的主要的Docker子命令。

表1-1 Docker子命令

命 令

目 的

docker build

构建一个Docker镜像

docker run

以容器形式运行一个Docker镜像

docker commit

将一个Docker容器作为一个镜像提交

docker tag

给一个Docker镜像打标签

2.镜像与容器

如果读者不熟悉Docker,可能这是第一次听说本书所说的“容器”和“镜像”这两个词。它们很可能是Docker中最重要的概念,因此有必要花点儿时间明确其差异。在图1-5中,读者将看到这些概念的展示,里面是从一个基础镜像启动的3个容器。

图1-5 Docker镜像与容器

看待镜像和容器的一种方式是将它们类比成程序与进程。一个进程可以视为一个“被执行的应用程序”,同样,一个Docker容器可以视为一个运行中的Docker镜像。

如果读者熟悉面向对象原理,看待镜像和容器的另一种方法是将镜像看作类而将容器看作对象。对象是类的具体实例,同样,容器是镜像的实例。用户可以从单个镜像创建多个容器,就像对象一样,它们之间全都是相互隔离的。不论用户在对象内修改了什么,都不会影响类的定义——它们从根本上就是不同的东西。

现在,我们要动手使用Docker来构建一个简单的“to-do”应用程序(todoapp)镜像了。在这个过程中,读者会看到一些关键的Docker功能,如Dockerfile、镜像复用、端口公开及构建自动化。这是接下来10分钟读者将学到的东西:

to-do应用是协助用户跟踪待完成事项的一个应用程序。我们所构建的应用将存储并显示可被标记为已完成的信息的简短字符串,它以一个简单的网页界面呈现。图1-6展示了如此操作将得到的结果。

图1-6 构建一个Docker应用程序

应用程序的细节不是重点。我们将演示的是,读者可以从我们所提供的一个简短的Dockerfile放心地在自己的宿主机上使用与我们相同的方法构建、运行、停止和启动一个应用程序,而无须考虑应用程序的安装或依赖。这正是 Docker为我们提供的关键部分——可靠地重现并简便地管理和共享开发环境。这意味着用户无须再遵循并迷失在那些复杂的或含糊的安装说明中。

 

注意 

这个to-do应用程序将贯穿本书,多次使用,它非常适合用于实践和演示,因此值得读者熟悉一下。

 

创建Docker镜像有4种标准的方式。表1-2逐一列出了这些方法。

表1-2 创建Docker镜像的方式

方 法

描 述

详见技巧

Docker命令/“手工”

使用docker run启动一个容器,并在命令行输入命令来创建镜像。使用docker commit来创建一个新镜像

详见技巧15

Dockerfile

从一个已知基础镜像开始构建,并指定一组有限的简单命令来构建

稍后讨论

Dockerfile及配置管理(configuration management,CM)工具

与Dockerfile相同,不过将构建的控制权交给了更为复杂的CM工具

详见技巧55

从头创建镜像并导入一组文件

从一个空白镜像开始,导入一个含有所需文件的TAR文件

详见技巧11

如果用户所做的是概念验证以确认安装过程是否正常,那么第一种“手工”方式是没问题的。在这个过程中,用户应对所采取的步骤做记录,以便在需要时回到同一点上。

到某个时间点,用户会想要定义创建镜像的步骤。这就是Dockerfile方式(也就是我们这里所用的方式)。

对于更复杂的构建,用户需要使用第三种方式,特别是在Dockerfile功能还不足以满足镜像要求的时候。

最后一种方式从一个空镜像开始,通过叠加一组运行镜像所需要的文件进行构建。如果用户想导入一组在其他地方创建好的自包含的文件,这将非常有用,不过这种方法在主流应用中非常罕见。

现在,我们来看一下Dockerfile方法,其他方法将在本书后面再做说明。

Dockerfile是一个包含一系列命令的文本文件。本示例中我们将使用的Dockerfile如代码清单1-1所示。创建一个新目录,移动到这个目录里,然后使用这些内容创建一个名为“Dockerfile”的文件。

代码清单1-1 todoapp Dockerfile

FROM node  ⇽--- 定义基础镜像
LABEL maintainer ian.miell@gmail.com  ⇽--- 声明维护人员
RUN git clone -q https://github.com/docker-in-practice/todo.git  ⇽--- 克隆todoapp代码
WORKDIR todo  ⇽--- 移动到新的克隆目录
RUN npm install > /dev/null  ⇽--- 执行node包管理器的安装命令(npm)
EXPOSE 8000  ⇽--- 指定从所构建的镜像启动的容器需要监听这个端口
CMD ["npm","start"]  ⇽--- 指定在启动时需要执行的命令

Dockerfile的开始部分是使用FROM命令定义基础镜像。本示例使用了一个Node.js镜像以便访问Node.js程序。官方的Node.js镜像名为node

接下来,使用LABEL命令声明维护人员。在这里,我们使用的是其中一个人的电子邮件地址,读者也可以替换成自己的,因为现在它是你的Dockerfile了。这一行不是创建可工作的Docker镜像所必需的,不过将其包含进来是一个很好的做法。到这个时候,构建已经继承了node容器的状态,读者可以在它上面做操作了。

接下来,使用RUN命令克隆todoapp代码。这里使用指定的命令获取应用程序的代码:在容器内运行git。在这个示例中,Git是安装在基础node镜像里的,不过读者不能对这类事情做假定。

现在使用WORKDIR命令移动到新克隆的目录中。这不仅会改变构建环境中的目录,最后一条WORKDIR命令还决定了从所构建镜像启动容器时用户所处的默认目录。

接下来,执行node包管理器的安装命令(npm)。这将为应用程序设置依赖。我们对输出的信息不感兴趣,所以将其重定向到/dev/null上。

由于应用程序使用了8000端口,使用EXPOSE命令告诉Docker从所构建镜像启动的容器应该监听这个端口。

最后,使用CMD命令告诉Docker在容器启动时将执行哪条命令。

这个简单的示例演示了Docker及Dockerfile的几个核心功能。Dockerfile是一组严格按顺序执行的有限的命令集的简单序列。它影响了最终镜像的文件和元数据。这里的RUN命令通过签出并安装应用程序影响了文件系统,而EXPOSECMDWORKDIR命令影响了镜像的元数据。

读者已经定义了自己的Dockerfile的构建步骤。现在可以键入图1-7所示的命令,从而构建Docker镜像了。

图1-7 docker build子命令

输出看起来和下面类似。

Sending build context to Docker daemon 2.048kB  ⇽---  Docker会上传docker build指定目录下的文件和目录
Step 1/7 : FROM node  ⇽--- 每个构建步骤从 1 开始按顺序编号,并与命令一起输出
 ---> 2ca756a6578b  ⇽--- 每个命令会导致一个新镜像被创建,其镜像ID在此输出
Step 2/7 : LABEL maintainer ian.miell@gmail.com
 ---> Running in bf73f87c88d6
 ---> 5383857304fc
Removing intermediate container bf73f87c88d6  ⇽--- 为节省空间,在继续前每个中间容器会被移除
Step 3/7 : RUN git clone -q https://github.com/docker-in-practice/todo.git
 ---> Running in 761baf524cc1
 ---> 4350cb1c977c
Removing intermediate container 761baf524cc1
Step 4/7 : WORKDIR todo
 ---> a1b24710f458
Removing intermediate container 0f8cd22fbe83
Step 5/7 : RUN npm install > /dev/null
 ---> Running in 92a8f9ba530a
npm info it worked if it ends with ok  ⇽--- 构建的调试信息在此输出(限于篇幅,本代码清单做了删减)
 [...]
npm info ok
 ---> 6ee4d7bba544
Removing intermediate container 92a8f9ba530a
Step 6/7 : EXPOSE 8000
 ---> Running in 8e33c1ded161
 ---> 3ea44544f13c
Removing intermediate container 8e33c1ded161
Step 7/7 : CMD npm start
 ---> Running in ccc076ee38fe
 ---> 66c76cea05bb
Removing intermediate container ccc076ee38fe
Successfully built 66c76cea05bb  ⇽--- 此次构建的最终镜像ID,可用于打标签

现在,拥有了一个具有镜像ID(前面示例中的“66c76cea05bb”,不过读者的ID会不一样)的Docker镜像。总是引用这个ID会很麻烦,可以为其打标签以方便引用,如图1-8所示。

图1-8 docker tag子命令

输入图1-8所示的命令,将66c76cea05bb替换成读者生成的镜像ID。

现在就能从一个Dockerfile构建自己的Docker镜像副本,并重现别人定义的环境了!

读者已经构建出Docker镜像并为其打上了标签。现在可以以容器的形式来运行它了。运行后的输出结果如代码清单1-2所示。

代码清单1-2 todoapp的docker run输出

$ docker run -i -t -p 8000:8000 --name example1 todoapp  ⇽---  docker run子命令启动容器,-p将容器的 8000 端口映射到宿主机的8000端口上,--name给容器赋予一个唯一的名字,最后一个参数是镜像
npm install
npm info it worked if it ends with ok
npm info using npm@2.14.4
npm info using node@v4.1.1
npm info prestart todomvc-swarm@0.0.1

> todomvc-swarm@0.0.1 prestart /todo  ⇽--- 容器的启动进程的输出被发送到终端中
> make all

npm install
npm info it worked if it ends with ok
npm info using npm@2.14.4
npm info using node@v4.1.1
npm WARN package.json todomvc-swarm@0.0.1 No repository field.
npm WARN package.json todomvc-swarm@0.0.1 license should be a valid SPDX
➥ license expression
npm info preinstall todomvc-swarm@0.0.1
npm info package.json statics@0.1.0 license should be a valid SPDX license
➥ expression
npm info package.json react-tools@0.11.2 No license field.
npm info package.json react@0.11.2 No license field.
npm info package.json node-
     jsx@0.11.0 license should be a valid SPDX license expression
npm info package.json ws@0.4.32 No license field.
npm info build /todo
npm info linkStuff todomvc-swarm@0.0.1
npm info install todomvc-swarm@0.0.1
npm info postinstall todomvc-swarm@0.0.1
npm info prepublish todomvc-swarm@0.0.1
npm info ok
if [ ! -e dist/ ]; then mkdir dist; fi
cp node_modules/react/dist/react.min.js dist/react.min.js

LocalTodoApp.js:9:    // TODO: default english version
LocalTodoApp.js:84:            fwdList = this.host.get('/TodoList#'+listId);
 // TODO fn+id sig
TodoApp.js:117:        // TODO scroll into view
TodoApp.js:176:        if (i>=list.length()) { i=list.length()-1; } // TODO
➥ .length
local.html:30:    <!-- TODO 2-split, 3-split -->
model/TodoList.js:29:        // TODO one op - repeated spec? long spec?
view/Footer.jsx:61:        // TODO: show the entry's metadata
view/Footer.jsx:80:            todoList.addObject(new TodoItem()); // TODO
➥ create default
view/Header.jsx:25:        // TODO list some meaningful header (apart from the
➥ id)

npm info start todomvc-swarm@0.0.1

> todomvc-swarm@0.0.1 start /todo
> node TodoAppServer.js

Swarm server started port 8000
^Cshutting down http-server...   ⇽--- 在此按组合键Ctrl+C终止进程和容器
 closing swarm host...
swarm host closed
npm info lifecycle todomvc-swarm@0.0.1~poststart: todomvc-swarm@0.0.1
npm info ok
$ docker ps -a  ⇽--- 执行这个命令查看已经启动和移除的容器,以及其ID和状态(就像进程一样)
 CONTAINER ID  IMAGE    COMMAND      CREATED        STATUS PORTS NAMES
b9db5ada0461  todoapp  "npm start"  2 minutes ago  Exited (0) 2 minutes ago
➥                example1
$ docker start example1  ⇽--- 重新启动容器,这次是在后台运行
 example1
$ docker ps
CONTAINER ID  IMAGE    COMMAND      CREATED       STATUS
➥ PORTS                    NAMES
b9db5ada0461  todoapp  "npm start"  8 minutes ago  Up 10 seconds
➥ 0.0.0.0:8000->8000/tcp example1  ⇽--- 再次执行ps命令查看发生变化的状态
$ docker diff example1  ⇽---  docker diff子命令显示了自镜像被实例化成一个容器以来哪些文件受到了影响
C /root
C /root/.npm
C /root/.npm/_locks
C /root/.npm/anonymous-cli-metrics.json
C /todo  ⇽--- 修改了/todo目录(C)
A /todo/.swarm  ⇽--- 增加了/todo/.swarm目录(A)
A /todo/.swarm/_log
A /todo/dist
A /todo/dist/LocalTodoApp.app.js
A /todo/dist/TodoApp.app.js
A /todo/dist/react.min.js
C /todo/node_modules

docker run子命令启动容器。-p标志将容器的8000端口映射到宿主机的8000端口上,读者现在应该可以使用浏览器访问http://localhost:8000来查看这个应用程序了。--name标志赋予了容器一个唯一的名称,以便后面引用。最后的参数是镜像名称。

一旦容器启动,我们就可以按组合键Ctrl+C终止进程和容器。读者可以执行ps命令查看被启动且未被移除的容器。注意,每个容器都具有自己的容器 ID 和状态,与进程类似。它的状态是Exited(已退出),不过读者可以重新启动它。这么做之后,注意状态已经改变为Up(运行中),且容器到宿主机的端口映射现在也显示出来了。

docker diff子命令显示了自镜像被实例化成一个容器以来哪些文件受到了影响。在这个示例中,todo目录被修改了(C),而其他列出的文件是新增的(A)。没有文件被删除(D),这是另一种可能性。

如读者所见,Docker“包含”环境的事实意味着用户可以将其视为一个实体,在其上执行的动作是可预见的。这赋予了Docker宽广的能力——用户可以影响从开发到生产再到维护的整个软件生命周期。这种改变正是本书所要描述的,在实践中展示Docker所能完成的东西。

接下来读者将了解Docker的另一个核心概念——分层。

Docker分层协助用户管理在大规模使用容器时会遇到的一个大问题。想象一下,如果启动了数百甚至数千个to-do应用,并且每个应用都需要将文件的一份副本存储在某个地方。

可想而知,磁盘空间会迅速消耗光!默认情况下,Docker在内部使用写时复制(copy-on-write)机制来减少所需的硬盘空间量(见图 1-9)。每当一个运行中的容器需要写入一个文件时,它会通过将该项目复制到磁盘的一个新区域来记录这一修改。在执行Docker提交时,这块磁盘新区域将被冻结并记录为具有自身标识符的一个层。

图1-9 启动时复制与写时复制对比

这一部分解释了Docker容器为何能如此迅速地启动——它们不需要复制任何东西,因为所有的数据已经存储为镜像。

 

提示 

写时复制是计算技术中使用的一种标准的优化策略。在从模板创建一个新的(任意类型)对象时,只在数据发生变化时才能将其复制进来,而不是复制整个所需的数据集。依据用例的不同,这能省下相当可观的资源。

 

图1-10展示了构建的to-do应用,它具有我们所感兴趣的3个层。因为层是静态的,所以如果用户需要更改更高层上的任何东西,都可以在想引用的镜像之上进行构建。在这个to-do应用中,我们从公开可用的node镜像构建,并将变更叠加在最上层。

所有这3个层都可以被多个运行中的容器共享,就像一个共享库可以在内存中被多个运行中的进程共享一样。对于运维人员来说,这是一项至关重要的功能,可以在宿主机上运行大量基于不同镜像的容器,而不至于耗尽磁盘空间。

想象一下,将所运行的to-do应用作为在线服务提供给付费用户。你可以将服务扩散给大量用户。如果是在开发中,你可以一次在本地机器上启动多个不同的环境。如果是在进行测试,你可以比之前同时运行更多测试,速度也更快。有了分层,所有这些东西都成为可能。

图1-10 Docker中to-do应用的文件系统分层

通过使用Docker构建和运行一个应用程序,读者开始见识到Docker能给工作流带来的威力。重现并共享特定的环境,并能在不同的地方落地,让开发过程兼具灵活性和可控性。


相关图书

云原生测试实战
云原生测试实战
Kubernetes快速入门(第2版)
Kubernetes快速入门(第2版)
Kubernetes零基础实战
Kubernetes零基础实战
深入浅出Windows API程序设计:核心编程篇
深入浅出Windows API程序设计:核心编程篇
深入浅出Windows API程序设计:编程基础篇
深入浅出Windows API程序设计:编程基础篇
云原生技术中台:从分布式到云平台设计
云原生技术中台:从分布式到云平台设计

相关文章

相关课程