火爆的Docker不是“上帝”

 

这可能是关于Docker最为详尽的诠释了。...



今天主要想科普一下Docker。Docker是dotCloud开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,可以简单的理解成轻量的虚拟化技术,它透露出一个思想,那就是统一,一次操作,随处运行。



今年是Docker技术三周年,各地的开发者纷纷献上祝福,其2015年一年的增长速度也是非常惊人的(具体数据如图),国内的各大公司也是纷纷跟进这项技术,Docker的云产品也如雨后春笋般的疯涨。

虚拟化技术的演进

谈及Docker就不能不谈一下虚拟化技术,Docker作为轻量级的虚拟化技术,是第三代虚拟化解决方案(说法不一),从最早的硬件虚拟化,只有特定厂商,特殊的硬件支持的操作系统多租户访问,到跑在通用硬件上的Hypervisor技术,再到通过内核特性,以进程方式运行多个容器,几代产品围绕着成本、隔离、性能不断演进。



  • 1990年,应用程序服务提供者服务(application service provider)模式出现,它的做法与运作模式与租用大型主机时相同,不过租用的资源是在软件上,除了操作系统以外也包含了其上的应用程序,例如ERP系统或是CRM等应用,系统可能会运行在数台不同的机器上,或是在相同的主机但共享不同的数据库,以区分并计算客户的资源使用量,藉以作为计费的标准,而此技术也有效的缩减供应商的实体机器成本。
  • 2000年,Hypervisor开始引领风潮,作为一种运行在物理服务器和操作系统之间的中间软件层(Hypervisor有两种,还有一种裸机模式,这里不做详述),允许多种操作系统和应用共享一套基础物理硬件,因此也可以看作是虚拟环境中的底层OS,它可以协调访问服务器上的所有物理设备和虚拟机,也叫虚拟机监视器(Virtual Machine Monitor)。当服务器启动并执行Hypervisor时,它会给每一台虚拟机分配适量的内存、CPU、网络和磁盘,并加载所有虚拟机的客户操作系统。与此同时也带来了Dom0控制器要占用一部分机器资源做“调度”功能,还有就是性能损失,机器资源碎片的问题也凸显出来(不超配的情况下会有不能充分利用的情况发生),但高隔离性、安全性,支持Windows等等优势也是十分明显。
  • 2010年,应用虚拟化进入人们视野,利用Linux Kernel中的资源分离机制,Cgroups,Namespaces,来建立独立的软件容器(Containers)。这可以在单一Linux实体下运作,避免启动一个虚拟机造成的额外负担(没有Dom0的资源浪费问题)。Kernel对命名空间的支援完全隔离了工作环境中应用程序的视野,包括进程树、网络、用户ID与挂载档案系统,而Kernel的Cgroups提供资源隔离,包括CPU、内存、block I/O与网络。从0.9版本起,Docker由Libvirt的LXC与Systemd nspawn提供支持,改为开始采用Libcontainer库直接使用由Kernel提供的虚拟化设施。
Docker与VM的差异

比较两图的差异,左图虚拟机的Guest OS层和Hypervisor层在Docker中被Docker Engine层所取代。(Guest OS即为虚拟机安装的操作系统,它是一个完整操作系统内核,而Docker则是共用宿主机系统内核),虚拟机实现资源隔离的方法是利用独立的OS,并利用Hypervisor虚拟化CPU、内存、IO设备等实现的。对比虚拟机实现资源和环境隔离的方案,Docker就显得轻量很多,只是对已有技术的封装,Docker利用的是目前Linux Kernel本身支持的方式实现资源和环境隔离。简单的说,Docker利用Namespaces实现系统环境的隔离,利用Cgroups实现资源限制,利用镜像实现根目录环境的隔离。



  • 计算效率上,Docker有着比虚拟机更少的抽象层。由于不需要Hypervisor实现硬件资源虚拟化,运行在Docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上Docker将会有明显优势(最少提高10%,IBM的文章说由于Hypervisor对CPU指令集、NUMA支持的不完整性,浮点运算会提高50%,个人感觉也没那么多)。在IO设备虚拟化上,Docker的镜像管理有多种方案,比如利用Aufs文件系统或者DeviceMapper实现文件管理。
  • 启动效率上,由于不需要Guest OS,当新建一个容器时,Docker不需要和虚拟机一样重新加载一个操作系统内核。我们知道,引导、加载操作系统内核是一个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,这个新建过程是分钟级的。而Docker由于直接利用宿主机的操作系统,省略了这个过程,因此新建一个Docker容器只需要几秒钟。快速启动、低系统资源消耗的优点使Docker在弹性云平台和自动化运维系统方面有着很好的应用前景。
  • 交付与部署上,Docker做到一次创建或配置,可以在任意地方运行,大量地节约开发、测试、依赖部署等的时间。
  • 缺点。资源隔离方面不如虚拟机,Docker利用Cgroups实现资源限制,只能限制资源消耗的最大值,而不能完全隔绝其它程序占用自己的资源,安全性方面,包括鉴权管控、fork炸弹防范等等还是有一些短板,网络资源管理相对简单,对宿主机OS的兼容性也是短版,毕竟企业级部署还处在Redhat 5时代。
