随着集群规模的扩大,您是否曾经因镜像分发问题而困扰过?根据不一样的场景,咱们利用不一样的镜像分发方法:java
你会发现第二种方式依赖于网络稳定性。本文将总结如何根据镜像内容读请求动态加载从远程到本地存储的镜像做为权衡,以及如何选择适合镜像分布的方式。
mysql
在业务很小的时候,能够单台机器部署单个容器;但当业务量大了,而且分为多个应用时,就须要将多个应用部署在同一台机器上;此时多个应用的依赖是一个比较难解决的问题。且多个应用同时运行,相互之间也有所干扰。
一开始,可使用虚拟机的方式来实现,这样每一个应用一个虚拟机,基本不存在依赖冲突,可是虚拟机仍是会有启动慢,空间占用大等一系列问题。
后面随着技术的发展,有了 lxc 技术,它能让应用跑在一个单独的命名空间上,实现了应用的运行态隔离。
这里能够看到,虚拟机解决了静态隔离的问题,lcx 解决了运行态隔离的问题,而 Docker 则同时解决了运行态和静态隔离两个问题,所以获得亲睐。
linux
Docker 的三大核心:镜像、容器、仓库。容器在 Docker 以前已经有了不一样的实现,好比 lxc。镜像和仓库是 Docker 的很大创新。镜像是一个实体,仓库是它的集中存储中心,而容器则是镜像的运行时。
Docker 经过镜像,能够很神奇地实现好比:开发的同窗在本身喜欢的平台上面作开发测试(例如 ubuntu),而真正部署的节点,多是 redhat/centos 的服务器。
Docker 是以镜像为基础,因此容器运行时必需要有镜像,而容器又能够经过 docker commit 生成一个镜像。可是第一个镜像从哪里来呢?能够经过 docker import/load 的方式导入。而且能够经过 docker push/pull 的方向将镜像上传到镜像中心,或从镜像中心下载镜像。
Docker 提供了一套 docker build 机制,方便来制做镜像。想像一下,没有 docker build 机制,制做镜像的步骤以下:
nginx
有了 docker build 机制,只须要一个 dockerfile,而后 Docker 就会把上述步骤自动化了,最后生成一个目标镜像。
详细看一下是如何使用镜像的,镜像是一层一层的,设计上,镜像全部层是只读的,linux 经过 aufs/overlayfs 的方式将一个镜像 mount 到主机上后,从上往下看,最上面层是容器的可读写层,容器运行时的全部写操做都写到这一层上面,而下面的层都是镜像的只读层。
golang
在 Docker 的 registry 或其它网站上看到,像 busybox, nginx, mysql 等镜像都不大,也就是几十 M 到几百 M 的样子。
可是阿里巴巴的镜像广泛有 3~4G,镜像这么大缘由有许多,好比:
算法
这就致使一个问题:镜像中心颇有可能被打爆。
这种状况在集团出现屡次,在打爆的状况下,当前的镜像拉取被阻塞,同时又有更多的镜像拉取请求上来,致使镜像中心严重超负荷运行,而且导致问题愈来愈严重,可能会致使整个系统奔溃。所以,必须解决镜像下载致使的问题。
sql
集团开发了蜻蜓(Dragonfly )这套 P2P 下载系统。上面是蜻蜓的架构图,蜻蜓能够分为三层,最上层是一个配置中心,中间是超级节点,下面就是运行容器的物理节点。
配置中心管理整个蜻蜓集群,当物理节点上有镜像要下载时,它经过 dfget 命令向超级节点发出请求,而向哪些超级节点发出请求是由配置中心配置的。
如下图为例:
当下载一个文件时,物理节点发送了一个 dfget 请求到超级节点,超级节点会检查自身是否有这部分数据,若是有,则直接把数据传送给物理节点;不然会向镜像中心发出下载请求,这个下载请求是分块的,这样,只有下载失败的块须要从新下载,而下载成功的块,则不须要。
更为重要的是,若是是两个或更多物理节点请求同一份数据,那么蜻蜓收到请求后,只会往镜像中心发一个请求,极大地下降了镜像中心的压力。
关于 P2P,也就是说若是一个节点有了另外一个节点的数据,为了减小超级节点的压力,两个节点能够完成数据的互传。这样更高效地利用了网络带宽。
docker
从上面的蜻蜓效果图能够看到:
随着镜像拉取的并发量愈来愈大,传统方式所消耗的时间会愈来愈多,后面的线条愈来愈陡;可是蜻蜓模式下,虽然耗时有增长,但只是轻微增长。效果仍是很是明显的。
ubuntu
应该说,集团内是先有蜻蜓,后面才有 docker 的。缘由是集团在全面 docker 以前,使用了 t4 等技术,无论哪一种技术,都须要在物理机上面下载应用的包,而后把应用跑起来。
因此,从里程碑上看,2015 年 6 月就已经有了蜻蜓 p2p 了,随着集团全面 Docker 化,在 2015 年 9 月,蜻蜓提供了对 Docker 镜像的支持,后面的几个时间点上,能够看到蜻蜓经受了几回双 11 的考验,而且通过软件迭代,在 2017 年 11 月的时候实现了开源。
如今蜻蜓还在不断演化,好比说所有使用 golang 重写,解决 bug,提供更多能力集,相信蜻蜓的明天更美好。
centos
蜻蜓在集团取得了极好的效果,可是并不能解决全部问题,好比咱们就遇到了如下比较突出的问题:
在实践中,上线的业务运行常常有抖动的现象,分析其中缘由,有部分业务抖动时,都在作镜像下载。
分析缘由发现:镜像层采用的是 gzip 压缩算法。这一古老的算法,是单线程运行,而且独占一个 CPU,更要命的是解压速度也很慢,若是几个镜像层同时展开的话,就要占用几个 CPU,这抢占了业务的 CPU 资源,致使了业务的抖动。
平时应用发布、升级时,只须要选择在业务低峰,而且经过必定的算法,好比只让 10% 左右的容器作发布、升级操做,其它 90% 的容器还给用户提供服务。这种不影响业务、不影响用户体验的操做,对于时效性要求不高,只要在业务高峰来临前完成操做便可,因此耗时一两个小时也无所谓。
假设遇到大促场景,原计划 10 个容器能够服务 100 万用户,可是,忽然来了 300 万用户,这时全部用户的体验将会降低,这时就须要经过扩容手段来增长服务器数量,此时对容器扩出来有着极高的时效要求。
如今全部公司都在上云,集团也在上云阶段,但云上服务器的一些特性与物理机仍是有些差异,对镜像来说感觉最深的就是磁盘 IO 了,原来物理机的 SSD 磁盘,IO 能够达到 1G 以上,而使用 ECS 后,标准速度是 140M,速度只有原来的十分之一。
对于 gzip 的问题,经过实测及数据分析,如上图所示:
1 与 2 下载的是同一个镜像层,只是 1 下载的是一个 gzip 格式的,而 2 下载的是一个没有压缩的镜像层。能够看到 2 由于下载的数据量要大不少,因此下载的时间要长许多,可是 1 中将 400M 的 gzip 还原成 1G 的原始数据却又消耗了太多时间,因此最终二者整体时效是差很少的。
而且因为去掉 gzip,镜像下载不会抢占业务的 CPU 资源。自从上了这一方案后,业务抖动的次数明显减小了许多。
在蜻蜓的章节,镜像下载是镜像层从超级节点到物理机,在这一过程当中,蜻蜓有一个动态压缩能力,好比使用更好的 lz4 算法,即达到了数据压缩的效果,又不会形成业务抖动。这就是图 3,总体上这一点在集团的应用效果很不错。
解决了镜像下载对业务的干扰后,扩容、云环境的问题尚未解决。这时远程盘就派上用场了。
从上面的架构图看,集团有一套镜像转换机制,将原始的镜像层放在远端服务器上,第一个层都有一个惟一的远程盘与之对应。而后镜像中保存的是这个远程盘的 id,这样作下来,远程盘的镜像能够作到 kB 级别。对于 kB 级别的镜像,下载耗时在 1~2 秒之间。
经过远程盘,解决了镜像下载的问题,同时因为远程盘放在物理机同一个机房,容器运行时读取镜像数据,至关于从远程盘上面读取数据,由于在同一个机房,固然不能跟本地盘比,可是效果能够与云环境的云盘性能相媲美。
远程盘虽然解决了镜像下载的问题,可是全部镜像的数据都是从远程盘上读取,消耗比较大的网络带宽。当物理机上环境比较复杂时,远程盘的数据又不能缓存在内存时,全部数据都要从远端读取,当规模上来后,就会给网络带来不小的压力。
另一个问题是,若是远程盘出现问题,致使 IO hang,对于容器进程来说,就是僵尸进程,而僵尸进程是没法杀死的。对于应用来说,一个容器要么是死,要么是活,都好办,死的了容器上面的流量分给活着的容器便可,但对于一个僵尸容器,流量无法摘除,致使这部分业务就受损了。
最后,远程盘由于要服务多台物理机,必然要在磁盘 IO 上面有比较好的性能,这就致使了成本较高。
针对上述问题,集团采用的 DADI 这一项技术,都是对症下药:
远程盘是物理机全部数据都要从远程盘上读,这样会致使对远程盘机器的配置较高的要求,而且压力也很大。
而经过 P2P 手段,能够将一部分压力分担掉,当一台机器已经有另外一台须要的数据时,它俩之间能够完成数据互传。
一样是解决网络带宽的问题,远程盘对应的数据都是没有压缩的,传输会占用比较多的带宽。
而 DADI 则在传输过程当中,跟蜻蜓同样,对数据进行压缩,减小网络压力。
这个是核心买点了,当容器启动后,DADI 会把整个镜像数据拉取到本地,这样即便网络有问题,也不会致使容器进程僵尸,解决了业务受损的问题。