循序渐进学Docker
上QQ阅读APP看书,第一时间看更新
icon1

第1章 全面认识Docker

欢迎来到Docker的世界。

Docker, Golang社区杀手级的应用,是Github上最活跃的项目之一,也是开源社区最受欢迎的项目。

Docker,号称要成为所有云应用的基石,并把互联网升级到下一代。

开发、测试、运维人员看到Docker,都激动地说:“太好了,这正是我所需要的!”

Docker是什么,能解决什么问题,为什么这么火?本章将一一道来。

1.1 Docker是什么

首先,我们了解下Docker产生的历史背景和当前发展情况,通过和一些熟悉的事物做类比,让大家对Docker有一个初步认识和了解。

1.1.1 Docker的由来

Docker是dotCloud公司开源的一款产品。dotCloud公司是2010年新成立的一家公司,主要基于PaaS(Platform as a Service,平台即服务)平台为开发者提供服务。在PaaS平台下,所有的服务环境已经预先配置好了,开发者只需要选择服务类型、上传代码就可对外服务,不需要花费大量的时间搭建服务和配置环境。dotCloud的PaaS平台已经做得足够好了,它支持几乎所有主流的Web编程语言和数据库,可以让开发者随心所欲地选择自己需要的编程语言、数据库和编程框架,而且它的设置非常简单,每次编码后只需要运行一条命令就能把整个网站部署上去;并且利用多层次平台的概念,理论上,它的应用可以运行在各种类型的云服务上。两三年下来,虽然DotCloud也在业界获得不错的口碑,但由于整个PaaS市场还处于培育阶段,dotCloud公司表现得不温不火,没有出现爆发性的增长。

2013年,dotCloud的CEO Solomon Hykes决定把dotCloud内部使用的Container容器技术单独拿出来开源。2013年3月发布Docker的V0.1版本,并且基本保持每月一个版本的迭代速度,到了8月,Docker已经足够火爆,并广受好评,各种各样的技术论坛和技术峰会都开始热烈讨论与推荐Docker,这时Docker才只发布到V0.6版本。

随着Docker的流行,越来越多的优秀开发者加入Docker社区参加开发。这里值得一提的是,Docker是基于Linux3.8以上内核,在aufs分层文件系统下构建的,主要运行在Ubuntu的系统下。REHL/Centos当时最新版6系列还是基于Liunux2.6.32内核,无法运行Docker。为了让REHL/Centos尽快支持Docker, RedHat公司的工程师亲自出马,加班加点为Docker贡献代码,新增对devicemapper的支持来实现文件系统分层,终于顺利地让Docker在REHL/Centos运行起来。

随着Docker在业界的知名度越来越高,到了2013年10月,dotCloud公司索性更名为Docker股份有限公司,工作的重心也从PaaS平台业务转向全面围绕Docker来开发。到了2014年1月,Docker公司宣布完成15000万美元的融资,雅虎联合创始人杨致远也参与跟投。

虽然Docker迟迟没有发布1.0版,但好多公司已纷纷把Docker应用到生产环境。其中,美国奢饰品电商Gilt的CTO说:“使用Docker以后,突然之间,传统方式中的各种问题都消失了,我们接下来要考虑如何进一步提高软件生产效率,让软件开发更加安全和创新。这种转变太不可思议了!”

千呼万唤,到了2014年6月9日,Docker终于发布了V1.0版,并举办了DockerCon 2014大会,大会上来自Google、IBM、RedHat、Rackspace等公司的核心人物均发表了主题演讲,纷纷表示支持并加入Docker的阵营。Docker的CTO Solomon Hykes充满雄心壮志地说:“我们能把互联网升级到下一代!”Google的基础架构部副总裁Eric Brewer也附和道:“容器技术曾是Google的基础,我们和Docker联手,把容器技术打造为所有云应用的基石。”

Google自2004年就开始使用容器技术,目前他们每周要启动超过20亿个容器,每秒钟新启动的容器就超过3000个,在容器技术方面有大量的积累。曾相继开源了Cgroup和Imctfy这两个重量级项目。Google对Docker的支持力度非常大,不仅把Imctfy先进之处融入Docker中,还把自己的容器管理系统(kubernetes)也开源出来。

2014年8月,不缺钱的Docker再次融资,融资超4千万美元,估值达到4亿美元。

