Docker是什么
Docker是用于开发(develop)、转移(ship)、运行(run)程序(application)的一个开放平台。Docker的设计目的是为了更快地传递程序。在Docker的帮助下,你能将程序与硬件基础(infrastructure)隔离、把硬件基础看作一个可管理的程序。Docker能帮你更快地转移代码、测试代码、部署代码,缩短编写代码与运行代码之间的周期。
Docker将一种轻量级的容器虚拟化平台技术(container virtualization platform)与相应的工作流程和工具结合起来,从而能帮你管理、部署自己的程序。
在核心层面,Docker支持在一个容器(container)中安全(securely)、独立(isolated)地运行几乎任何一种程序。这种独立性、安全性允许你在主机(host)上同时运行多个容器。容器在运行时不需要分配额外负载给监视程序(hypervisor),它的这种轻量级特性意味着你能更大限度地使用硬件资源。
基于容器虚拟化,Docker提供的工具和平台能帮助你:
- 将你的程序(和支持的组件)放到Docker容器中
- 分发(distribute)、转移(ship)这些容器给自己的团队成员,以便他们后续的开发和测试
- 把这些程序部署到产品环境中,不管你的产品环境位于本地数据中心还是在云中
我能用Docker做什么?
更快地转移程序
Docker是帮你处理开发周期的绝好工具。Docker能允许开发者在包含你的程序和服务的本地容器上开发,然后它能整合到一个连续的整合、部署工作流程中。
举个例子。开发者在本地编写程序,通过Docker将开发环境与同事共享。当他们的工作完成时,开发者将他们的代码和开发环境推送到一个测试环境上,并且执行任何必要的测试。然后,你能从测试环境将Docker镜像(image)推送到产品,部署代码。
更方便地部署、扩展
Docker基于容器的平台支持高便携性(portable)的工作负载(workload)。Docker容器能运行在开发者的本地机器上、数据中心的物理/虚拟机器上,也能运行在云端。
支持更高密度,运行更多工作负载
Docker是轻量级的,而且很快。与基于监督程序(hypervisor)的虚拟机相比,它提供了可变的、低消耗的替代方案。在高密度(high density)的工作环境中,这一点就显得格外重要,例如:当搭建你自己的云或者Platform-as-a-service服务时。不止如此,当你想尽可能地利用你的资源来做小型/中型的部署时,Docker也同样有用。
Docker的主要组件有哪些?
Docker主要组件有两个:
- Docker: 开源的容器虚拟化平台
- Docker Hub:Software-as-a-Service平台,用来分享、管理Docker容器
注意:Docker受开源协议Apache 2.0约束
Docker的架构
Docker使用客户端-服务器架构。Docker客户端(client)与Docker守护进程(deamon)通信,后者来完成建立版本(build)、运行(run)、分发(distribute)Docker容器等工作。Docker客户端和守护进程*可以*同时运行在一个系统上;你也可以将Docker客户端连接到一个远程Docker守护进程。Docker客户端和Docker守护进程通过socket或者REST API通信。
Docker守护进程
如上图所示,Docker守护进程运行在一台宿主机器上。用户不直接与守护进程通信,而是通过客户端与之通信。
Docker客户端
Docker客户端,往往是二进制形式的docker
程序,是Docker最主要的用户使用接口。它接收来自用户的命令,将它来回地与守护程序进行通信。
在Docker内部
为了理解Docker的内部原理,你需要理解三个组件:
- 镜像(image)
- 仓库(registry)
- 容器(container)
#镜像
镜像是一个只读(read-only)的模板。例如,一个镜像可能包含安装了Apache和你的Web服务器的一个Ubuntu操作系统。镜像是用来创造Docker容器的。通过Docker,你能以简单的方式创建新的镜像、更新现存的镜像,或者下载别人已经创建好了的镜像。Docker镜像是Docker的创建(build)组件。
仓库
仓库保存镜像。它们是你用来上传、下载镜像的私有/公有场所。官方的Docker仓库是Docker Hub,它提供了一个巨大的镜像仓库集以供你使用。你可以自己创建镜像,也可以使用别人事先已经建好了的镜像。Docker仓库是Docker的分发(distribute)组件。
容器
容器与目录类似。容器包含了运行一个程序所需要的所有东西。每个容器都是创建自一个镜像。容器可以被运行、启动、停止、移动、删除。每个容器都是一个隔离、安全的程序平台。Docker容器是Docker的运行(run)组件。
那么,Docker到底如何工作?
现在,我们已经知道:
- 你可以创建Docker镜像来保存程序
- 你可以从Docker镜像中新建Docker容器来运行程序
- 你可以通过Docker Hub或者自己的私有仓库来分享Docker镜像
下面,让我们看看这些组件是如何协作起来使Docker工作的。
镜像如何工作?
我们已经知道,镜像是只读模板,由它们启动容器。每个镜像由一系列层(layer)组成。Docker利用union file system将这些层组合成单个镜像。Union file system允许独立文件系统的文件和目录(被称作branch)被透明地叠架起来(overlaid),以此组成一个单个紧密的文件系统。
Docker被称为“轻量级”,原因之一就在于这些层。当你改变一个镜像的时候,譬如说将某个程序更新到了新版本,一个层会被新建出来。如果我们使用的是虚拟机,这时候往往需要替换整个镜像,要不就是整体再创建一个版本。对比之下,Docker只需添加或者更新一个层。如此,你不必再去分发一整个镜像,而仅仅需要更新层就好了,这使得发布Docker的镜像变得更快、更容易。
每个镜像都以一个基础镜像为起点,譬如ubuntu
,一个基础的Ubuntu镜像,或者fedora
,一个基础的Fedora镜像。你也可以用自己的镜像做新镜像的基础镜像,譬如如果你有个基础的Apache镜像,你就能用它作你所有网页程序镜像的基础。
注意:Docker一般从Docker Hub中获取基础镜像。
从基础镜像出发,我们能通过简单、描述性的一系列步骤(我们称其为指示(instructions))新建一个镜像。每一步都会在我们的镜像中新建一个层。这些步骤包括以下动作:
- 运行命令
- 添加文件或者目录
- 创建环境变量
- 定义当启动从这个镜像创建的容器时,应该运行那些进程
这些指示被保存在Dockerfile
文件中。当你申请从镜像生成一个版本(build)时,Docker会读取Dockerfile
、执行指示,然后返回最终的镜像。
仓库如何工作?
仓库是Docker镜像的存储之处。当你创建了一个镜像,你可以将它推送到公共仓库Docker Hub或者自己防火墙之内的私有仓库。
使用Docker客户端,你能搜索已发布的镜像,然后将它们拉去到本地的Docker主机,再从它里面创建容器。
Docker Hub提供镜像的公有和私有存储。公有存储可以被任何人搜索、下载。私有存储不显示在搜索结果中,而且只有你和你的用户能从中拉取镜像、用这些镜像来生成容器。
容器如何工作?
容器由操作系统、用户添加的文件和元文件(meta-data)构成。我们已经知道,每个容器都由一个镜像创建。这个镜像告诉Docker应该持有什么、在启动容器时应该运行什么,以及其他一系列的配置文件。镜像是只读的。当Docker从镜像创建一个容器时,它在镜像的顶端加上一个读写层(read-write layer),这样我们的程序就能在它上面运行了。
启动一个容器时,发生了什么
不论通过docker
命令还是API,Docker客户端通知Docker守护进程去启动一个容器。
$ sudo docker run -i -t ubuntu /bin/bash
我们将这条命令分解来看。Docker客户端通过带run
参数的docker
命令新启动一个容器。为了启动一个容器,Docker客户端至少需要告知Docker守护进程:
- 容器应该创建自哪个Docker镜像。在这里是ubuntu
,一个基础Ubuntu镜像。
- 当容器启动后,你要在容器内运行什么命令。这里是/bin/bash
,它在容器内启动了Bash shell。
那么,当我们运行这条命令时,后台发生了什么呢?
Docker按顺序做了如下事情:
- 拉取ubunut镜像:Docker检查
ubuntu
镜像是否存在,如果在本地不存在,那么它从Docker Hub下载镜像;如果镜像已经存在,Docker将利用它启动新容器。 - 创建新容器:Docker有了镜像,用它来新建一个容器。
- 分配文件系统,挂载读写层:在文件系统中新建了容器,并给镜像新添了一个读写层。
- 分配网络/网桥接口:新建一个网络接口,使Docker容器能与本地主机通信。
- 设置IP地址:从地址池中找到一个可用IP,将它关联到容器。
- 执行你指定的程序:运行程序。
- 捕获、提供程序输出结果:连接并记录标准输入、标准输出、标准错误,使你能看到程序的运行情况。
恭喜,你有了一个运行中的容器!从这里,你可以管理容器,与程序交互,然后当结束后停止、移除容器。
底层技术
Docker用Go语言编写,而且利用了Linux 内核的相关特性来完成上述的功能。
命名空间(namespace)
Docker使用了一项叫作命名空间(namespace)
的技术来为容器提供隔离的工作空间。当我们启动一个容器时,Docker会为它创建一系列命名空间。
这样形成了一个隔离层:容器的每个部分都在它自己的命名空间里运行,而且没有访问它之外的权限。
Docker使用的部分命名空间包括:
- pid命名空间:用于进程隔离(PID, Process ID)
- net命名空间:用于管理网络接口(NET, networking)
- ipc命名空间:用于管理IPC资源(IPC, InterProcess Communication进程间通信)
- mnt命名空间:用于管理挂载点(MNT, Mount)
- uts命名空间:用于内核和版本标志隔离(UTS, Unix Timesharing System)
组控制(Control groups)
Docker还用到cgroups
技术来进行组控制。隔离运行中程序的关键一点在于,让它们只使用你想让它使用的资源。这确保这些容器在宿主机器上能规规矩矩的。组控制允许Docker能向容器共享硬件资源,而且在必要时候设置资源的上限和限制。例如,可以设置某个特定容器的内存上限。
Union file Systems
Union file Systems,或者UnionFS,是通过创建层的方式运行的,轻量、快速的文件系统。Docker使用Union file Systems为容器提供块(block)。Docker能利用包括AUFS, btrfs, vfs, 和DeviceMapper在内的Union file Systems。
容器格式
Docker将这些组件结合成一个我们称之为容器格式的包裹层(wrapper)。默认的容器格式被称作libcontainer
。Docker也支持使用LXC的传统Linux容器。未来,Docker可能会支持更多的容器格式,例如可能会整合BSD Jail或者Solaris Zone。