也不深入浅出,就慢慢走近Docker吧
Docker是什么?docker与传统虚拟机;docker的镜像;docker的rootfs
提到容器,大家会想到Docker,对于没有实际应用和细分理解的人来说,Docker就是容器,但不然。之前我也对这些概念的关系有些模糊,接触的稍微一多,层理和关系慢慢就明了了,本文我想以我的理解,结合网络上的相关资料来解释一下Docker中的一些概念以及它们之间的区别和联系。
Docker不是容器,而是一种高级容器引擎、镜像和容器管理工具
docker与传统虚拟机的对比:
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
很多人都拿 Docker 和虚拟机 VMware、KVM 和 VirtualBox 比较。尽管从功能上看,Docker 和虚拟化技术致力于解决的问题都差不多,但是 Docker 却是采取了另一种非常不同的方式。虚拟机是虚拟出一套硬件,虚拟机的系统进行的磁盘操作,其实都是在对虚拟出来的磁盘进行操作。当运行 CPU 密集型的任务时,是虚拟机把虚拟系统里的 CPU 指令“翻译”成宿主机的CPU指令并进行执行。两个磁盘层,两个处理器调度器,两个操作系统消耗的内存,所有虚拟出的这些都会带来相当多的性能损失,一台虚拟机所消耗的硬件资源和对应的硬件相当,一台主机上跑太多的虚拟机之后就会过载。而 Docker 就没有这种顾虑。Docker 运行应用采取的是“容器”的解决方案:使用 namespace 和 CGroup 进行资源限制,和宿主机共享内核,不虚拟磁盘,所有的容器磁盘操作其实都是对 /var/lib/docker/ 的操作。
如果拿VMware来做比对解说,那么你可以把docker理解成VMware这样一个虚拟机的管理平台,而镜像就是虚拟机的文件形式,容器就是使用虚拟机文件运行起来的虚拟机。
如果从编程的角度来说,docker可以理解成一种面向对象语言,这门语言中已经有很多别人封装好的对象(镜像),我们只需要在遵守这门编程语言规定的情况下根据需要将使用到的对象进行实例化(运行容器),可以根据实际需求扩展实例并且生成新的对象,而且这个新生成的对象不仅包含母对象的所有属性,还包含我们新加入的属性,更了不起的是这个新的对象在新属性以下是完全指向母对象的,并包含自己的对象层级属性,我们可以继续将这个新对象进行实例化然后再添加新的属性再生成对象,这样最新的对象包含了指向母对象的对象层级、上一个新对象的对象层级以及自己又新加的对象层级属性。。。一直可以达到128个对象层级,那么这个对象(镜像)就不可扩展了。
Docker基于Linux容器技术(LXC),Namespace,Cgroup,UnionFS(联合文件系统)等技术。
namespace(命名空间):命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样。名字空间保证了容器之间彼此互不影响。docker实际上一个进程管理实现,它通过namespace实现了进程和进程所使用的资源的隔离。使不同的进程之间彼此不可见。我们可以把Docker想像成进程+操作系统除内核之外的一套软件。
cgroup(控制组):是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时的对系统资源的竞争。控制组技术最早是由 Google 的程序员 2006 年起提出,Linux 内核自 2.6.24 开始支持。控制组可以提供对容器的内存、CPU、磁盘 IO 等资源的限制和审计管理。
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对 文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。Docker 中使用的 AUFS(AnotherUnionFS)就是一种 Union FS。 AUFS 支持为每一个成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限, 同时 AUFS 里有一个类似分层的概念, 对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分的)。
docker的镜像
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是上文说到的UnionFS。在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。Docker在bootfs之上的一层是rootfs(根文件系统)。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
docker镜像的层级结构图:
docker的rootfs与传统意义的rootfs有什么不同
传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统自检之后将rootfs从read-only改为read-write。然后我们就可以在rootfs上进行写和读的操作了。但docker的镜像却不是这样,他在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only 的rootfs层之上。并在加载了这么多层的rootfs之后,仍然让它看起来只像一个文件系统,在docker的体系里把union mount的这些read-only层的rootfs叫做docker的镜像(image)。请注意,此时的每一层rootfs都是read-only的,也就是说我们此时还不能对其进行操作,那么我们怎样对其进行读写操作呢?
答案是将docker镜像进行实例化,就是上文说的从镜像(image)变成容器(container)的过程,当镜像被实例化为容器之后,系统会为在一层或是多层的read-only的rootfs之上分配一层空的read-write的rootfs。而这个分配的动作是由docker run命令发起的
我们将一个镜像实例化为一个容器之后,docker会在read-only 的rootfs之上分配一层空的read-write的rootfs,我们对文件系统的改变实际上是在空的这层rootfs(read-write)上发生的。打个比方,如果你想修改一个文件,系统实际上是将这个在read-only层的rootfs的文件拷贝到read-write层的rootfs之中,然后对它进行修改,但read-only层的文件并不会被修改,依然存在于read-only层之中,只不过是在read-write层下被隐藏了。这种模式被称为copy on write。这是unionFS的特性。也是docker的强大之处,为什么说强大呢?它允许镜像被继承,也就是说我们想生成一套虚拟环境不用从零开始了,而只要在一个相对完善的基础环境之上来创建我们的虚拟环境就可以了,比如我们想生成一个具有tomcat环境的镜像,只要在一个装有java环境的镜像之上来创建就可以了。这也是docker便捷性的体现。
隔离性: Linux Namespace(ns)
每个用户实例之间相互隔离, 互不影响。 一般的硬件虚拟化方法给出的方法是VM,而LXC给出的方法是container,更细一点讲就是kernel namespace。其中pid、net、ipc、mnt、uts、user等namespace将container的进程、网络、消息、文件系统、UTS("UNIX Time-sharing System")和用户空间隔离开。
1) pid namespace
不同用户的进程就是通过pid namespace隔离开的,且不同 namespace 中可以有相同pid。所有的LXC进程在docker中的父进程为docker进程,每个lxc进程具有不同的namespace。同时由于允许嵌套,因此可以很方便的实现 Docker in Docker。
2) net namespace
有了 pid namespace, 每个namespace中的pid能够相互隔离,但是网络端口还是共享host的端口。网络隔离是通过net namespace实现的, 每个net namespace有独立的 network devices, IP addresses, IP routing tables, /proc/net 目录。这样每个container的网络就能隔离开来。docker默认采用veth的方式将container中的虚拟网卡同host上的一个docker bridge: docker0连接在一起。
3) ipc namespace
container中进程交互还是采用linux常见的进程间交互方法(interprocess communication - IPC), 包括常见的信号量、消息队列和共享内存。然而同 VM 不同的是,container 的进程间交互实际上还是host上具有相同pid namespace中的进程间交互,因此需要在IPC资源申请时加入namespace信息 - 每个IPC资源有一个唯一的 32 位 ID。
4) mnt namespace
类似chroot,将一个进程放到一个特定的目录执行。mnt namespace允许不同namespace的进程看到的文件结构不同,这样每个 namespace 中的进程所看到的文件目录就被隔离开了。同chroot不同,每个namespace中的container在/proc/mounts的信息只包含所在namespace的mount point。
5) uts namespace
UTS("UNIX Time-sharing System") namespace允许每个container拥有独立的hostname和domain name, 使其在网络上可以被视作一个独立的节点而非Host上的一个进程。
6) user namespace
每个container可以有不同的 user 和 group id, 也就是说可以在container内部用container内部的用户执行程序而非Host上的用户。
可配额/可度量 - Control Groups (cgroups)
cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup目录下新建一个文件夹即可新建一个group,在此文件夹中新建task文件,并将pid写入该文件,即可实现对该进程的资源控制。groups可以限制blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns九大子系统的资源,以下是每个子系统的详细说明:
blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。
cpuacct 产生cgroup任务的cpu资源报告。
cpuset 如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
devices 允许或拒绝cgroup任务对设备的访问。
freezer 暂停和恢复cgroup任务。
memory 设置每个cgroup的内存限制以及产生内存资源报告。
net_cls 标记每个网络包以供cgroup方便使用。
ns 名称空间子系统。
便携性: AUFS
AUFS (AnotherUnionFS) 是一种 Union FS, 简单来说就是支持将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)的文件系统, 更进一步的理解, AUFS支持为每一个成员目录(类似Git Branch)设定readonly、readwrite 和 whiteout-able 权限, 同时 AUFS 里有一个类似分层的概念, 对 readonly 权限的 branch 可以逻辑上进行修改(增量地, 不影响 readonly 部分的)。通常 Union FS 有两个用途, 一方面可以实现不借助 LVM、RAID 将多个disk挂到同一个目录下, 另一个更常用的就是将一个 readonly 的 branch 和一个 writeable 的 branch 联合在一起,Live CD正是基于此方法可以允许在 OS image 不变的基础上允许用户在其上进行一些写操作。Docker 在 AUFS 上构建的 container image 也正是如此,接下来我们从启动 container 中的 linux 为例来介绍 docker 对AUFS特性的运用。
安全性: AppArmor, SELinux, GRSEC
安全永远是相对的,这里有三个方面可以考虑Docker的安全特性:
由kernel namespaces和cgroups实现的Linux系统固有的安全标准;
Docker Deamon的安全接口;
Linux本身的安全加固解决方案,类如AppArmor, SELinux;
Docker解决了云计算环境难于分发并且管理复杂,而用KVM、Xen等虚拟化又浪费系统资源的问题。
Docker具体亮在哪?
1. 因为与宿主机共享内核,不需要再启动内核,所以应用扩缩容时可以秒速启动。
2.一键启动所有依赖服务,测试不用为搭建环境犯愁,PE也不用为建站复杂担心。
3.Docker命令简单、易用,社区十分活跃,Bug提交一周内就改好,且周边组件丰富。
4.镜像增量分发,由于采用了Union FS, 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念,每次发布只传输变化的部分,节约带宽。
5.直接使用宿主机内核调度资源,性能损失小。
6.通过Docker能实现物理机的冷迁移(docker commit 、docker push)。
7.动态CPU、内存资源调整。
8.使用的人再也不用担心如何搭建服务,清理还原服务。镜像一次固化,随处使用,多个应用版本可以并存在机器上。Jenkins已经支持自动化构建镜像,解决了打包发布麻烦的问题。
9.测试、生产环境高度一致(数据除外)。
10.应用的运行环境和宿主机环境无关,完全由镜像控制,一台物理机上部署多种环境的镜像测试。
11.能实现秒级快速回滚。
12在高性能计算的场景中,容器热迁移可以保证运行了许多天的计算结果不会丢失,只要周期性的进行检查点快照保存就可以。
。。。
部分内容参考自
http://www.infoq.com/cn/articles/docker-core-technology-preview/
https://yq.aliyun.com/articles/48695
更多推荐
所有评论(0)