所有的云计算大公司,如Azure、Google和亚马逊等都在支持Docker技术,这实际上也让Docker成为云计算领域的一大重要组成部分。

2014年10月15日,Azure副总裁Jason Zander宣布了微软与Docker的合作伙伴关系;2014年11月5日,Google发布支持Docker的产品DockerGoogle Container Engine;2014年11月13日,Amazon发布支持Docker的产品AWS Container Service。至此,几个重要的云计算大公司都已经支持Docker技术,这不仅让Docker成为云计算领域的一个重要级成员,也让Docker成为云应用部署的事实上的标准。

2014年12月,Docker发布了Docker集群管理工具Machine和Swarm,标志着Docker开始突破一个标准的容器框架,打造属于Docker自己的集群平台和生态圈。

2015年4月,Docker公司宣布完成了9500万美元的D轮融资。

2015年10月,Docker收购Tutum, Tutum本身已经实现对亚马逊网络服务(AWS)、Digital Ocean、微软的Azure等主流云服务商的良好支持。

2016年1月,Docker官方计划全面支持自身的Alpine Linux,使用它构建的基础镜像最小只有5M。

截至2016年3月,Docker在Github上收获29962个关注(star)、8437个拷贝(Fork),在Github所有项目中排第7位,在云平台管理领域排名第一,远远超Openstack项目的1316个关注、768个拷贝。

1.1.2 Docker为什么这么火

Docker从诞生到现在,短短两年时间,已经成为开源社区最火爆的项目,风头已经远远盖过了近年来很流行的Puppet和OpenStack。那么Docker的火爆到底是一种炒作、一种跟风,还是它确实名副其实、众望所归呢?

要回答这个问题,首先看看当前我们所处的环境和面临的问题。

随着计算机近几十年的蓬勃发展,产生了大量优秀系统和软件。比如:

❑操作系统,如REHL/Centos、Debian/Unbuntu、FreeBSD、OpenSuse等。

❑编程语言,如Java、C/C++、Python、Ruby、Golang等。

❑Web服务器,如Apache、Nginx、Lighttpd等。

❑数据库,如Mysqld、Redis、Mongodb等。

现在的软件开发人员真是幸运,可以在这么多种类中自由选择。自由选择的结果是,维护一个非常庞大的开发、测试和生产环境,开发、测试和运维人员都被种类繁多的环境折腾得筋疲力尽,不得不收缩战线,每种类型的软件只选择一两种来支持。许多优秀的开发框架和软件尽管有不少优秀特性,但因为维护麻烦,便没有了用武之地。

即便每种类型的软件只选择一两种来支持,随着操作系统和软件版本的更新迭代,维护工作还是变得越来越庞大。

面对这种情况,业界大牛群策群力,给出了很多解决方案,比较有代表的是Puppet和OpenStack。

❑Puppet是集中的配置管理系统,它把文件、用户、cron任务、软件包、系统服务等抽象为资源,并通过自有的语言描述资源间的依赖关系,集中管理各类资源的安装配置。Puppet主要适用于需要大批量部署相同服务的应用场景。

❑OpenStack是开源的云计算管理平台项目,可以帮助企业内部实现类似于Amazon EC2的云基础架构服务。虽然灵活,但组件繁多、构建复杂,比较适合中大型企业使用。

Puppet和OpenStack虽然比较流行,但适应的场景有限,不具备通用性。正当大家在众多方案中左右为难时,Docker出现了,它作为一个开源的应用容器引擎,让开发者可以打包他们的应用及依赖环境到一个可移植的容器中,然后发布到任何运行有Docker引擎的机器上。它集版本控制、克隆继承、环境隔离等特性于一身,提出一整套软件构建、部署和维护的解决方案,可以非常方便地帮助开发人员,让大家可以随心所欲地使用软件而又不会深陷到环境配置中。

这只是Docker的一个应用场景而已,Docker还能干更多的事情。

作为计算机的从业人员,下面场景你或许碰到过。

❑小A是一名资深码农,作为新招聘实习生的导师,小A要给实习生的开发机装一套和自己开发机一样的运行环境,不仅要安装Nginx、Java、Mysqld和一些依赖库等,还要修改相关的配置文件。结果花了一天时间,小A也没把实习生的开发环境搞定,在徒弟面前颜面尽失,尴尬不已。

❑小B是一名QA测试工程师,他按开发给的文档、部署的服务,测试出一大堆问题,通过和开发的沟通,发现是开发和测试环境不一致引起的。

