docker——从隐喻说起
本文转自:http://metaphor.space/2016/01/07/docker-a-metaphor/
docker的学术化定义
首先来看维基百科的定义:
Docker is an open-source project that automates the deployment of applications inside software containers, by providing an additional layer of abstraction and automation of operating-system-level virtualization on Linux. Docker uses the resource isolation features of the Linux kernel such as cgroups and kernel namespaces, and a union-capable filesystem such as aufs and others to allow independent “containers” to run within a single Linux instance, avoiding the overhead of starting and maintaining virtual machines.
Docker是一个开放源代码软件项目,让应用程序布署在软件容器下的工作可以自动化进行,借此在Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。Docker利用Linux核心中的资源分离机制,例如cgroups,以及Linux核心命名空间(name space),来建立独立的软件容器(containers)。这可以在单一Linux实体下运作,避免启动一个虚拟机器造成的额外负担。
通过这个比较学术化的定义,可以模糊的知道docker是可以实现自动化部署之类功能的工具,但是并不能清楚的理解它到底牛逼在什么地方,又好在哪里。为了搞明白这些,只看定义是远远不够的,一种手段是可以在长期使用docker的过程中,逐渐体会到docker的优势。但是对于一个从未接触过docker,甚至对部署都不是很了解的新人,应该怎么快速理解这一点呢?一个比较好的办法,就是借助于隐喻。
Docker的隐喻
关于「隐喻」的概念,有一篇文章单独介绍:「隐喻」
同样,要快速理解docker,抓出核心思想,隐喻是非常重要的
字面意思:docker——码头工人。
docker的名称本身就带有隐喻的性质:在全球物流系统中,一个非常重要的发明就是集装箱。
集装箱重要在哪里?为了理解这件事情,可以先考察一下集装箱出现之前的物流情况:货物生产出来之后,装箱,然后一箱一箱的搬到卡车上,然后再一箱一箱卸下来,送上火车,运送到码头附近的火车站,再一箱一箱卸下来,装上卡车,拉到货轮上,一箱一箱的装上去….
可以发现,大量的时间,人力 ,物力全部浪费在了中间的装卸上,在物流里面,货物真正在路上的时间是一定的,在交通技术得到改善之前,也很难去缩短。于是货物的装卸这部分时间,就成了物流中的瓶颈,而这个局面在集装箱出现之后,得到了很大的改善。
集装箱重要在它提供了一种通用的封装货物的标准规格(说白了指的就是它的尺寸,外形是符合一定标准的),这样就产生了一个巨大的优点:在物流运输中,只需要在运输前一次封装,集装箱就可以放上火车,卡车,拉到码头,直接放在货船上,卸船之后直接再放上火车,卡车,运送到目的地。而且,集装箱本身的标准,使得它非常容易机械化操作,这也引发了以集装箱为中心的整个全球物流的标准化进程,从而节省了大量的时间资源和人力资源,促进了全球资源的流动与重新配置。
docker(码头工人)正是借用了集装箱的隐喻
docker就像往集装箱里装货物的码头工人那样,它把应用打包成具有某种标准规格的集装箱,用计算机领域的语言来说,这种按照一定规格封装的集装箱叫「镜像」。其实就是将你原来的代码添加点额外的内容,格式啥的,整出来的一个静态的应用。而且就像光盘镜像只能由特定的解析光盘镜像格式的软件来解析一样,这个镜像也只能由docker来解析。
集装箱减少了货物的运输工作量,那docker镜像又有什么相似的优势呢?首先可以看看docker出现之前的应用部署是啥样的。
在docker出现之前,
比如说我要部署一个django应用,要做哪些事情:
首先我得有个python环境,比如我用的是python3,而你机器上是python2,那ok,先装个python3吧,一看装起来还挺麻烦,要先装各种依赖,还要解决一些可能的冲突,没办法硬着头皮上吧。
装完python之后,因为有pip这些神奇的工具,很快就装完django,需要的python库了。咦,发现还要装mysql,还用了redis。没办法,继续上吧,下载,安装,配置。费了九牛二虎之力终于搞完了。一天就这么过去了。
啥?你告诉我原来的服务器不用了,要换一台服务器?我靠,那重新来一遍吧,有了昨天的经验,只用了大半天就搞定了。
啥?你说咱们的应用做的太好,要进行推广,需要指导它们部署?我选择狗带,删代码走人。
上面的描述可能有些夸张,但也绝不是罕有发生。在docker出现之前,这些正是运维人员很多时候都在做的事情,在不断的重复工作上,浪费了巨大的人力物力。
docker出现之后
标准的交付件
说docker就像集装箱那样,关键作用就是「标准化」,它的具体产物是什么呢,其实就是「镜像」。这个词说实话太玄乎,当然原文image本身也挺玄乎的,玄乎的原因是它根据一些特殊的场景引申了本来的含义。它本身是「画像,映像」的意思,画像又有「现实物体的抽象描绘」等一层意思,而且画像本身可以很容易的复制,后来又有了「原画像复制品」一类的意思,再后来直接就拿来表光盘镜像(很容易复制的存储影像的东西,只不过画像是画在纸上,但这种影像以数字形式存在于光盘上)。当然「镜像」在汉语中就有「复制品」的含义,只不过加入了汉字独有的意境,显得玄乎,朦胧了。
当然image后来又被引申了,在docker中,它指的就是,把你的应用本身,按照一定的格式封装(其实就是一些按规则执行的命令行)成一种具有某种标准规格的东西(就像集装箱把你的货物封装起来类似)。就像光盘镜像那样把原影像格式给封装成能直接放在刻成光盘的格式一样。这种格式只能由docker本身解析,就像你的光盘镜像也只能使用特定的工具解析类似。
在docker中,镜像是无法直接运行的,我猜想这并不是技术上的原因,而是工程设计上的原因。因为一般来说,一个软件的某个具体版本只会打包成一个镜像。如果镜像可以配置,运行的话,在使用过程中很可能会对镜像造成破坏。
那怎么样避免这个问题呢,就是再给他加一层,也就是相当于多了个分身术,只要本尊没问题,分身怎么扑街都不会真正的跪掉。多加的这一层分身,就叫容器(container),这个名字也挺形象,它就像个瓶子这样的容器一样,你的应用在里面运行,而且多了一层安全机制。你想使用服务或把你的应用跑起来的话,只需要使用镜像新创建一个容器就可以了(也是一条命令搞定),而镜像还放在那里不动,没办法,金贵嘛。
笼统地说,镜像分为两种:
一种简单的称之为基础独立镜像吧。它一般是一个独立的服务,最独立的服务镜像,莫过于各种精简的操作系统了。它们被封装在镜像中,作为最基础的服务,基本上所有镜像都离不开它。但是对于我们这些普通应用开发者来说,一般不会直接使用这种镜像。
如果要使用这种镜像,我们的Dockerfile一般类似于下面这样:
1 2 3 4 5 6 7 | # 基础的操作系统 From ubuntu:latest # 构建其他需要的其他服务,以及我们自己的代码 RUN apt-get install mysql COPY . /src/ |
另一种,姑且称之为组合镜像。它建立在一个独立镜像的基础上,在上面组合了其他服务,比如python服务,或是mysql之类的服务。作为比较顶层的应用开发者,我们一般会直接使用这种组合好后的服务镜像。
Dockerfile则类似于这样:
1 2 3 4 5 6 7 | # 基础python服务 From python:2.7.10 # 我们自己的代码 RUN apt-get install mysql COPY . /src/ |
其实像上面的python服务,其本身也是建立在一个基础的操作系统之上的。如果我们查看dockerhub上的python的Dockerfile,就可以明确这一点:
1 2 3 4 | FROM buildpack-deps:jessie # jessie就是一个精简的debian操作系统 # remove several traces of debian python RUN apt-get purge -y python.* |
而我们自己构建的镜像,也可以称之为一种组合镜像。我们一般使用Dockerfile将自己的应用代码,加上上面的某些具体的服务镜像(比如python)再组合起来,就可以构建我们自己的应用了。
Docker 究竟做了什么简化?
docker正是在部署过程中,将上面那些重复的部分,由docker自动化完成。只需要在第一次部署时,构建完可用的docker镜像。然后在以后使用的过程中,短短的几行命令,就可以直接拉取镜像,根据这个镜像创建出一个容器,把服务跑起来了。所需要的仅仅是安装了docker的服务器,一个Dockerfile文件,以及比较流畅的网络而已。真可谓,『一次构建,到处部署』。
需要python3环境?直接 from python:3.x 搞定。
需要迁移服务器? 直接把应用连带Dockerfile,volumes数据拷贝到新服务器上,几条命令又搞定
需要作为服务给别人使用?Dockerfile即是最清晰的部署文档,维护一个官方镜像即可,谁需要就直接拉下来几条命令部署上就行了。
到这个地方,你可能已经发现了,docker镜像成为了一种像集装箱那样的标准货件。它不像传统的软件交付方式那样,只把代码以及说明文档之类的给你就完了,而是直接给你一个标准docker货件,它可能 Dockerfile,或者直接就是镜像,这个标准件不仅包括了代码本身,还包括了代码运行的OS等各种整体环境。
于是,谁想用我的服务,直接拉取镜像,实例化一个容器就可以了,能直接提供你所要的服务,不再像之前那样有繁复的安装过程————这些都有人给你做过了。
当然优点不止于此
就像集装箱带来的物流领域的「标准化」,不只是单纯影响集装箱这个范围本身,而是整个领域的运输器械,物流管理等整个领域的标准化和效率的提高。
也就是说基于一件核心事物「标准化」,可以做更多的事情,比如集装箱的机械自动搬运等。docker也是类似的,一旦这种软件标准件建立起来之后,就可以基于这种标准件和相应的管理方式带来更多的改变。比如
统一的管理服务
使用docker部署的应用,都会在docker的管理范围之内。这也是docker的另一个非常大的优点,它提供了一种隔离的空间,把服务器上的零散的部署应用集中起来进行管理。
举个例子,比如我一个服务器上部署了n多服务,有mysql,redis,rabbitmq,其他还有一堆应用。有一天我服务器突然断电重启了,准备茶几吧。那些没有设置自动重启的应用,那些重启出问题的应用,那些你甚至都不知道隐藏在某个角落里的重要应用没启动成功….
然而使用docker,一眼就可以看出那些应用正常启动了,那些应用又出问题了。接下来只用有条不紊的处理就ok了。
持续交付上的应用
持续交付有一些超出范围,自己去寻找答案吧
关于docker的一个误解
有人可能会问,我只有一台服务器,也不太可能会迁移。我的python服务,mysql服务,只需要部署一次,就可以在以后重复使用了。那这样docker对于我来说还有优势吗?毕竟docker也是有学习成本的。
如果你真的确信你的应用都是一次性的,而且只提供给自己使用,那么docker在这种场景下的优势不是特别明显:即便是docker,最初的构建也是需要有人做的,也是有工作量的。这和你一次性部署的工作量差不多。但是即便如此也是又有点的,它提供了一个干净,隔离的环境。
另外,很多人说docker改变了运维世界,这句话是从群体角度来说的,像mysql,python这样的使用群体众多的应用,以及会经常迁移,变动,重新部署的应用,docker化之后,整个群体所节省的时间,是非常巨大的。
也就是说,docker并不能部署的工作「减少为0」,比较好的情况下是「基本减少为1」,因为无论如何都需要把应用及所在环境构建一遍,就像集装箱虽然减少了运输途中的搬运过程,但是把货物搬进集装箱这一次操作是难以避免的,但这可以成为「一次构建,到处交付」的基础。你第一次构建自己的镜像时,就像往集装箱里装东西是类似的,需要和直接在机器上部署差不多的工作量。
这个时候,docker的效率提升是很小的。
但是,你真的确信,你所做的工作只是一次性的吗?