关于docker:Docker-与-k8s-的恩怨情仇三后浪-Docker-来势汹汹

2次阅读

共计 3637 个字符,预计需要花费 10 分钟才能阅读完成。

转载请注明出处:葡萄城官网,葡萄城为开发者提供业余的开发工具、解决方案和服务,赋能开发者。

上一节咱们为大家介绍了 Cloud Foundry 等最后的 PaaS 平台如何解决容器问题,本文将为大家展现 Docker 如何解决 Cloud Foundry 遭逢的一致性和复用性两个问题,并比照剖析 Docker 和传统虚拟机的差别。

Docker 相比于 Cloud Foundry 的改良

利用“Mount Namespace”解决一致性问题

在本系列文章的第一节中,咱们提到 Docker 通过 Docker 镜像(Docker Image)性能迅速取代了 Cloud Foundry,那这个 Docker 镜像到底是什么呢,如何通过为不同的容器应用不同的文件系统以解决一致性问题?先卖个关子,咱们先来看看上一节中说过隔离性能和 Namespace 机制。

Mount Namespace,这个名字中的“Mount”能够让咱们想到这个机制是与文件挂载内容相干的。Mount Namespace 是用来隔离过程的挂载目录的,让咱们能够通过一个“简略”的例子来看看它是怎么工作的。

(用 C 语言开发出未实现文件隔离的容器)

下面是一个简略的的 C 语言代码,内容只包含两个逻辑:
1. 在 main 函数中创立了一个子过程,并且传递了一个参数 CLONE_NEWNS,这个参数就是用来实现 Mount Namespace 的;
2. 在子过程中调用了 /bin/bash 命令运行了一个子过程外部的 shell。

让咱们编译并且执行一下这个程序:

gcc -o ns ns.c
./ns

这样咱们就进入了这个子过程的 shell 中。在这里,咱们能够运行 ls /tmp 查看该目录的构造,并和宿主机进行一下比照:

(容器内外的 /tmp 目录)

咱们会发现两边展现的数据竟然是齐全一样的。依照上一部分 Cpu Namespace 的论断,应该别离看到两个不同的文件目录才对。为什么?

容器内外的文件夹内容雷同,是因为咱们批改了 Mount Namespace。Mount Namespace 批改的是过程对文件系统“挂载点”的认知,意思也就是只有产生了挂载这个操作之后生成的所有目录才会是一个新的零碎,而如果不做挂载操作,那就和宿主机的完全一致。

如何解决这个问题,实现文件隔离呢?咱们只须要在创立过程时,在申明 Mount Namespace 之外,通知过程须要进行一次挂载操作就能够了。简略批改一下新过程的代码,而后运行查看:

(实现文件隔离的代码和执行成果)

此时文件隔离胜利,子过程的 /tmp 曾经被挂载进了 tmpfs(一个内存盘)中了,这就相当于创立了齐全一个新的 tmp 环境,因而子过程外部新创建的目录宿主机中曾经无奈看到。

下面这点简略的代码就是来自 Docker 镜像的实现。Docker 镜像在文件操作上实质是对 rootfs 的一次封装,Docker 将一个利用所需操作系统的 rootfs 通过 Mount Namespace 进行封装,扭转了应用程序和操作系统的依赖关系,即本来应用程序是在操作系统内运行的,而 Docker 把“操作系统”封装变成了应用程序的依赖库,这样就解决了利用程序运行环境一致性的问题。不管在哪里,利用所运行的零碎曾经成了一个“依赖库”,这样就能够对一致性有所保障。

利用“层”解决复用性问题

在实现文件系统隔离,解决一致性问题后,咱们还须要面对复用性的问题。在理论应用过程中,咱们不大可能每做一个镜像就挂载一个新的 rootfs,费时费力,不带任何程序的“光盘”也须要占用很大磁盘空间来实现这些内容的挂载。

因而,Docker 镜像应用了另一个技术:UnionFS 以及一个全新的概念:层(layer),来优化每一个镜像的磁盘空间占用,晋升镜像的复用性。

咱们先简略看一下 UnionFS 是干什么的。UnionFS 是一个联结挂载的性能,它能够将多个门路下的文件联结挂载到同一个目录下。举个“栗子”,当初有一个如下的目录构造:

(应用 tree 命令,查看蕴含 A 和 B 两个文件夹)

A 目录下有 a 和 x 两个文件,B 目录下有 b 和 x 两个文件,通过 UnionFS 的性能,咱们能够将这两个目录挂载到 C 目录下,成果如下图所示:

mount -t aufs -o dirs=./a:./b none ./C

(应用 tree 命令查看联结挂载的成果)

最终 C 目录下的 x 只有一份,并且如果咱们对 C 目录下的 a、b、x 批改,之前目录 A 和 B 中的文件同样会被批改。而 Docker 正是用了这个技术,对其镜像内的文件进行了联结挂载,比方能够别离把 /sys,/etc,/tmp 目录一起挂载到 rootfs 中造成一个在子过程看起来就是一个残缺的 rootfs,但没有占用额定的磁盘空间。