❑小C作为一名业务运维工程师,同时维护开发、测试、生产三套环境,经常在不同环境下装相同的包,做大量重复工作。

❑小D同时在为三个项目开发功能模块,他要不停地修改他的开发环境为适应在三个项目间开发、联调测试。

❑小E发现服务器被入侵过,他想知道什么文件被篡改过。

❑小F从离职同事那里接手一个系统,文档不全,突然一台机器硬件故障,他不知道该如何重新部署这个应用。

❑小G新上线一个游戏,游戏火爆超预期,需要紧急扩容,花了一两个小时才完成扩容,期间用户体验很卡,流失不少潜在用户。

❑小H和小I共同维护一套系统,分工轮流值夜班,但一出现突发故障,排查问题时,即便半夜,还需要把对方叫醒,确认下对方在前一天有没有变更过什么配置。

❑小M的一个机房要裁撤了,该机房的数千个应用都要迁移到其他机房,小M觉得这项工作非常庞大,半年时间都未必能完成。

但是如果使用Docker,这些根本不算事儿,分分钟就能搞定。

Docker的解决方案简单、灵活、高效,还很直观,甚至不需要过多地改变现有的使用习惯,就可以和已有的工具,如Puppet、OpenStack等配合使用。各种优势让Docker脱颖而出,有鹤立鸡群的感觉,Docker的火爆也就不难理解了。

1.1.3 Docker究竟是什么

按照官方的说法,Docker是一个开源的应用容器引擎。很多人觉得这个说法太抽象,不容易理解。

那我们就从最熟悉的事物说起吧,但凡从事过计算机相关行业的人,对Java、Android和Github都很熟悉。

先说Java,在Java之前的编程语言,像C/C++,是严重依赖平台的,在不同平台下,需要重新编译才能运行。Java的一个非常重要的特性就是与平台无关性,而使用Java虚拟机是实现这一特性的关键。Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成可以在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。

软件部署也依赖平台,Ubuntu的软件包在Centos下可能就运行不起来。和Java虚拟机类似,Docker使用容器引擎解决平台依赖问题,它在每台宿主机上都启动一个Docker的守护进程,守护进程屏蔽了与具体平台相关的信息,对上层应用提供统一的接口。这样,Docker化的应用,就可以在多个平台下运行,Docker会针对不同的平台,解析给不同平台下的执行驱动、存储驱动和网络驱动去执行。

Java曾提出“Write Once, Run Anywhere”,而Docker则提出了“Build once, Run anywhere, Configure once, Run anything”。虽然,Java和Docker是为了解决不同领域的问题,但在平台移植方面却面临相同的问题,使用的解决方式也相似。

提起Android,大家想到什么?它是一个开源的手机操作系统,也是一个生态圈,它的App应用以apk形式打包、发布,可以运行在任何厂商的Android手机上。它还有一个官方的安卓市场,提供各种各样的App,我们需要某个App时,就从安卓市场上搜索下载,手机开发者也可以编写一些App,发布到安卓市场,提供给别人使用, Android也允许在第三方的安卓市场上下载或上传应用。

如果把软件部署的应用看作Android的App, Docker简直和Android一模一样, Docker是一个开源的容器引擎,也有自己的生态圈,它的应用以镜像(image)的形式发布,可以运行在任何装有Docker引擎的操作系统上。它有一个官方的镜像仓库,提供各种各样的应用,当需要某个应用时,就从官方的仓库搜索并下载,个人开发者也可以提交镜像到官方仓库,分享给别人使用。Docker也允许使用第三方的镜像仓库。

最后,再谈谈Github。它主要用来做版本控制,不仅可以比较两个版本的差异,还可以基于某些历史版本创建新的分支。

使用Docker后,软件部署的应用也可以具备类似Github的版本控制功能,对应用做一些修改,提交新版本,运行环境可以在多个版本间快速切换,自由选择使用哪个版本对外提供服务。

通过和Java、Android、Github的对比,大家对Docker应该有了比较直观的认识, Docker用来管理软件部署的应用,Docker把应用打包成一个镜像,镜像带有版本控制功能,应用的每次修改迭代就对应镜像的一个版本,制作好的镜像可以发布到镜像仓库,分享给别人;也可以直接从镜像仓库下载别人制作好的应用,不做任何修改,即可运行起来。

