一、什么是Docker:
Docker是一种Linux容器技术,一种高效、敏捷、和轻量级的容器解决方案,并且支持在多种主流平台(PaaS)和本地部署。Docker是基于Go语言实现的云开源项目,诞生于2013年,最初发起者是DotCloud公司,后来改名为Docker Inc,之后专注于Docker相关技术和产品的开发。Docker项目目前已经加入了Linux基金会,遵循Apache2.0开源协议,全部开源代码均在https://github.com/docker上进行相关维护,官网地址为https://www.docker.com/,有相关的文档可以参考,现在docker与openstack同为最受欢迎的云计算开源项目。
Docker的Logo设计为蓝色鲸鱼,拖着许多集装箱,Docker的构想思想是要实现“Build ,Ship and Run Any App, Anywhere”,即通过对应用的封装(Packaging),分发(Distribution)、部署(Deployment)、运行(Runtime)生命周期进行管理,达到应用组件“一次封装,到处运行”的目的。这里的应用组件,既可以是一个Web应用,一个编译环境,也可以是一套数据库平台服务,甚至是一个操作系统或集群。
基于Linux平台上的多项开源技术,Docker提供了高效,敏捷和轻量级的容器方案,并支持部署到本地环境和多种主流云平台。可以说,Docker首次为应用的开发、运行和部署提供了“一站式”的使用解决方案。
跟大部分新新兴技术的诞生一样,Docker也并非“从石头分离蹦出来的”,而是站在前人的肩膀上,其中最终要的就是Linux容器(Linux Containers,LXC)技术。
操作系统级虚拟化的历史:
1982年:你一定会很惊讶,第一个操作系统级的虚拟化技术是什么。答案就是chroot,知道现在依然在使用的一个系统调用。这个系统调用会改变运行进程的工作目录,并且只能爱这个目录里面工作。这种操作其实就是一种文件系统层的隔离。
2000年:FreeBSD jail,真正意义上的第一个功能完整的操作系统级虚拟化技术,所以,真正的容器技术出现到现在已经过去16年,并不是几年的时间。
2005年:OpenVZ,这是Linux平台上的容器化技术实现,同时也是LXC,即Docker最初使用的容器核心实现。
2008年:LXC发布,这是docker最初使用的具体内核功能实现。
2013年:Docker发布,可以看出,docker最初是使用了LXC,同时封装了其他的一些功能。docker的成功,与其说是技术的创新,还不如说是一次组合式的创新。
总结:iPhone你要说有很多创新,真的说不上,手机很早就有了,电脑很早就有了, 触摸屏很早就有了,但是苹果将所有这些有机的组合到一起,在提供机制的用户体验,就产生跨时代的产品,同样Docker所使用的技术也都不是新技术,它将一系列邮寄的组合到一起,并提供了极致的用户体验,就产生了跨时代意义的产品。
二、为何要使用Docker:
1、Docker容器虚拟化的好处
Docker项目的发起人和Docker公司CTO Solomon Hykes曾认为,Docker在正确的地点、正确的时间顺应了正确的趋势——如何正确的构件应用。
在云时代,开发者创建的应用必须要能很方便地在网络上传播,也就是说应用必须脱离底层物理硬件的限制;同时必须是“任何时间、任何地点”可获取的。因此,开发者需要一种新型的创建分布式应用程序的方式,快速分发和部署,这正是Docker所能够提供的最大优势。
举个简单的例子,假设用户试图基于最常见的LNMP(Linux+Apache+MySQL+PHP)组合来构建一个网站。按照传统做法,首先,需要安装Apache、MySQL和PHP及他们各自运行所依赖的环境;之后分别对他们进行配置(包括创建合适的用户、配置参数等);经过大量的操作后,还需要进行功能测试,看是否工作正常;如果不正常,则进行调试和追踪,意味着更多的时间代价和不可控的风险。可以想象,如果应用数目变多,事情会变得更加难以处置。
更可怕的是,一旦需要服务器迁移(例如从亚马逊云迁移到其它云),往往需要面对每个应用都进行重新部署和调试。这些琐碎而无趣的“体力活”,极大降低了工作效率。究其根源,是这些应用直接运行在底层操作系统上,无法保证同一份应用在不同环境中行为一致。
而Docker提供了一个中更为聪明的方式,通过容器来打包应用,解耦应用和运行平台。意味着迁移的时候,只需要在新的服务器上启动需要的容器就可以了,无论新旧服务器是否是同一类型平台。这无疑将节约大量的包裹事件,并降低部署过程出现问题的风险。
2、Docker在开发和运维中的优势
对开发和运维(DevOps)人员来说,可能最梦寐以求的效果就是一次创建或配置,之后可以在任何地方、任意时间让应用正常运行。而Docker恰恰是可以实现这一种既目标的“瑞士军刀”。
具体来说,Docker在开发和运维过程中,具有如下几个方面的优势:
更快速的交付和部署:
使用Docker,开发人员可以使用镜像来快速构建一套标准的开发环境;开发完成之后,测试和运维人员可以直接使用完全相同的环境来部署代码,只要开发测试过的代码,就可以确保在生产环境无缝运行。Docker可以快速创建和删除容器,实现快速迭代,大量节约开发、测试、部署的时间。并且,整个过程全程可见,使团队更容易理解应用的创建和工作过程。
更高效的资源利用:
Docker容器的运行不需要额外的虚拟化管理程序(Virtual MachineManager,VMM,以及Hypervisor)支持,他是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低。根传统虚拟机方式相比,要提高一道两个数量级。
更轻松的迁移和扩展:
Docker容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等,同时支持主流的操作系统发型版本。这种兼容性让用户可以在不同平台之间轻松滴迁移应用。
更简单的更新管理:
使用Dockerfile,只需要小小的配置修改,就可以代替以往大量的更新工作,并且所有修改都以增量的方式被分发和更新,从而实现自动化并且高效的容器管理。
3、Docker与虚拟机比较
作为一种轻量级的虚拟化方式,Docker在运行应用上与传统的虚拟机方式相比具有显著优势:
Docker容器很快,启动和停止可以在秒级实现,而传统的虚拟机方式需要数分钟。
Docker容器对系统资源需求很少,一台主机上可以同时运行数千个Docker容器(在IBM服务器上已经实现了同时运行10K量级的容器实例)。
Docker通过类似Git设计理念的操作来方便用户获取、分发和更新应用镜像,存储服用,增量更新。
Docker通过Dockerfile支持灵活的自动化创建和部署机制,提高工作效率,使流程标准化。
Docker容器除了运行其中应用外,基本不消耗额外的系统资源,保证应用性能的同事,尽量减小系统开销。传统虚拟机方式运行N个不同的应用就要启用N个虚拟机(每个虚拟机需要单独分配独占的内存、磁盘等资源),而Docker只需要启动N个隔离的“很薄的”容器,并将应用放进容器内即可。应用获得的是接近原生的运行性能。
当然,在隔离性方面,传统的虚拟机方式提供的是相对封闭的隔离,但这并不意味着Docker就不安全,Docker利用Linux系统上的多种防护技术实现了严格的隔离可靠性,并且可以整合众多安全工具。从1.3.0版本开始,Docker重点改善了容器的安全控制和镜像的安全机制,极大提高了使用Docker的安全性。在以往的大规模应用中,目前尚未出现值得担忧的安全隐患。
Docker容器技术与传统虚拟机技术的特性比较
4、Docker与虚拟化
虚拟化(Virtualization)技术是一个通用的概念,在不同领域有不用的理解。在计算领域,一般指的是计算虚拟化(Computing Virtualzation),或通常说的服务器虚拟化。维基百科上的第一如下:“虚拟化是一种资源管理技术,是将计算机的各种试题资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来的,打破实体结构件的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。”
可见,虚拟化的核心是对资源的抽象,目标旺旺是为了在同一个主机上同时运行多个系统或应用,从而提高系统资源的利用率,并且带来降低成本、方便管理和容错容灾等好处。
从大类上分,虚拟化技术可分为基于硬件的虚拟化和基于软件的虚拟化,基于软件的虚拟化从对象所在的层次,又可以分为应用虚拟化和平台虚拟化(通常说的虚拟化技术即属于这个范畴)。其中,前者一般指的是一些模拟设备或诸如Wine这样的软件。后者又可以细分为如下几个子类:
完全虚拟化:虚拟机模拟完整的底层硬件环境和特权指令的执行过程,客户操作系统无需进行修改。例如IBM p和z系列的虚拟化、VMware Workstation、VirtualBox、QEMU等。
硬件辅助虚拟化:利用硬件(主要是CPU)辅助支持(目前x86体系结构上可用的硬件辅助虚拟化技术包括Intel-VT和AMD-V)处理敏感指令来实现完全虚拟化的功能,客户操作系统无需修改,例如VMware Workstation、Xen、KVM。
部分虚拟化:只针对部分硬件资源进行虚拟化,客户操作系统需要进行修改。现在有些虚拟化技术的早期版本仅支持部分虚拟化。
准虚拟化(paravirtualization):部分硬件接口以软件的形式提供给客户机操作系统,客户啊哦做系统需要进行修改。例如早期的Xen。
操作系统级虚拟化:内核通过创建多个虚拟的操作系统实例(内核和库)来隔离不同的进程。容器相关技术即在这个范畴。课件,Docker以及其他容器技术,都属于操作系统虚拟化这个范畴,操作系统虚拟化最大的特点就是不需要额外的supervisor支持。
Docker虚拟化方式之所以有众多优势,这与操作系统虚拟化自身的设计和实现是分不开的。
传统方式是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层。Docker容器是在操作系统层面上实现虚拟化。直接复用本地主机的操作系统,因此更加轻量级。
5、Docker的体系结构
Docker使用C/S架构,Docker daemon作为server端接受client的请求,并处理(创建、运行、分发容器),他们可以运行在一个机器上,也通过socket或者RESTful API通信。
注解:
socket: Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
RESTful API:表示性状态转移(representation state transfer)。简单来说,就是用URI表示资源,用HTTP方法(GET, POST, PUT, DELETE)表征对这些资源的操作。
Resource: 资源,即数据,存在互联网上的可被访问的实体
Representation: 数据的某种表现形式,如HTML, JSON。
State Transfer:状态变化,HTTP方法实现
RESTful API 就是REST风格的API。现在终端平台多样,移动、平板、PC等许多媒介向服务端发送请求后,如果不适用RESTful API,需要为每个平台的数据请求定义相应的返回格式,以适应前端显示。但是RESTful API 要求前端以一种预定义的语法格式发送请求,那么服务端就只需要定义一个统一的响应接口,不必像之前那样解析各色各式的请求。
Docker daemon 一般在宿主机后台运行。
Docker client以系统命令的形式存在,用户用docker命令来跟docker daemon交互。
Docker守护进程(Docker daemon)
如上图所示,Docker守护进程运行在一台主机上,用户并不直接和守护进程进行交互,而是通过Docker客户端间接和其通信。
Docker客户端(Docker client)
Docker客户端,实际上是docker的二进制程序,是用户与Docker交互方式。它接受用户指令并且与背后的Docker守护进程通信。
注解:docker CLI:docker CLI指的是docker常用的命令。
Docker内部:
要理解Docker内部构件,需要理解一下三大核心概念:
Docker 镜像 - Docker images
Docker 仓库 - Docker repository
Docker 容器 - Docker containers
只有理解了这三个核心概念,才能顺利滴理解Docker容器的整个生命周期。
Docker镜像:
Docker镜像是Docker容器运行时的制度模板,镜像可以用来创建Docker容器。每一个镜像有一系列的层(layers)组成。Docker使用UnionFS(联合文件系统)来将这些层联合到单独的镜像中。UnionFS允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker是如此的清凉。当你改变了一个Docker镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了,现在你不用重新发布整个镜像,只需要升级,层使得分发Docker镜像变得简单和快速。
例如:CentOS镜像中安装Nginx,就成了“Nginx镜像”,其实在此时Docker镜像的层级概念就体现出来了,底层概念就不难理解,此时我们一般CentOS操作系统镜像成为Nginx镜像层父镜像。
镜像是创建Docker容器的基础。通过版本管理和增量的文件系统,Docker提供了一套十分简单的机制来创建和更新现有的镜像,用户甚至可以从网上下载一个已经做好的应用镜像,并直接使用。
Docker仓库:
Docker仓库类似于代码仓库,它是Docker集中存放镜像文件的场所。
不要将Docker仓库和仓库注册服务器(Registry)混为一谈。实际上,仓库注册服务器是存放仓库的地方,其上往往存放着多个仓库。每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签(tag)来进行区分。例如存放Ubuntu操作系统镜像的仓库成称为Ubuntu仓库,其中可能包括14.04、12.04等不同版本的镜像。仓库注册服务器的示例如图所示。
根据所有存储的镜像公开分享与否,Docker仓库可以分为公开仓库(Public)和私有仓库(Private)两种形式。目前,最大的公开仓库是官方提供的Docker Hub,其中存放了数量庞大的镜像共用户下载。国内不少云服务器提供商(如时速云、阿里云等)也提供了仓库的本地源,可以提供稳定的国内访问。
当然,用户如果不希望公开分享自己的镜像文件,Docker也支持用户在本地网络内创建一个只能自己访问的私有仓库。当用户创建了自己的镜像之后就可以使用push命令将它上传到指定的公有或者私有仓库。这样用户下次再另一台机器上使用该镜像时,只需要将其从仓库上pull下来就可以了。
可以看出,Docker利用仓库管理镜像的设计理念与Git非常相似,实际上在理念设计上借鉴了Git的很多优秀思想。
Docker容器:
Docker利用容器来运行应用,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个Docker容器都是从Docker镜像创建的,是通过镜像创建的运行实例,Docker容器可以运行、开始、停止、移动、删除。每一个Docker容器都是独立和安全的应用平台,彼此相互隔离、互不可见。
可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
注解:镜像是只读的,容器在启动的时候创建一层可写层做为最上层。
与虚拟机相比,容器有一个很大的差异,它们被设计用来运行“单进程”,无法很好滴模拟一个完整的环境。Docker设计者极力推崇“一个容器一个进程的方式”如若果你要选择在一个容器中运行多个进程,那唯一情况是:处于调试目的。容器是设计来运行一个应用的,而非一台机器。你可能会把容器当虚拟机用,但你将失去很多灵活性,因为Docker提供了用于分离应用与数据的工具,使得你可以快捷地更新运行中的代码/系统,而不影响数据。
Docker从0.9版本开始使用libcontainer代替LXC,libcontainer和Linux系统的交互图如下:
Docker底层技术:
Namespaces用来隔离各个容器:
Docker底层的2个核心技术分别是Namespaces和Control groups
1)pid namespace
不同用户的进程就是通过pid namespace隔离开的,且不同namespace中可以有相同pid。所有的LXC进程docker中的父进程为docker进程,每个LXC进程具有不同的namespace。
2)net namespace
有了pid namespace,每个namespace中的pid能够相互隔离,但是网络端口还是共享host的端口,网络隔离是通过net namespace实现的,每个net namespace有独立的network drvices,IP addresses,IP routing tables,/proc/net目录。这样每个container的网络就能隔离开来。docker默认采用veth的方式将container中的虚拟网卡同host上的一个docker bridge:docker0连接在一起。
3)ipc namespace
containet中进程交互还是采用Linux常见的进程间交互方法(interprocess communication - IPC),包括常见的信号量、消息列队和共享内存。container的进程间交互实际上还是host上具有相同pid namespace中的进程间交互。
4)mnt namespace
类似chroot,将一个进程放到一个特定的目录执行,mnt namespace允许不同namespace的进程看到这个文件结构不同,这样每个namespace中的进程所看到的文件目录就被个离开了。在container里头,看到的文件系统,就是一个完整的Linux系统,有/etc、/lib等,通过chroot实现。
注解:CHROOT就是Change Root,也就是改变程序执行时所参考的根目录位置。CHROOT可以增进系统的安全性,限制使用者能做的事。
5)uts namespace
UTS(“UNIX Time-sharing System”)namespace允许每个container拥有独立的hostname和domain name,使其在网络上可以被视作一个独立的节点而非host上的一个进程。
6)user namespace
每个container可以有不同的user和group id,也就是说可以在container内部用container内部的用户执行程序而非host上的用户
有了以上6中namespace从进程、网络、IPC、文件系统、UTS和用户角度的隔离,一个container就可以对外展现出一个独立计算机功能,并且不同container从OS层面实现了隔离。然后不同namespace之间资源还是相互竞争的,仍然需要类似ulimit来管理每个container所能使用的资源——cgroup。
cgroups(Control group)实现了对资源的配额和度量:
cgroups(Control groups)最初叫Process Container,由Google工程师(Paul Manage和Rohit Seth)于2006年提出,后来因为Container有多重含义容易引起误解,就爱2007年更名为Control Groups,并被整合进Linux内核,顾名思义就是把进程放到一个组里面统一加以控制。官方定义如下:cgroups是Linux内核提供的一种机制,这种机制可以根据特定的行为,把一系列系统任务整合(或分隔)到按自愿划分等级的不同组内,从而为系统资源管理提供一个统一的框架。
通俗来说,cgroups可以限制、记录、隔离进程组所使用的的物理资源(包括CPU、Memory、IO等),为容器实现虚拟化提供了基本保证,是构建Docker等一系列虚拟化管理工具的基石。
实现cgroups的主要的是为不同用户层面的资源管理,提供一个统一化的接口。从单个进程的资源控制到操作系统层面的虚拟化。Cgroups提供了以下四大功能。
容器限制(Resource Limitation):cgroups可以对进程组使用的资源总额进行限制。如设定应用运行时使用内存的上线,一旦超过这个配额就发出OMM(Out of Memory)。
优先级分配(Prioritzation):通过分配的CPU时间片数量级银盘IO带宽大小,实际上就相当于控制了进程运行的优先级。
资源统计(Accounting):cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等等,这个功能非常使用于计费。
进程控制(Control):cgroups可以对进程组执行挂起、恢复等操作。