在此基础上,Docker 还本人翻新了一个层的概念。首先,它将零碎内核所须要的 rootfs 内的文件挂载到了一个“只读层”中,将用户的应用程序、零碎的配置文件等之类能够批改的文件挂载到了“可读写层”中。在容器启动时,咱们还能够将初始化参数挂载到了专门的“init 层”中。容器启动的最初阶段,这三层再次被联结挂载,最终造成了容器中的 rootfs。

(Docker 的只读层、可读写层和 init 层)

从下面的形容中,咱们能够理解到只读层最适宜搁置的是固定版本的文件,代码简直不会扭转,能力实现最大水平的复用。比方活字格私有云是基于.net core 开发的,咱们将其用到的根底环境等都会设计在了只读层,每次获取最新镜像时,因为每一份只读层都是齐全一样的,所以齐全不必下载。

Docker 的“层”解释了为什么 Docker 镜像只在第一次下载时那么慢,而之后的镜像都很快,并且明明每份镜像看起来都几百兆,然而最终机器上的硬盘缺没有占用那么多的起因。更小的磁盘空间、更快的加载速度,让 Docker 的复用性有了十分显著的晋升。

Docker 容器创立流程

下面介绍的是 Docker 容器的整个原理。咱们联合上一篇文章,能够总结一下 Docker 创立容器的过程其实是:

  • 启用 Linux Namespace 配置;
  • 设置指定的 Cgroups 参数;
  • 过程的根目录
  • 联结挂载各层文件

题外:Docker 与传统虚拟机的区别

其实 Docker 还做了很多性能,比方权限配置,DeviceMapper 等等,这里说的仅仅是一个遍及性质的概念性解说,底层的各种实现还有很简单的概念。具体而言,容器和传统的虚拟机有啥区别?

其实容器技术和虚拟机是实现虚拟化技术的两种伎俩,只不过虚拟机是通过 Hypervisor 管制硬件,模拟出一个 GuestOS 来做虚拟化的,其外部是一个简直实在的虚构操作系统,外部内部是齐全隔离的。而容器技术是通过 Linux 操作系统的伎俩,通过相似于 Docker Engine 这样的软件对系统资源进行的一次隔离和调配。它们之间的比照关系大略如下:

(Docker vs 虚拟机)

虚拟机是物理隔离,相比于 Docker 容器来说更加平安,但也会带来一个后果:在没有优化的状况下,一个运行 CentOS 的 KVM 虚拟机启动后本身须要占用 100~200MB 内存。此外,用户利用也运行在虚拟机外面,利用零碎调用宿主机的操作系统不可避免须要通过虚拟化软件的拦挡和解决,自身会带来性能损耗,尤其是对计算资源、网络和磁盘 I / O 的损耗十分大。

但容器与之相同,容器化之后的利用仍然是一个宿主机上的一般过程,这意味着因为虚拟化而带来的损耗并不存在;另一方面应用 Namespace 作为隔离伎俩的容器并不需要独自的 Guest OS,这样一来容器额定占用的资源内容简直能够忽略不计。

所以,对于更加须要进行细粒度资源管理的 PaaS 平台而言,这种“麻利”和“高效”的容器就成为了其中的佼佼者。看起来解决了所有问题的容器。难道就没有毛病吗?

其实容器的弊病也特地显著。首先因为容器是模仿进去的隔离性,所以对 Namespace 模仿不进去的资源:比方操作系统内核就齐全无奈隔离,容器外部的程序和宿主机是共享操作系统内核的,也就是说,一个低版本的 Linux 宿主机很可能是无奈运行高版本容器的。还有一个典型的栗子就是工夫,如果容器中通过某种伎俩批改了零碎工夫,那么宿主机的工夫一样会扭转。

另一个弊病是安全性。个别的企业,是不会间接把容器裸露给内部用户间接应用的,因为容器内能够间接操作内核代码,如果黑客能够通过某种伎俩批改内核程序,那就能够黑掉整个宿主机,这也是为什么咱们本人的我的项目从刚开始本人写 Docker 到最初弃用的间接起因。当初个别解决安全性的办法有两个:一个是限度 Docker 内过程的运行权限,管制它值能操作咱们想让它操作的零碎设施,然而这须要大量的定制化代码,因为咱们可能并不知道它须要操作什么;另一个形式是在容器内部加一层虚拟机实现的沙箱,这也是当初许多头部大厂的次要实现形式。

小结

Docker 凭借一致性、复用性的劣势战败了前辈 Cloud Foundry。本文介绍了 Docker 具体对容器做的一点扭转,同时也介绍了容器的显著毛病。下一篇文章,咱们会为大家介绍 Docker 又是如何落寞,而后 Docker 时代,谁又是时代新星。敬请期待。

正文完
 0