1.2 Docker的结构与特性

通过上一小节的介绍,大家对Docker有一个初步的了解。这一节,再来聊一下Docker的组织结构。

1.2.1 Docker构成

如果把Docker当作一个独立的软件来看,它就是用Golang写的开源程序,采用C/S架构,包含Docker Server和Docker Client,源代码托管在https://github.com/docker/docker上。

如果把Docker看作一个生态的话,它主要由两部分组成:Docker仓库和Docker自身程序。拿iPhone做类比的话,Docker仓库相当于iPhone的Appstore(应用商店), Docker相当于iPhone的iOS手机操作系统。

1.Docker仓库

官方Docker仓库地址为https://hub.docker.com,上面的应用非常丰富,既有各大公司打包的应用,也有大量个人开发者提供的应用,如图1-1所示。

图1-1 Docker官方仓库截图

2.Docker自身程序

Docker本身是一个单机版的程序,它运行在Linux操作系统之上,属于用户态程序,通过一些接口和内核交互。它在机器上的位置如图1-2所示。

图1-2 Docker在Linux系统的位置

由于Docker需要用到Linux的cgroups、namespaces等特性,所以目前只能运行在Linux环境下,当然,通过虚拟机,也可以在Windows和Mac上使用Docker。

Docker是一个C/S的架构,它的Docker Daemon作为Server端,在宿主机上以后台守护进程的形式运行。Docker Client使用比较灵活,既可以在本机上以bin命令的形式(如Docker info、Docker start)发送指令,也可以在远端通过RESTful API的形式发送指令;Docker的Server端接收指令并把指令分解为一系列任务去执行。

3.工作流程

我们知道了Docker的构成,那么该如何使用Docker呢?

首先,要在Linux服务器上安装Docker软件包,并启动Docker Daemon守护进程。然后,就可以通过Docker Client端发送各种指令,Docker Daemon守护进程执行完指令,向Client端返回结果。

假如要启动一个新的Docker应用app1(名字是随便起的),它的工作流程大致如图1-3所示。

图1-3 Docker工作流程

1)Docker Client向Daemon发送启动app1指令。

2)因为我们的Linux服务器只装有Docker软件包,根本没有app1相关软件或服务,Docker Daemon就发请求给Docker的官方仓库,在仓库中搜索app1。

3)如果找到app1这个应用,就把它下载到我们的服务器上。

4)Docker Daemon启动app1这个应用。

5)把启动app1应用是否成功的结果返回给Docker Client。

Docker的其他操作,比如停止或删除Docker应用和启动的流程差不多,这里就不再一一介绍了。

1.2.2 Docker化应用的存在形式

我们知道,经过20多年的发展,Linux下应用软件已经不计其数,不但种类繁多,而且安装部署方式也千奇百怪、不一而足,如有些软件依赖特定操作系统、有些依赖特定内核版本、有些依赖一些第三方软件和共享库等。另外,不同操作系统,不同的系统版本软件的配置和启动方式也存在很大差异。

既然软件安装部署方式没有一个统一的标准,那么Docker的官方仓库该如何做呢?总不能针对每个软件,写一个安装说明书吧。

换个角度想一下,用户的需求是什么——把软件运行起来,至于怎么安装软件、软件运行在什么操作系统下用户不太关心。那么,就把软件和它依赖的环境(包括操作系统和共享库等)、依赖的配置文件打包在一起,以虚拟机的形式放到官方仓库,供大家使用。只要有虚拟机的运行环境,就可以不做任何修改把软件轻松地运行起来。这种方式甚至不需要大家重复安装和配置软件,只要有一个人把软件安装和配置好,提交到官方仓库,其他人下载后就可以直接以虚拟机的形式运行起来。我们以这种方式解决了软件安装部署方式没有一个统一标准的问题,如图1-4所示。

图1-4 应用的组织形式(一)

但这种软件部署方式却存在很多问题,一般一个软件包大小也就几兆到几十兆不等,但一个操作系统却有好几个G。如果每个软件都带上它依赖的操作系统,那么每个软件都有几个G,不要说运行,仅仅下载1个软件都要数小时,是不是有“捡了芝麻丢了西瓜”的感觉?

Docker为了解决这个问题,引入分层的概念。把一个应用分为任意多个层,比如操作系统是第一层,依赖的库和第三方软件是第二层,应用的软件包和配置文件是第三层。如果两个应用有相同的底层,就可以共享这些层。