Docker的组成

容器真正运行起来至少包含三个方面,镜像、仓库、容器。



典型的Linux文件系统由bootfs和rootfs两部分组成,bootfs(boot file system)主要包含 Bootloader和Kernel,Bootloader主要是引导加载Kernel,当Kernel被加载到内存中后bootfs就被umount了。rootfs (root file system) 包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统自检之后将rootfs从read-only改为read-write,然后我们就可以在rootfs上进行写和读的操作了。但Docker的镜像却不是这样,它在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(Union FS, 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念。对read-only目录只能读,而写操作只能实施在read-write目录中。重点在于,写操作是在read-only上的一种增量操作,不影响read-only目录。当挂载目录的时候要严格按照各目录之间的这种增量关系,将被增量操作的目录优先于在它基础上增量操作的目录挂载,待所有目录挂载结束了,继续挂载一个read-write目录,如此便形成了一种层次结构。)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。当我们创建一个容器,也就是将Docker镜像进行实例化时,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。

仓库是一个存储容器镜像的地方,包括搜索、下载、上传三个功能。

Docker Engine作为容器中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求,而后Engine执行Docker内部的一系列工作,每一项工作都以一个Job的形式存在。Job运行过程中,当需要容器镜像时,则从Docker Hub中下载镜像,并通过镜像管理驱动GraphDriver将下载的镜像以Graph的形式存储,当需要为Docker创建网络环境时,通过网络管理驱动NetworkDriver创建并配置Docker容器网络环境,当需要限制Docker容器运行资源或执行用户指令等操作时,则通过ExecDriver来完成。而NetworkDriver以及ExecDriver都是通过Libcontainer来实现具体的对容器进行的操作。Libcontainer是一个独立的容器管理包,Docker刚发布的时候,它是一款基于LXC的开源容器管理引擎。把LXC复杂的容器创建与使用方式简化为Docker自己的一套命令体系。随着Docker的不断发展,它开始将底层实现都抽象化到Libcontainer接口。这就意味着,底层容器的实现方式变成了一种可变的方案,无论是使用Namespaces、Cgroups技术,抑或是使用Systemd等其他方案,只要实现了Libcontainer定义的一组接口,Docker都可以运行。这也为Docker实现全面的跨平台带来了可能。目前版本的Libcontainer,功能实现上涵盖了包括Namespaces使用、Cgroups管理、Rootfs的配置启动、默认的Capability权限集、以及进程运行的环境变量配置等。内核版本最好是3.8以上,这与内核对Namespaces的支持有关。目前除User Namespace没完全开放以外,其他五个Namespace都是默认开启的,通过Clone系统调用进行创建。

Docker化的利与弊

前面说了这么多,应用Docker化能给应用能带来什么切实的好处呢,主要有下面几点:

由于不需要启动内核,在应用扩缩容的时候能做到秒级快速启动。

  • 一键启动所有依赖服务,测试再也不用为搭建环境犯愁了(编排服务,要啥服务直接指定),PE再也不用为建站复杂担心了(镜像只读,不存在变数)。
  • Docker命令简单、易用(实际情况是有图形化控制台不需要知道如何启动,docker run -d --net=host -c 4 -m 8g image),社区十分活跃,Bug提交一周之内就统统修改好,周边组件丰富。
  • 镜像增量分发,由于采用了Union FS, 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念,每次发布只传输变化的部分,节约带宽。
  • 由于直接使用宿主机内核调度资源,性能损失小。
  • 通过Docker能实现物理机的冷迁移(docker commit 、docker push)。
  • 动态CPU、内存资源调整(能绑核,能Share,多种模式可选)。
