1. Docker 简介

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,每个容器内运行一个应用,不同的容器相互隔离,容器之间不会有任何接口(类似 iPhone 的 app),容器之间也可以建立通信机制。更重要的是容器性能开销极低。容器的创建和停止都十分快速,容器自身对资源的需求也十分有限。

Docker 引擎的基础是 Linux 容器(Linux Containers, LXC)技术。

2. Docker 容器与虚拟机比较

docker 镜像运行容器 docker 容器 镜像 区别_容器

docker 镜像运行容器 docker 容器 镜像 区别_Docker_02

3. Docker 和常见的虚拟机的差异

docker 镜像运行容器 docker 容器 镜像 区别_Docker_03

4. Docker 基础概念

4.1 Docker 镜像

Docker镜像(Image)类似于虚拟机镜像,可以将它理解为一个面向 Docker 引擎的只读的模板。包含了文件系统。

例如:一个镜像可以只包含一个完整的 Ubuntu 操作系统环境,可以把它称为一个 Ubuntu 镜像。镜像里面仅安装了 Apache 应用程序(或用户需要的其他软件)。可以把它称为一个 Apache 镜像。

镜像是创建 Docker 容器的基础。通过版本管理和增量的文件系统,Docker 提供了一套十分简单的机制来创建和更新现有的镜像,用户甚至可以从网上下载一个已经做好的应用镜像,并直接使用。

4.2 Docker 容器

Docker容器(Container)类似于一个轻量级的沙箱,Docker 利用容器来运行和隔离应用。

容器是从镜像创建的应用运行实例。可以将其启动、开始、停止、删除,而这些容器都是彼此相互隔离的、互不可见的。

可以把容器看做是一个简易版的 Linux 系统环境 (包括 root 用户权限、进程空间、用户空间和网络空间等) 以及运行在其中的应用程序打包而成的盒子。

镜像自身是只读的。容器从镜像启动的时候,会在镜像的最上层创建一个可写层。

4.3 Docker 仓库

Docker仓库(Repository)类似于代码仓库,它是 Docker 集中存放镜像文件的场所。

有时候会看到有资料将 Docker 仓库和仓库注册服务器 (Registry) 混为一谈,并不严格区分。实际上,仓库注册服务器是存放仓库的地方,其上往往存放着多个仓库。每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签 (tag) 来进行区分。

例如存放 Ubuntu 操作系统镜像的仓库称为 Ubuntu 仓库,其中可能包括 14.04、12.04 等不同版本的镜像。仓库注册服务器的示例如下图所示。

docker 镜像运行容器 docker 容器 镜像 区别_文件系统_04

根据所存储的镜像公开分享与否,Docker 仓库可以分为公开仓库 (Public) 和私有仓库 (Private) 两种形式。

目前,最大的公开仓库是官方提供的 Docker Hub,其中存放了数量庞大的镜像供用户下载。国内不少云服务提供商 (如时速云、阿里云等) 也提供了仓库的本地源,可以提供稳定的国内访问。

当然,用户如果不希望公开分享自己的镜像文件,Docker 也支持用户在本地网络内创建一个只能自己访问的私有仓库。当用户创建了自己的镜像之后就可以使用 push 命令将它上传到指定的公有或者私有仓库。这样用户下次在另外一台机器上使用该镜像时,只需要将其从仓库上 pull 下来就可以了。