以图1-4为例,假如应用A和应用B操作系统版本是一样的,它们就可以使用共享这一层,安装应用A时需要下载操作系统层,安装B应用就不用下载操作系统层,只需要下载它的依赖包和自身的软件包。因为主流的操作系统也就那么几个,最差情况下,也就把常用的操作系统都安装一遍,然后,包含操作系统的软件包就和传统的软件包一样大小了,如图1-5所示。

图1-5 应用的组织形式(二)

但这种共享层存在冲突问题,比如,应用A需要修改操作系统的某个配置,应用B不需要修改。如何解决这个冲突呢?我们规定层次是有优先级的,上层和下层有相同的文件和配置时,上层覆盖下层,数据以上层的数据为准。我们给每个应用一个优先级最高的空白层,如果需要修改下层的文件,就把这个文件拷贝到这个优先级最高的空白层进行修改,保证下层的文件不做任何改变。这样,从应用A的角度来看,文件已经修改成功了,而从应用B的角度来看,文件没发生任何改变,如图1-6所示。

图1-6 应用的组织形式(三)

Docker的分层和写时拷贝策略,解决了包含操作系统的应用程序比较大的问题。但我们知道,主流的虚拟机(KVM、Xen、VMWare、VirtualBox等)一般比较笨重,除了虚拟机本身运行要消耗大量的系统资源(CPU、内存等)外,启动一个虚拟机也需要花费数分钟,如何把虚拟机做到轻量化呢?

以OpenVZ、VServer、LXC为代表的容器类虚拟机,是一种内核虚拟化技术,与宿主机运行在相同Linux内核,不需要指令级模拟,性能消耗非常小,是非常轻量级的虚拟化容器,虚拟容器的系统资源消耗和一个普通的进程差不多。Docker就是使用LXC(后来又推出libcontainer)让虚拟机变得轻量化。

在Docker的官方仓库里,只需它有完整的文件系统和程序包,没有动态生成新文件的需求;当把它下载到宿主机上运行对外提供服务时,有可能修改文件(比如输出新日志到日志文件中),需要有空白层用于写时拷贝。Docker把这两种不同状态做了区分,分别叫作镜像(image)和容器(container),如图1-7所示。

图1-7 应用的组织形式(四)

在仓库中的应用都是以镜像的形式存在的,把镜像从Docker仓库中下拉到本机,以这个镜像为模板启动应用,就叫容器。

综上所述,镜像指的是以分层的、可以被LXC/libcontainer理解的文件存储格式。Docker的应用都是以这种格式发布到Docker仓库中,供大家使用。把应用镜像从Docker仓库下载到本地机器上,以镜像为模板,在一个容器类虚拟机中把这个应用启动,这个虚拟机叫作容器。

在Docker的世界里,镜像和容器是它的两大核心概念,几乎所有的指令和文档都是围绕这两个概念展开的。

1.2.3 Docker对变更的管理

对于软件开发来说,版本迭代、版本回退是常态,Docker对变更管理又有什么特别之处呢?

假若有一个应用的Docker镜像,它的V1.0版本有三层,每层文件的大小如图1-8所示。

图1-8 应用的分层结构和大小

接下来,我们需要对它做如下修改:

❑修改位于第一层的文件A。

❑删除位于第二层的文件B。

❑添加一个新文件C。

Docker会新增一个第四层,针对上面的修改需求,它处理方法如下:

❑把第一层的文件A拷贝到第四层,修改文件A的内容。

❑在第四层,把名称为B的文件设置为不存在。

❑在第四层,创建一个新文件C。

通过增加一个第四层,我们的版本变更为V1.1,如图1-9所示。

图1-9 应用两个版本变化

我们想把应用的V1.1版本发布到Docker仓库,供其他宿主机使用。Docker的仓库已经存在这个应用镜像的V1.0版本,也就存储有这个应用的第一层、第二层和第三层,我们上传V1.1版本时,不需要重复上传前三层,只需要把第四层(只有3M大小)上传到Docker仓库就可以了。

有一台远程服务器,正在运行这个应用的V1.0版,它想升级到V1.1版。因为它本机已经有这个应用的前三层,所以只需要从Docker仓库把第四层下载下来,就可以运行V1.1版,如图1-10所示。

图1-10 应用版本变更流程