优势:

  • 使用的人再也不用担心如何搭建服务,清理还原服务,镜像一次固化,随处使用,多个应用版本可以并存在机器上,想发哪个版本就发哪个版本,想用哪套环境就Run哪个镜像。有人问打包发布麻烦不,Jenkins已经支持自动化构建镜像。
  • 测试、生产环境高度一致(数据除外),以前无法准确获取客户环境的问题不复存在(原先要去现场排查问题,成本高),开发再也不能说Bug无法复现了(仅限环境问题啊)。
  • 应用的运行环境和宿主机环境无关,完全由镜像控制,一台物理机上部署多种环境的镜像测试,想上下哪个环境哪个应用,就是Run和Stop的事。
  • 能实现秒级快速回滚,先把老版本Pause,新版本Run起来,这时发现新版本有毒,Stop掉新版本,Unpause老版本恢复服务。CRIU作为容器检查点保存与恢复(通常也称为热迁移)的解决方案,通过Libcontainer把一个正在运行的进程状态保存到磁盘上,然后在本地或其他机器中重新恢复当前的运行状态。
  • 在高性能计算的场景中,容器热迁移可以保证运行了许多天的计算结果不会丢失,只要周期性的进行检查点快照保存就可以了。
劣势:

  • 缺乏成熟的开源资源调度、集群管理产品,目前的K8s自成体系,性能问题,使用成本高,Swarm作为官方推荐,功能相对单一,Mesos使用太复杂。
  • Engine发展速度快、平滑升级困难,一般不支持业务无感知升级。
  • 上文提到隔离与安全的问题,这里不再详述。
  • 网络资源管控还稍微差一些。
  • 容器中加载、卸载内核模块会影响其他容器。
  • 无法像qemu那样模拟嵌入式系统运行环境。
  • Docker的App Container体系对现有运维体系产生巨大的冲击,成为企业级推进的主要困难。
曲折中前进的Machine Container

Docker的App Container体系对现有运维体系产生巨大的冲击,成为企业级推进的主要困难,Docker落地过程中现有的OS、应用、日志、监控、运维,发布体系由于有历史包袱,改造起来有些成本,很难推动全部升级,经过多次权衡,最终采用了Machine Container的模式,几种模式的对比如图所示,这里不展开说了,都是泪。

Docker三剑客与集群管控



Docker Machine大大简化了Docker主机部署的复杂度,极大地方便了开发者管理分布式Docker主机,而且Machine对部署平台进行了抽象,支持本地和云上的部署工作。

Docker Compose它是一个定义及运行多个Docker容器的工具。使用Docker Compose你只需要在一个配置文件中定义多个Docker容器,然后使用一条命令将多个容器启动,Docker Compose会通过解析容器的依赖关系并按先后顺序启动所定义的容器。

Docker Swarm是针对Docker容器的集群管理工具,能够管理资源,并可以按需在集群中加入和移除容器,实现优化主机资源利用率和提供故障转移服务。

Dcoker Swarm对外提供的是完全标准的Docker API,因此任何使用Docker API与Docker进行通讯的工具都可以完全无缝地和Swarm协同工作。用户可以通过标准的Docker镜像来安装Swarm,无需外部的其它依赖。

集群管控方面重点说一下Swarm。

  • 资源管理方面,Swarm通过标准的布局策略考虑资源容器的需求,并且利用可用的主机资源组成的集群去优化布局。
  • 容错调度方面,Swarm可以在宿主机故障时重新调度容器,保证服务的高可用。
  • 高可用性方面,Swarm支持主机选举,如果一个Swarm主机宕机,另一个主机将成为主,集群上的容器调度将不会中断。
  • 调度算法方面,目前支持三种算法,一种是BinPack模式,这种模式下容器集中部署在一台宿主机,尽可能的提高单机资源利用率,同时也增加了宕机影响面,第二种模式是Spread,这种模式是根据集群负载把容器相对均匀的部署在集群的不同节点上,第三种模式就是随机部署模式。
Docker的发展方向

总结起来就是三点:



1.高效便捷的运维

2.统一的部署方式

3.弹性的资源交付

Tips

  • 容器技术不是万能的。
  • 微软也进军这个领域,Win10都能跑Linux Bash了。
  • 树莓派+Docker,会不会以后分布式应用都跑在手机上?
  • Go语言和Docker互相成就。
本文为云栖社区文章,如需转载,请注明出处,并附上云栖社区微信公众号:yunqiinsight。

点击“阅读原文”可查看我们的云栖社区。


    关注 云栖社区


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册