时至今日,企业IT领域中最主要的问题在于如何解决不同功能部门之间的固有摩擦。一方面,业务线的主要诉求是在市场上实现差异性竞争优势,其具体体现包括更出色的应用程序、客户服务以及始终领先于竞争对手的实际方案。而在另一方面,大家还拥有自己的IT运维和安全团队,他们的任务则主要集中在可用性、安全性、合规性以及相关治理工作层面。在多数组织成员看来,容器技术能够切实解决双方之间所存在的各类矛盾。容器机制将带来一场革命性的转变,最终让IT组织中的这两大阵营真正实现顺畅对接与通力协作。 在本文博客中,我将向大家讲解: •容器加速应用交付 •何谓Linux容器 • Linux容器的优势 • 对容器的误解 • Docker与Kubernetes概览。
发展阐述
Google云平台的高级软件工程师Joe Beda在Gluecon上做了一个关于Google如何使用Linux容器技术的报告。他在报告中声称现在Google所有的应用都是运行在容器中的。这导致的结果就是Google每周要启动超过20亿个容器,每秒钟要启动超过3000个容器,这还不包括那些长期运行的容器。
Google自从2004年起就已经开始使用容器技术了,于2006年发布了cgroups,并在去年创建了名为Let Me Contain That For You (lmctfy)的项目。Imctfy是Google开源版本的容器栈,它提供了用来代替LXC的Linux应用容器。当在单台机器上运行多个应用时,这些容器支持应用间的资源隔离,而且这会给人感觉这些应用是独自占用机器而运行的。这些应用可以知道容器的存在,这样它们就可以创建和管理它们自己的子容器了。
应用案例
进入2016年以后,容器技术早已经从最初的牛逼满天飞到了脚踏实地的大规模铺开。很多企业都已经在实际项目中或深或浅的使用着容器技术,享受到新技术带来的简洁和高效。作为国内最早研究和使用Docker技术的企业,ThoughtWorks在2013年底就在实际项目中将Docker用于测试环境的资源复用,并在之后的许多项目中逐渐总结出许多有用的实践和经验。在这篇文章里,我将聊聊Docker在我经历过项目中的一些比较有代表性的运用场景。
现实中的容器技术运用方式非常广泛而灵活,时常让人觉得脑洞大开,概括来说是『可小可大,可远可近』。下面用四个案例来阐示容器在非特定领域里的运用场景。
容器之小:小而美的容器DevOps架构栈
图1基于容器和DevOps理念的运维架构
这张架构图来自于一个规模不到20人的小型产品团队,团队的结构十分精巧,由两名开发人员兼任主要的运维工作。这两位开发人员,花了几周时间通过Ansible陆陆续续搭建起了这套由上百个服务器节点组成的集群,并由团队所有开发人员共同维护。整体套集群系统高度自动化,使得团队的每个人都能够十分快速而安全的完成业务功能的部署、获取线上业务的运行状况、以及对出现问题的故障点进行快速的日志错误定位。
麻雀虽小,五脏俱全。这套技术方案包含了集群管理、网络管理、服务发现、日志管理、性能监控等许多方面的设计,从架构的角度上看,已经俨然是一个小型私有PaaS平台。
Swarm作为集群的管理工具,具有与Docker原生命令良好的一致性,在学习曲线比较缓和,在DevOps文化比较好的团队中很容易让开发人员快速上手。在这个架构方案中使用了Consul作为集群元数据存储的方案,Swarm的主、从节点信息以及Docker的跨节点网络划分的信息都存放在这里。Consul除了作为集群信息的存储,还可以用于应用服务的配置存储和服务发现,以及作为内网的DNS服务使用。不过出于安全性和可维护性的考虑,应该为应用服务单独搭建独立的Consul节点,与存储集群配置的Consul分开,防止由于数据干扰和意外修改引起大规模系统故障。
使用Swarm的另一个潜在好处是它能够充分利用Docker内置的跨节点网络功能,这套基于VxLAN的SDN实现十分简洁易用,通信效率也很不错。
容器集群的的性能监控和日志管理是使得这个小团队得以驾驭比团队人数更多的服务节点的关键要素,任凭运行的服务在机器漫漫海洋中随意穿行,这两件工具就是开发人员的罗盘和风向标,在关键时候为线上故障的定位争取宝贵时间,并能从中迅速找到每个服务当前运行的节点,从而采取必要的应急措施。cAdvisor+Influxdb+Grafana是一套为容器集群性能监控设计的开源解决方案,利用cAdvisor对容器信息的良好监控能力,Influxdb对时间序列数据的快速检索能力,以及Grafana的强大图表展示能力,形成性能数据的实时查看和历史回溯,并反馈到开发和运营的状态报表,形成完整闭环。不过,这个开源组合的缺陷在于缺乏现成的事件告警组件,在Influxdata公司的Telegraf项目逐渐成熟后,可以考虑使用它替代cAdvisor的功能,然后集成Kapacitor作为告警模块,提前预知服务的不正常状态。日志管理方面,这套系统使用了当下最主流的容器日志开源工具组合Fluentd+EslasticSearch+Kibana,在《程序员》2016年6月刊『容器的性能监控和日志管理』一文中已经对这个组合进行过比较深入的探讨。
这是Docker集群化实践中运用得比较出色的一个案例,特别是对中小型产品团队,会有不少可借鉴和启发之处。在不用增加额外运维人员的情况下,这套系统可以比较轻松的扩容至几百上千的规模。然而,这个架构本身并没有考虑譬如多数据中心、租户隔离、访问授权、资源配额等复杂情景,它主要的设计初衷在于解决集群易用性的问题。试想在过去使用虚拟机管理服务的时代,让只有几个人的团队去维护上千个计算节点上运行的需要各种不同环境和配置的服务,这简直是不可完成的任务,然而通过容器化的部署、DevOps思维的团队、加上适当的集群辅助工具,他们做到了。
容器之大:大型任务集群的容器化调度
图2基于容器的多数据中心任务平台架构
并不是所有的团队都愿意从头构建自己的整套运维架构和基础设施环境。在许多企业里,服务的运维管理是有专门的组织负责的。这些组织可能叫做平台部门、运维部门、或者环境支持部门,不论称呼如何,这些组织以及部门通常都需要管理数量相当庞大的计算资源。这些资源可能是跨机房,跨城市,甚至是分布在欧洲、美洲、非洲并且相互无法直接通信的数据中心里。他们所需要调度的作业数量和种类也远远超过一个自运维产品团队所需要考虑的规模。
为这样的组织设计基于容器的任务调度平台需要对企业的需求和特定业务领域有充分的了解,越是大型的基础设施集群,所需要应对的风险和不确定也越大,设计一个面面俱到的通用大型集群也越困难。因此针对具体业务场景做出一定的取舍是不得已、但又是必要的。例如为了获得较高的响应速度而将集群划分为多个互不重叠的调度区域,因而限制了每个区域的容量;为了避免内网数据网络风暴而将节点数据分层处理并逐级减少数据汇总的维度,因而增加监控管理复杂度;或者为了增加系统规模而采用高度聚合而不适合多数据中心的方案。这些方案往往不需要具备普适性,而是会针对特定企业和业务场景进行恰到好处的修剪和优化。
上面图中展示的是一个企业PaaS服务平台的结构,架构基于Kubernetes集群,需要应用在多个异地数据中心,并在统一的部署系统上对服务进行管理。由于单Kubernetes集群容量有限,这个方案实际上根据地域划分和租户的规模构建了多个几十到上千节点不等的子集群,集群直接互不重合,属于同一个任务组的服务只会在特定的某个集群内进行部署和调度,其实就是将集群和租户进行了绑定。在所有集群之上,通过自研的一个任务分发服务作为所有调度任务的入口,在这里处理服务的依赖关系、所属区域、以及其他元数据信息,然后调用Kubernetes的API完成任务的部署和调度,并通过额外的组件处理网络、存储等资源的配置。
在图中省略了系统采用的其他自研模块,值得一提的是这个系统的性能数据管理使用了开源的Promethus软件。Promethus是SoundCloud公司维护的一款包含信息采集、处理、分析、展示和告警的性能监控整体解决方案,它提供了比较灵活的多数据中心级联能力和集中式的配置管理功能,因此特别适合规模较大的计算集群。不同于前一案例中Influxdb方案每个数据采集节点发数据给存储数据的中心节点的方式,Promethus的性能数据采集是由中心服务器主动向所有节点定时轮询的方式拉取的,因此所有与数据采集相关的配置全部在中心服务器上进行修改即可。而节点的数量和IP地址变动则通过服务发现机制来告知中心服务器,这大大简化了修改数据收集参数的流程。
这个案例是一个比较典型的大规模容器集群,在大型容器集群方面许多企业都有着自己的实践沉淀。其中有两个比较明显的特点是从业务场景制定架构和系统中包含许多自研的组件,因此在借鉴的时候更需要广泛的收集信息,避免盲目照搬。
容器之远:基于容器的持续集成实践
图3基于容器的持续交付流水线示意
接下来,让我们用广角镜头来审视一下软件发布的生命周期。通过持续交付的流水线,我们能够清晰的定义出软件从代码提交到上线发布之前所需要经过的每个环节,协助开发者发现工作流程中存在的瓶颈,并促使团队提升端到端的自动化程度,缩短独立功能上线的周期。
那么容器在其中能扮演什么样的角色呢?首先是资源的隔离,为了确保每一次编译和测试的独立性,软件应该在干净的环境中分别进行构建、打包、并运行测试用例,而容器是非常合适用来提供这种虚拟环境的轻量级工具。其次是一致的软件打包方式,Docker的封装意味着不论运行的服务是用Java、Python、PHP还是Scala、Golang,平台可以用几乎相同的方式去完成部署,而不用考虑安装服务所需的环境,这些都在软件开发的时候就已经准备好了。最后是成熟的调度平台。基于容器有许多现成的任务调度框架,也正是由于前两个角色,容器使得任务的分发变得容易,由于应用不需要依赖主机的配置,这就让任务的灵活调度成为可能。
基于容器的持续交付流水线和普通交付流水线很相似,包含构建、打包、测试、部署等环节。同时这其中也有许多技巧和专用于容器的优化手段。这个案例中我们选取其中两个比较具有启发性的来说。
第一个例子是关于容器构建的优化。容器的构建通常都是由某个基础镜像开始,通过Dockerfile的描述自动化逐步执行,直至完成预期的状态。几乎所有项目的Dockerfile都不会每次从一个原始的Ubuntu或者CentOS的镜像做为基础,从头构建整个运行环境,因为那样会使得每次构建花费非常长的时间。制作用于继承的公共基础镜像是早已世人皆知的镜像构建提速优化的方法,这样可以让费时而又不常改变的步骤固定下来,每次构建时候就只需要基于这个镜像再进行增量修改就可以了。但这种方法其实也有潜在问题,那就是当我们需要升级基础镜像的时候,不得不重新构建所有基于它制作的所有服务镜像。
这个问题被称为『脆弱的基础镜像』,该问题的应对策略有很多。例如简单的延迟子级镜像的升级时间,直到每个子镜像下次重新构建发布时自然会获得更新。又例如比较激进的方式,通过流水线建立镜像的依赖关系,在父级镜像一旦更新时,自动触发所有子级镜像的自动重建,这种方式要慎重采用,因为它很可能会导致同时产生大量的镜像构建任务,对网络和磁盘造成严重的压力。那么,有没有在一种办法既能获得具有时效性的更新,又不会产生短时间内的构建风暴呢?其实对于一些场景是可以有取巧方法的,通过Docker的外挂存储能力,将经常可能变化的内容做成单独的镜像,然后利用Docker的『–volume-from』参数在服务启动时覆盖掉运行容器的特定目录。典型的场景就是用于编译其他服务的容器,这些容器中一般都会有一些编译服务时所需的时依赖库,这些依赖库随着项目所需依赖的变化也要跟着变,像Maven的~/.m2/repository目录,Node的全局node_module目录等就很适合这样管理。当这个目录下面的内容需要更新时,只需重新构建提供目录内容的一个镜像,而不会产生镜像构建的链式反应,服务下次启动时候就会获得新的依赖库目录了。
第二个例子是流水线中的测试环节。进行自动化测试的时候,容器的优势发挥尤其明显。对于外部服务的依赖,比如与数据库相关的测试,由于测试过程需要反复运行,过去时候,如果测试运行完没有正确的清理留下的数据,特别容易影响后续测试的运行结果。容器恰恰是提供这种即用即弃基础设施最佳的方式,完全可以在测试脚本中先启动一个全新的MySQL服务,然后测试完就销毁,保证了每次测试的独立性。关于这方面的应用在下个案例中再介绍更多细节。
类似的技巧还有很多。持续交付流水线是最能体现容器在软件领域带来各方面改进的大观园。许多现成的工具可以最大化的避免手工操作对流程的干扰,让软件发布开上高速公路。
容器之近:容器在自动化测试平台的运用
图4基于容器的自动化测试平台架构
最后这个案例是一个针对软件自动化测试环节的容器化基础设施设计。它是软件持续交付流水线上的一个重要环节,让我们带上长焦镜,近距离审视容器在软件测试场景中能解决怎样的问题。
容器快速启动、快速销毁的特性与软件测试时所需的每次干净独立的临时运行环境十分匹配。使得在这方面容器可以大有作为。特别是在集成测试和功能性测试的阶段,被测系统的运行往往会需要涉及多个要独立运行的子组件或子模块。还有外部模块的依赖,如果进行的是界面相关的测试用例,往往还会用到Selenium和浏览器的组件。而运行数据库相关的测试则会需要MySQL、Mongodb等组件。手工为每个测试用例准备并维护这些环节依赖是十分让人抓狂的事情。过去做这类测试时候为了解决依赖问题,通常做法是额外部署一套专用于测试依赖的环境,所有模块测试需要别的模块时都统一指向这套测试环境作为目标。由于过于频繁的升级这个依赖环境可能会打断正在运行的测试用例,因此只能对它进行定期的更新,这种无形中限制了的时效性和可靠性。
特别是一些比较重要并且耗时较短的回归测试和冒烟测试用例,理想情况下应该在每次代码提交后都全量的更新并执行,以便第一时间发现一些潜在的功能缺陷。但为每次提交创建一套测试环境不论是手工操作还是过去基于虚拟机的自动化方式都过于繁琐。
案例中的测试平台正是意图通过容器和简单的依赖描述,来解决测试环境管理的问题。它基于所有被测组件和所依赖的组件都使用Docker镜像来提供的前提之上,将所有组件抽象成一致的模型写成描述文件,描述文件的主要内容就是整个测试环境所需的镜像和启动顺序。
示意图中的『运行调度器模块』是接入持续交付流水线的调用入口,可以采用譬如Jenkins的形式插件实现,它用来创建和保存特定测试用例所需的环境描述文件内容。在流水线触发该测试环节时,这个模块调用『测试执行器模块』,将描述模型用特定的结构体传递给后者,后者解析这个数据模型,转化为接近Kubernetes服务模型的形式,然后在『服务依赖管理模块』的协助下,通过Kubernetes创建临时的Namespace,并依次创建每个服务。当测试环境就绪后,『测试执行器模块』就开始执行测试用例,最后又通过『服务依赖管理模块』通知Kubernetes销毁整套环境。
整个过程对于平台的用户而言,仅仅是增加了一个测试环境描述的内容,写在持续交付流水线测试步骤的定义(例如Jenkins插件配置)里。而这套系统内部颇为复杂的执行过程,能够有效的利用整个集群的资源,恰到好处的为测试的过程提供支持。
总结
这四个案例由浅入深、由远及近的展现了容器在现代软件和基础设施设计中举足轻重的作用。有些技术会直接改变人们的生活,而另一些技术则会改变技术本身以及技术的发展方向,容器技术属于后者。
随着容器运用的普及,当下的主流媒体对容器周边技术的关注还在持续升温。不仅是《程序员》推出了本期的容器技术专刊,在最新一期的ThoughtWorks公开刊物《技术雷达【1】》中,容器和Docker相关的关键词同样占据了大量版面。在越来越多的技术领域里,无论是移动设备、物联网、大数据,都能看到容器技术各种形式的延伸,作为现实容器运用的一道缩影,此文可作为窥斑见豹、抛砖引玉之用。
参考链接:
【1】https://assets.thoughtworks.com/assets/technology-radar-apr-2016-cn.pdf
Docker&MeSOSDocker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是,他们不依赖于任何语言、框架包括系统。 在docker的网站上提到了docker的典型场景: Automating the packaging and deployment of applications Creation of lightweight, private PAAS environments Automated testing and continuous integration/deployment Deploying and scaling web apps, databases and backend services 由于其基于LXC的轻量级虚拟化的特点,docker相比KVM之类最明显的特点就是启动快,资源占用小。因此对于构建隔离的标准化的运行环境,轻量级的PaaS(如dokku), 构建自动化测试和持续集成环境,以及一切可以横向扩展的应用(尤其是需要快速启停来应对峰谷的web应用)。 构建标准化的运行环境,现有的方案大多是在一个baseOS上运行一套puppet/chef,或者一个image文件,其缺点是前者需要base OS许多前提条件,后者几乎不可以修改(因为copy on write 的文件格式在运行时rootfs是read only的)。并且后者文件体积大,环境管理和版本控制本身也是一个问题。 PaaS环境是不言而喻的,其设计之初和dotcloud的案例都是将其作为PaaS产品的环境基础 因为其标准化构建方法(buildfile)和良好的REST API,自动测试和持续集成/部署能够很好的集成进来 因为LXC轻量级的特点,其启动快,而且docker能够只加载每个container变化的部分,这样资源占用小,能够在单机环境下与KVM之类的虚拟化方案相比能够更加快速和占用更少资源 虚拟化是一个广义的术语,在计算机方面通常是指计算元件在虚拟的基础上而不是真实的基础上运行。 虚拟化,原本是指资源的抽象化,也就是单一物理资源的多个逻辑表示,或者多个物理资源的单一逻辑表示。具体到服务器虚拟化,就是多个物理资源的单一逻辑表示。 虚拟化技术可以扩大硬件的容量,简化软件的重新配置过程。CPU的虚拟化技术可以单CPU模拟多CPU并行,允许一个平台同时运行多个操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。 在实际的生产环境中,虚拟化技术主要用来解决高性能的物理硬件产能过剩和老的旧的硬件产能过低的重组重用,透明化底层物理硬件,从而最大化的利用物理硬件。
以 Docker 为代表的容器技术一度被认为是虚拟化技术的替代品,然而这两种技术之间并不是不可调和的。作者分别列举了容器技术以及虚拟化技术的优缺点,并提出将两者结合取长补短的解决方案。
容器为应用程序提供了隔离的运行空间:每个容器内都包含一个独享的完整用户环境空间,并且一个容器内的变动不会影响其他容器的运行环境。为了能达 到这种效果,容器技术使用了一系列的系统级别的机制诸如利用Linux namespaces来进行空间隔离,通过文件系统的挂载点来决定容器可以访问哪些文件,通过cgroups来确定每个容器可以利用多少资源。此外容器之 间共享同一个系统内核,这样当同一个库被多个容器使用时,内存的使用效率会得到提升。
对于系统虚拟化技术来说,虚拟层为用户提供了一个完整的虚拟机:包括内核在内的一个完整的系统镜像。CPU虚拟化技术可以为每个用户提供一个独享且和其他用户隔离的系统环境,虚拟层可以为每个用户分配虚拟化后的CPU、内存和IO设备资源。
通常来说,这取决于你的需求。如果你只是希望将应用运行的实例进行隔离,那么对于管理应用运行环境、启动应用实例以及控制资源 开销方面容器将是一个极为高效的工具。像Docker这一类的容器,其设计原则就是为了解决这种应用环境的修改以及应用部署的问题,并且这十分符合 DevOps理念(你可能希望知道更多关于DevOps理念的内容)。
如果你从服务器虚拟化的角度来寻找最好的环境隔离方案,那么系统级的虚拟化是更好的方案:和容器相比,邻居租户(Noisy neighbours )对系统的影响在虚拟化的方案下将不是一个问题。尽管现在很多容器都在专注于提高其隔离能力,但是虚拟机的隔离还是要优于容器。从物理服务器过渡到虚拟服 务器是一个很自然的过程,并且现在针对虚拟服务器的管理的生态系统也很完善。
在z系统中,Linux具有很好的伸缩性(运行容器),但是z是一个极度高效虚拟化(运行虚拟服务器)的平台,它继承了整个系统架构。尽管没有精确的测量,将虚拟化和容器技术相结合,在z系统中会比其他平台要容易。
还有第三条路:两者兼而有之。
有多种方式可以将系统虚拟化及容器技术相结合:
1.一个容器中运行一个虚拟机
Docker在部署容器方面十分灵活。其中一个选择(execution driver)是利用KVM镜像。这样就可以在最好的隔离性情况下发挥DevOps所擅长的使用Docker各种方式。但是这也付出了需要在启动容器时启 动整个操作系统实例的代价。这也就意味着较长的启动时间以及低效的内存使用,只能通过内核共享内存(KSM)来提升内存利用率。这种方法效果和效率都不理 想,但是这是一个好的开始。
2.一个虚拟机中运行一个容器
与之相反的,你一可以在虚拟机中启动一个容器。这里的虚拟机并不是由Docker控制,而是通过现有的虚拟化管理设施来控制。一旦系统实例启动, 就可以通过Docker来运行容器而武器其他特殊的设置。同时,由于不同容器运行在不同的虚拟机上,容器之间也能有很好的隔离。而内存的使用率需要通过虚 拟层的内存共享来提升。
2b.一个虚拟机中运行多个容器
对于多租户的情况,可以用另一种形式在虚拟机中运行Docker。这种情况下,我们假设在不同租户的容器之间需要强隔离,而对于同一用户的不同容 器,简单的Linux容器隔离已经足够。这样我们就可以在减少虚拟机个数的情况下保证租户之间的隔离,同时可以利用Docker带来的各种便利。
总结来说:需要根据实际需求进行选择。
1、企业级Docker镜像仓库的管理和运维 - 极客头条 - CSDN.NET http://geek.csdn.net/news/detail/109110
2、Docker+Rally实现OpenStack性能测试自动化 - 极客头条 - CSDN.NET http://geek.csdn.net/news/detail/107981