综上所述,Docker不仅具有版本控制功能,并且还能够利用分层特性做到增量更新。

1.3 为什么使用Docker

当深入了解Docker后,你想在公司或部门推广Docker,就需要给大家讲明白为什么要使用Docker。

当讲Docker是什么时,你的听众是一批Docker爱好者,Docker的原理和实现细节讲得越深入、越具体,就越受大家的欢迎;当讲为什么要使用Docker时,你的听众有领导,有开发、测试和运维人员,而他们可能从来没听过Docker,或者对Docker知之甚少,他们更关注Docker能带来什么价值,引入Docker需要对现有的系统或程序做多大改造,Docker是不是足够的稳定,学习和使用Docker的成本有多高等问题。

1.3.1 从代码管理说起

现在的软件项目,失败的原因主要是结构设计不合理、编程人员水平不高、代码bug太多等因素。不管项目有多大,参与的成员有多多,代码修改多么频繁,很少是由管理上混乱导致项目失败的。究其原因,主要是我们有非常优秀的代码管理工具——Git和SVN。它们有版本控制和中心仓库这两大核心功能,能保证大家不用担心下列问题:

❑快速把代码分享给别人。

❑多人同时修改一个文件,导致代码不一致。

❑分不清每个人在什么时间都提交过什么代码。

❑代码被误删误改,不知道哪些文件被删被改,也恢复不到以前的状态。

❑变更和新分支太多,混乱到无法维护的地步。

版本控制功能不仅能清晰记录每个开发者在什么时间交过什么代码,还能让大家在各个版本间自由地切换、融合。大家如果要对齐开发环境,不需要逐一列出文件的名字、文件的内容,只需要简单地说下版本号,就能保证大家的文件完全一致。

中心仓库可以保证多人协作有一个统一的平台,既可以与别人分享代码和开发进度,也可以对每个人的代码做异机备份,防止因为机器故障而导致数据丢失。

软件部署和代码管理面临很多相似的问题:

❑如何快速把一台机器的应用环境分享给其他机器使用,用于扩容和故障时服务转移。

❑同一个功能模块的多台机器,软件版本和配置文件镜像出现不一致,很难被发现。

❑多人维护一套系统,很难清晰地记录下每个人都做过什么操作、每次变更都有哪些内容,以便在故障时快速回退。

❑有些配置文件或数据被误删了,恢复不回来,甚至很长一段时间都察觉不到。

❑随着操作系统版本、软件、硬件的更新迭代,系统维护的复杂度直线上升,混乱不堪。

既然代码管理做得这么好,那软件部署为什么不借鉴代码管理的方法呢?

代码管理的对象是纯文件(代码),体量小,有统一规范的文件编码方式,对平台无依赖。而软件部署管理的对象是一个环境,除了文件,还有二进制的软件和它依赖的运行环境(包括操作系统和依赖库)。由于操作系统和依赖库的体量很大,难以完全照搬代码管理的方式,用版本控制和中心仓库来解决软件部署的问题。

1.3.2 当前的优化策略

下面让我们看看,对于软件部署,目前主流的解决方案是什么样子的。

把环境分为两个部分:基础环境和应用环境。

❑基础环境:包含机器硬件、操作系统、提供基础服务的应用(如ssh、syslog等)。

❑应用环境:包含应用需要的各种软件包和配置文件(虽然软件包中也可以包含配置文件,但对于一些变更频繁和需要个性化配置的文件,最好还是独立出来)。

对这两种类型的环境,采取不同的策略:

❑基础环境统一化,尽量保持完全一致。比如,使用相同的硬件服务器,运行相同版本的操作系统和基础软件。

❑应用环境分解出一个个独立服务,每个服务再分解为包和配置文件,使用版本控制和中心仓库来对包与配置进行管理。

整体的解决方案如图1-11所示。

图1-11 Docker整体的解决方案

在这个解决方案中,我们把软件包和配置都版本化,每次变更都提交一个版本到中心仓库,然后再下发到各台机器上。由于每台机器的基础环境都保持一致,所以软件部署时可以不关注底层操作系统的适配问题,在一台机器打包编译的应用可以快速迁移到其他机器。

这个解决方案,在基础环境一致的前提下,实现了分解版的版本控制和中心仓库,针对分解出来的软件安装包和配置文件,可以通过版本来管理,并且可以把包和配置提交到中心仓库,让所有其他机器分享。