4.4 镜像、容器、仓库的区别

  1. docker 镜像(Images)
    一个只读模板,可以用来创建容器,一个镜像可以创建多个容器;
    Docker 提供了一个很简单的机制来创建和更新现有的镜像,甚至可以直接从其他人那里获取做好的镜像直接使用;
    可以理解为 Java 中的类;
  2. docker 容器(Container)
    容器是从镜像创建的运行实例,也就是镜像启动后的一个实例称为容器,是独立运行的一个或一组应用;
    Docker 利用容器来运行应用,他可以被启动、开始、停止、删除,每个容器都是相互隔离的、保证安全的平台;
    可以把容器看做是一个简易版的 Linux(包括 root 用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序;
    可以理解为 Java 中通过类创建的实例;
  3. docker 仓库(Resoisitory)
    仓库是集中存放镜像文件的场所,类似 git 代码仓库等;
    仓库(Respository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器一般存放多个仓库,每个仓库又有多个镜像,每个镜像又有不同的标签 tag;
    仓库分为公开仓库(public)和私有仓库(private)两种形式;
    最大的公开仓库是 Docker Hub,国内的公开仓库有阿里云等;
    可以在本地网络创建一个私有仓库;
    当创建好自己的镜像后,可以通过 push 命令把它上传到公开或私有仓库;
    仓库的概念类似 Git,仓库注册服务器可以理解为 GitHub 这种托管服务;
  4. docker 主机(Host)
    一个物理或虚拟的机器用来执行 Docker 守护进程和容器。
  5. docker客户端(Client)
    客户端通过命令行或其他工具使用 Docker。

5. Docker 的技术实现

Docker 的实现,主要归结于三大技术:

  • 命名空间 ( Namespaces )
  • 控制组 ( Control Groups )
  • 联合文件系统 ( Union File System )

5.1 Namespace

命名空间是 Linux 核心在 2.4 版本后逐渐引入的一项用于运行隔离的模块。

相信很多开发者在不同的编程语言中都见过命名空间的概念,在这些编程语言中,命名空间的主要目的就是为了集合相同模块的类,区分不同模块间的同名类。

同样的道理,Linux 内核的命名空间,就是能够将计算机资源进行切割划分,形成各自独立的空间。

就实现而言,Linux Namespaces 可以分为很多具体的子系统,如 User Namespace、Net Namespace、PID Namespace、Mount Namespace 等等。

这里我们以进程为例,通过 PID Namespace,我们可以造就一个独立的进程运行空间,在其中进程的编号又会从 1 开始。在这个空间中运行的进程,完全感知不到外界系统中的其他进程或是其他进程命名空间中运行的进程。

docker 镜像运行容器 docker 容器 镜像 区别_docker 镜像运行容器_05


利用 PID Namespace,Docker 就实现了容器中隔离程序运行中进程隔离这一目标。

Namespace 它可以创建出独立的文件系统、主机名、进程号、网络等资源空间,相当于给进程盖了一间小板房,这样就实现了系统全局资源和进程局部资源的隔离。

5.2 Control Groups

资源控制组 ( 常缩写为 CGroups ) 是 Linux 内核在 2.6 版本后逐渐引入的一项对计算机资源控制的模块。

顾名思义,资源控制组的作用就是控制计算机资源的。与以隔离进程、网络、文件系统等虚拟资源为目的 Namespace 不同,CGroups 主要做的是硬件资源的隔离。

之前我们提到了,虚拟化除了制造出虚拟的环境隔离同一物理平台运行的不同程序之外,另一大作用就是控制硬件资源的分配,CGroups 的使用正是为了这样的目的。

docker 镜像运行容器 docker 容器 镜像 区别_Docker_06


需要再强调一次的是,CGroups 除了资源的隔离,还有资源分配这个关键性的作用。通过 CGroups,我们可以指定任意一个隔离环境对任意资源的占用值或占用率,这对于很多分布式使用场景来说是非常有用的功能。

例如,我们在服务器上部署一个业务服务和一个健康监控服务。通常情况下,监控服务只会占用很少的计算机资源,但我们无法保证其不会因为一些逻辑问题产生 Bug 进而过分消耗计算机资源。而它申请的计算机资源越多,意味着业务服务所能使用的计算机资源也就越少,最后甚至可能造成物理服务器的崩溃。

上述的问题在没有隔离实现的普通运行环境下是比较难解决的,因为所有不从系统层面出发的限制程序资源使用的方式都并不完全有效。由于 CGroups 实现于操作系统,而操作系统垄断着系统资源的分配,所以其完全能够限制隔离环境下应用的资源占有量。

5.3 Union File System

联合文件系统 ( Union File System ) 是一种能够同时挂载不同实际文件或文件夹到同一目录,形成一种联合文件结构的文件系统。联合文件系统本身与虚拟化并无太大的关系,但 Docker 却创新的将其引入到容器实现中,用它解决虚拟环境对文件系统占用过量,实现虚拟环境快速启停等问题。

在 Docker 中,提供了一种对 UnionFS 的改进实现,也就是 AUFS ( Advanced Union File System )。

docker 镜像运行容器 docker 容器 镜像 区别_文件系统_07


AUFS 将文件的更新挂载到老的文件之上,而不去修改那些不更新的内容,这就意味着即使虚拟的文件系统被反复修改,也能保证对真实文件系统的空间占用保持一个较低水平。

也许这个表述还不够形象,那么我们来用 Git 进行比较,会让大家会更容易理解。大家知道,我们在 Git 中每进行一次提交,Git 并不是将我们所有的内容打包成一个版本,而只是将修改的部分进行记录,这样即使我们提交很多次后,代码库的空间占用也不会倍数增加。

同样的,通过 AUFS,Docker 大幅减少了虚拟文件系统对物理存储空间的占用。由此,Docker 也开创出了虚拟化领域很多新的轻量级解决方案,这在之后的小节里我们会提到。