这个方案,存在以下两个问题:

❑基础环境难以改动:因为上层应用环境的软件和配置文件都是基于基础环境编译与配置的,一旦基础环境发生改变,可能导致上层的软件包和配置不能正常工作。

❑应用环境的维护成本取决于包和配置的数量:由于应用环境不是一个统一整体,而是分解出一个个包和配置,随着包和配置的增多,维护的复杂性也随之增加。

1.3.3 Github版的应用部署解决方案

Github是最流行、最优秀的代码管理平台,Docker借鉴了Github的管理思路,打造了一个Github版的应用部署的管理方案。

如果使用Docker来管理部署应用,解决方案如图1-12所示。

图1-12 使用Docker来制作和下发镜像流程图

和上一节的方案做下对比:

❑基础环境灵活,对硬件和操作系统都没有限制,只需要在每台机器上安装Docker Engine,用于运行Docker应用。

❑应用环境也不再分解为一个个包和配置文件,而是作为一个有机的整体,这个有机的整体包含应用需要的所有软件包、配置文件和它依赖的运行环境(操作系统和依赖库),带有版本控制功能,也可以提交到中心仓库供大家共享。由于Docker的镜像中包含应用运行需要的所有包、配置和系统环境,下发镜像后不需要做任何安装配置,应用就可以直接运行,不会随着应用中包和配置数量的增加,导致安装部署变得复杂。

Docker方案完美地把代码管理中的版本控制和中心仓库概念移植到应用部署领域,让大家顿时从应用部署烦琐、重复的工作中解脱出来,可以像使用Github管理代码那样优雅地管理应用的部署工作。

Docker这个方案能顺利实施,一个关键点是通过分层共享和增量变更技术把应用的运行环境(包括操作系统在内)这么一个庞大的体量顺利瘦身,让应用运行环境的安装和修改在大多数情况下与只装软件包一样轻量、简单。分层共享和增量变更技术在1.2.2节有过详细讲解,这里就不再复述了。

1.3.4 Docker应用场景

下面描述两个典型的Docker应用场景,如图1-13所示。

图1-13 Docker两个应用场景

在这两个应用场景中,有开发、测试(QA)和运维人员,分别维护开发、测试和生产环境的服务,他们有一个私有的Docker仓库,存储着各种各样的Docker化的应用镜像。

场景一

现在有一个需求,需要对应用App1做修改、测试和发布新变更到生产环境,工作步骤如下:

1)开发者先从私有仓库找到App1这个应用最新稳定版本,假设为V1.0版,把这个App1:v1.0下载到开发机,修改,并提交新版本V1.1到私有仓库,并告诉测试人员测试。

2)测试人员下载开发者刚提交的新版本App1:v1.1,测试,并把测试结果反馈给开发者。

3)如果测试失败,开发继续修改,提交新版本给测试人员做新一轮的测试;如果测试成功,开发把要发布的应用的名称和版本号提供给运维同事。

4)运维人员根据开发提供的应用名和版本号,把相关镜像从私有仓库下拉到各个生产环境的机器上,停掉旧版本的Docker容器,启动新版本的Docker容器,完成发布。生产环境的机器上可以同时缓存应用的多个版本镜像,如果新发布的版本有问题,可以快速切换回原来的版本。

因为Docker的应用镜像包含应用运行需要的所有软件包、配置和操作系统,所以开发者打包好Docker镜像,测试和运维人员从私有仓库下拉,不需要做任何修改,就可以运行起来,并保证和开发者运行的环境完全一致。“一处编译,到处运行”,真的很方便。

场景二

有一个应用App2,目前在生产环境正常运行,但由于系统比较老、缺乏维护、开发和运维经历过更替且交接文档不全,现在谁都不知道该如何部署App2这个应用到新服务器上。

如果App2是Docker化的应用,它可以直接把运行的环境转换为一个带版本号的Docker镜像(比如App2:v1.0),提交到私有仓库,供开发者修改或运维人员发布新机器。

1.3.5 Docker可以解决哪些痛点

1.开发人员

不管在大公司还是小公司,开发人员经常被如下问题困扰:

❑为了节约成本,一台开发机多人使用,管理混乱,相互干扰。

❑一个开发往往只用一套开发环境,同时有多个开发任务时,不得不反复修改开发环境,以适应不同的开发任务。

❑多个开发人员希望保持相同的开发环境开发同一个项目,但开发环境难以复制,即便大家起初的开发环境一样,随着项目的滚动、开发环境的不停更新,很难保证每个人的开发环境都同步更新。

❑开发机硬件故障,需要更换新机器,重新搭建开发环境是件头疼的事。如果硬件故障,重要数据没备份,那就更让人崩溃。

❑打算调研下新软件,安装配置文档复杂,仅仅把软件安装、运行起来就要花费大半天时间。

上面问题带来的工作量不能体现开发人员的核心价值,但是是不得不面对的。

如果使用Docker,可以轻松解决上述问题:

❑Docker化的应用使用容器虚拟化技术,每个应用都运行在独立的虚拟化环境中,天然具有隔离性,不用担心一机多用造成的管理混乱。

❑开发人员在多任务开发时,可以并行启动这些应用的Docker容器,每一个Docker应用有一个独立的运行环境,互不干扰。

❑开发机硬件故障,在新开发机上,重新从Docker仓库下拉开发环境的镜像,一两分钟内就可以重新搭建一套开发环境,并且即便新旧开发机的硬件和操作系统不一致,重新搭建的开发环境仍能保持和原来的环境一模一样。另外,还可以通过Docker仓库,把重要变更及时备份到远端。

❑Docker的每个复杂软件都可以制作成Docker镜像,分享给大家使用。随着Docker的流行,几乎所有主流的软件都提供Docker化的部署方式。软件部署将成为再简单不过的事情。

2.测试人员

测试人员经常费了九牛二虎之力测出一些bug,和开发逐一核对,发现大多数bug都是开发和测试环境不一致造成的。

测试人员经常为配置不同的测试环境浪费大量的时间,还是不能保证和开发环境完全保持一致,开发人员虽然很认真负责地告诉测试人员如何配置测试环境,但还是经常性地遗漏一些配置。

使用Docker,不需要做任何配置,就能保证开发和测试环境完全一致,测试人员只需要关注测试本身就可以了。

3.运维人员

运维人员大部分时间都浪费在装软件、修改配置上,重复单调,经常半夜还要起来做紧急扩容、故障机服务迁移。如果使用了Docker,好处显而易见:

❑服务具备快速部署能力,扩缩容、版本回退在几秒钟内就可以完成。

❑基于同一个Docker镜像部署服务,可以保证每台机器应用完全一致。

❑由于Docker化应用是虚拟化,多个应用可以混合部署在一台机器上,互不干扰,可以提高机器使用率。

❑Docker化的应用可以运行在不同的硬件和操作系统平台下,在不同的环境自由迁移。

❑通过Dockerfiel管理Docker镜像,即使系统多次易手、交接文档不全,运维人员也可以快速了解系统是如何搭建的。

❑Docker倡导“Build once, Run anywhere”,再烦琐的活儿,只需要做一次,制作成Docker镜像,在任何环境下都可以运行;还可以基于这个Docker镜像做修改,制作新的镜像。

上面只罗列几条好处,运维在使用Docker的过程中,还会发现很多意想不到的好处。一句话,Docker可以让运维工作变得简单和易于维护。

1.3.6 Docker的使用成本

坦白说,Docker是有学习和使用成本的。

Docker虽然已经做得足够简单易用,但由于它的定位是虚拟化容器,是一个单机版的应用,如果要基于Docker构建集群或PaaS管理系统,如Web管理界面、任务调度策略、监控报警等,则还需要自己开发或从开源社区寻求支持。

另外,传统的运维是以机器为中心,而Docker是以应用为中心,它会颠覆我们一些固有的运维习惯和运维方式。

但好在Docker有非常活跃的社区,不但有大量的开发者为Docker贡献代码、修复bug,让Docker越来越好用、越来越稳定;还有大量的学习资料和问题解答。另外,很多公司和个人也为Docker提供大量优秀的第三方开源软件,如kubernetes、fig、etcd和cadvisor等。这些都有助于进一步降低Docker学习和使用的门槛。

如果你被上面的理由说服了,那就赶快跟着本书,学习和使用Docker吧!

1.4 本章小结

本章概括性地介绍了Docker是什么,它有哪些独特的物理特性,以及它适用于哪些场合和能带来哪些好处。它灵活便利,但也有一定的学习成本。下面章节中我们将循序渐进,讲解Docker的使用方法。