关于云原生的一些思考


第一部分:定义

在Craig和我开始打造Heptio的时候,我们做了不少关于我们行业发展方向的思考。我们在Google里呆了很久的时间(两个人加起来的日子有16年了),对于Google构建和管理系统的方式有很深入的理解。但是很可能你没有在Google任职的经历。那么对于一个普通的公司、开发或者运维人员,应该如何理解并应用这些不断演变的新概念呢?

云原生意味着什么,并没有一个固定或死板的定义。实际上,还有一些与之重叠的概念和思想。但在它云原生的核心,是对团队、文化和技术的精心组织,利用自动化和架构的力量来掌控复杂度,突破速度的束缚。在这种模式下的运作,不但是一种技术的扩张方式,更是一种人力的扩张方式。

很重要的一点是,不一定要在云环境中运行才能叫“云原生”。这其中的技术可以在恰当的时候叠加地采用,并且帮助向云过渡的过程变得顺畅。

云原生的真正价值所在远不止与之密切相关的一篮子技术。要真正的理解行业的动向,我们需要思考我们能从哪些地方用什么途径让公司、团队和个人更加成功。

目前,这些技术已经被那些以技术为中心且眼光高远的公司验证,它们为此做出了努力并投入了大量的资源。想想Google、Netflix或者Facebook。同样也有规模更小,灵活性更高的公司实现了价值。然而,除在这些技术的早期采用者之外,这套哲学理念却少见有利用。 当把目光投射到整个IT领域,我们仍然在这条旅程起点不远的地方。

随着早期的经验不断被验证和分享,如今都有哪些涌现的主题呢?

  • 更有效率和更开心的团队。云原生的工具让大的问题切分成小的问题,便于分工让更专注和更敏捷的团队来解决。

  • 减少枯燥重复的劳动,通过将很多人工操作自动化,也避免了因为人工操作引发的运维上痛苦和停机时间。这一般是通过自愈合、自管理基础设施来达成。系统被期望解决更多的事情。

  • 更可靠的基础设施和应用。构建用来处理一些可预见的故障的自动化流程,通常对不可预见的事件和故障是更好的失效模式。举个例子,如果部署开发的应用只需要一个命令或者点击一下按钮,那在灾难恢复的场景下(通过手动或自动的方式)进行自动化部署也更加的容易。

  • 可审计,可察视,可调试。复杂的系统会变的非常晦涩难懂。云原生使用的工具,通常能让人对应用内部发生的事情有清楚的了解。

  • 深度的安全性。如今很多IT系统的外壳都很坚固,但内在都很脆弱。现代的系统默认情况下应该有安全防护,采取最小信任(least trust)策略。云原生让应用开发者能在应用安全方面扮演重要的角色。

  • 资源的更高效的利用。应用和服务的自动化式的、类云环境的部署及管理方式,开启了自动化的算法化机遇。比如,一个集群调度器/编排器可以自动完成工作负载的安放,而不需要运维团队用类似excel的方式进行管理。


在Heptio,我们特别兴奋我们能帮助更广阔的IT领域得到云原生的恩惠。在接下来的部分,我们将讨论与已有的系统进行整合,DevOps,容器和编排,微服务和安全。

第二部分:实践

与任何正在经历变革的领域一样,云原生世界中有繁杂的概念。上一个部分列举的概念,不是每人个人都清楚应该如何恰当地利用。同时,很多关键的项目,要么太庞大,要么太重要,不适合重头重写。因此,我们觉得最好把这些新的结构用在新的项目或者老项目的新部件中。在系统老的部件得到了改善后,然后再花时间合理地学习并采用其他新技术。寻找把新的特性或者系统分解成微服务的方式。

没有硬性的规则。每一个机构都是不同的。软件开发的实践必须要根据身边的团队和项目来做出调整。理想不同于现实。有的项目经得起实验折腾,但是很多非常重要的项目应该采取更加谨慎的态度。还有一些场景是介于两者之间的,一些技术即使已经被验证过,但仍需要规范化并且经过大规模的测试,之后才能应用到核心系统上。

云原生的定义中离不开的更好的工具和系统。没有这些工具链,每一个新部署在生产环境的服务都会有很高的运维成本。监控,跟踪,配置等等都增加了一个处理的负担。这个额外的开销是对微服务大小的切分应该合理的主要原因之一。开发团队的速率和在生产环境中运行更多(服务)的成本,这两者的利弊需要权衡。类似的,新的技术和语言引入,尽管可能新鲜刺激,也必须仔细考虑其风险和代价。关于这个话题,Charity Majors有一个非常好的演讲

要降低构建和运行新服务中的运维成本,自动化是关键。像Kubernetes、容器、CI/CD、监控等等这样的系统,都有一个相同的重要目标 - 让应用开发和运维团队的效率更高行动更快,让打造的产品更可靠。

要达到云原生的愿景,最好使用新一代的工具和系统,而不是传统的配置管理工具,因为它们有助于分解问题然后分工给不同的团队处理。通常来讲,新的工具通常能让独立的开发和运维团队拥有自主权,然后通过自服务(self service IT)提高生产力

第三部分:DevOps

不妨将DevOps看成一种文化的转型,开发者现在需要关心他们的应用是怎样在生产环境中运行的。而运维也对应用的运作机制有了认识并且授予了知情权,从而可以在帮助打造可靠应用方面发挥重要作用。增进这些团队之间的理解和建立同理心是关键。

但是可以更进一步。如果我们重新思考我们的应用的构建过程和运维团队的结构组成,我们能进一步加深这种关系。

Google没有传统意义的运维团队。相反,Google定义了一种新的叫做SRE(Site Reliability Engineer,网站可靠性工程师)的工程师。他们是技能扎实的工程师(报酬同其他的工程师处同一个水平)他们不但随时保持在线,同时得到授权并被赋予通过自动化来在推动应用变得更加稳固方面扮演至关重要角色的重望。

当在凌晨两点报警触发的时候,任何响应该报警的人都会做同一件事情 - 努力弄清出了什么问题,好早点回去继续睡觉。真正定义一个SRE的地方是第二天早上10点钟发生的事情。运维组的人是否只会抱怨,还是会和开发团队一起协作保证同样的报警不会再次出现?在让应用变得尽可能稳定可靠方面,SRE和开发团队有一样追求。结合不追责的的事后剖析,可以保持项目的健康,不让技术债务堆积。

SRE在Google是最被看重的人群之一。在实际中,很多项目在没有SRE参与启动的时候,开发团队肩负了让他们的产品在生产环境中运行起来的期望。引入SRE团队的流程,通常需要开发团队向SRE团队证明他们的产品已经准备到位。开发团队需要已经做好了所有的准备工作,包括设置好监控和报警,报警的应对策略和发布流程。开发团队要能显示出报警的情况已经达到了最少的程度,并且绝大多数的问题已经被自动化了。

随着运维的角色参与程度更深,与特定的应用相关度更高,让一个运维团队掌控整个运维栈变得不合理。这引出运维规范(Operations Specialization)的问题。从某种意义来说这是一种
“反DevOps(anti-devops)”的做法。让我们来自下而上的看:

  • 硬件运维。这一层很显然可以分离出来。实际上,很容易把云IaaS看成是“硬件运营即服务(Hardware Ops as a Service)”。

  • 操作系统运维。必须有人保证机器能够顺利启动,并且有一个好的内核。将这一部分从应用的依赖管理中分离出来也反映出了用来托管容器的操作系统发行版(CoreOS, Red Hat Project Atomic, Ubuntu Snappy, Rancher OS, VMWare Photon, Google Container Optimized OS)的最小化趋势。

  • 集群运维。在容器化的世界中,一个计算的集群变成了一个逻辑上的基础设施平台。集群系统(Kubernetes)提供了一组原语能让很多传统的运维任务自服务化。

  • 应用运维。每一个应用现在可以根据需要拥有一个专门的应用团队。若有必要,开发团队有能力并且应该担任起这个角色。这种运维团队应该更深入应用,因为他们不需要在其他的层次上专研太深。比如,在Google,AdWords的前端SRE团队会和AdWords Frontend的开发团队交流很多,超过他们与集群团队之间的交流。这能带来更好的成果。


很可能还有其他专业性的SRE团队的空间。例如,存储服务可能会划分成单独的服务,或者根据某种政策,可能有团队来负责验证所有团队使用的基础容器镜像。

第四部分:容器和集群

有很多人对于容器相关技术兴奋不已。了解为什么大家如此兴奋的的根本原因是有益处的。在我看来,有三个原因:
  1. 打包和移植性
  2. 效率
  3. 安全性


让我们依次地来看。

首先,容器提供了一种打包机制。这能让系统的构建从这部署流程中分离开来。另外,构建的成品和镜像比传统的方式如虚拟机移植性更好。最后,部署变得更加的原子化。传统的配置管理系统(Puppet,Chef,Salt,Ansible)很容易让系统处于一种配置不完整的、很难调试的状态。也很容易在机器中残留一些不易被发现的错误版本。

第二,容器更轻量化,使得资源利用率增加。这是当Google引入cgroups(容器底层的主要核心技术之一)时的一个主要原因。通过共享一个内核,并且允许更流体化的过量使用(fluid overcommit),容器更容易让系统资源每一部分都不浪费(“use every part of the cow”)。随着时间的推移,有望看到更加复杂的用来均衡单个主机上共存容器需求的方式,杜绝吵闹邻居问题。

最后,很多用户把容器看做是一个安全性界限。尽管容器可以比一个简单的Unix进程更安全,当把它们看做是一个硬的安全性边界的时候还是要注意。Linux命名空间提供的安全保证对于那些运行半信任工作量的“软”多租户环境来说合适,但对于那些运行充满敌意的工作量的硬多租户环境却不适用。

有从很多方面的努力来在模糊容器和虚拟机的界限。一些早期研究如unikernel很有意思,但是很多年内尚不能应用于生产。

要达到上述目标,容器尽管是一条简单的途径,但却不是必要的途径。例如,Netflix,传统以来一直运行一个非常现代化的技术栈,它们以类似使用容器的方式,来打包并使用虚拟机镜像。

尽管绝大多数围绕着容器的努力集中于在单个节点上管理软件,使之更加可靠和可预测,这个改革的下一步集中于集群(通常也被称作编排器)。给定一批节点然后把他们和自动化系统绑定起来,为开发和运维团队提供了一组逻辑基础设施的自服务

集群能帮助消除运维中枯燥乏味。有了容器集群我们让计算机来接管工作,决定负载应该由哪台机器来处理。集群也会默默的在硬件失效的时候修复问题,而不是需要去通知某人。

集群的第一个好处就是它启用了运维的规范(见第三部分)可以让应用运维作为一个单独的学科来努力。通过定义个以良好的集群接口,应用开发者们能集中于解决应用自身的一些问题。

集群的第二个好处是它让启动并管理更多的服务成为可能。从而允许使用能为开发团队高速率的架构(通过下一部分介绍的微服务)。

第五部分:微服务

微服务是一个已经存在很久的概念的一个新名称。基本上,它是一种将一个大的应用进行切分的方法,使得他们能独立地进行开发和管理。让我们看看此处相关的关键概念:

  • 强大和清晰的接口。服务之间的紧耦合必须避免。配套了清晰文档和有版本管理的接口有助于强化这种协定,对于这些服务的消费者和生产者同时又都能保有一定的自由度。

  • 独立的部署和管理。微服务应该能够单个更新,无需和其他的服务一起同步进行。同时,大家也都很希望能够轻松地回滚一个微服务的版本。这意味着部署的二进制文件必须在API上和任何数据格式方面保持向前和向后兼容。这可以作为检验运维和开发团队之间的合作和沟通程度的试金石。

  • 由内打造的耐受性。微服务应该构建成为并经测试验证有独立的耐受性的。在消费的服务不可用或者异常的时候,那些消费一个服务的代码应该要努力保持正常工作并且做出合理响应。同样的,那些提供的服务应该对于未曾预料的负载和异常输入做好防护。


确定微服务的大小是一个很难做对的事情。我的意见是要避免选择小的过分的服务(pico-services),反之将服务按照自然的边界(编程语言,异步队列,伸缩的要求)进行切分,同时保持团队大小合理(例如:两个披萨团队)。

应用的架构应该能允许以一种切合实际并且自然的方式增长。与其以20个微服务开始,不如从2到3个开始,然后随着该领域复杂度再对服务进行拆分。经常对一个应用的架构的认识直到应用在处于开发阶段才会变得透彻。这也说明了很少有已经竣工完成的应用,他们都总是一个正在施工的工程。

微服务是一个新概念吗? 非也。这其实是另外一种类型的软件组件化(software componentization)。我们过去把代码切分成库。这只是把“链接器”从一个构建阶段的概念转变成了一个运行时的概念(实际上,Buoyant有一个有意思的项目叫做linkerd,其基于Twitter Finagle系统。)。这与多年前的SOA潮很相似,只是不见了各种样式的XML。数据库从另外一个角度看,一直以来几乎是一个微服务,因为它实现和部署的方式都满足上面列的点。

约束可以转变成生产力。 尽管,很容易让每一个团队各自决定在每一个微服务上使用什么语言或框架,但不妨考虑下规范化,定下几种语言或者框架。这么做有助于机构内部的知识技术交流积累,更好应对人员流动。但是,在必要时也要有打破政策的开放心态。这是比起PaaS的垂直式集成结构,这个世界中的一个关键优势,换句话说,约束应该是来自政策层面的,而不是来自技术能力层面(constraints should be a matter of policy rather than capability)。

尽管绝大多数人把微服务看做是实现一个大型应用的技术,服务光谱(services spectrum)中还有很多其他类型的服务:

  1. 服务作为实现细节。正如上面描述过的一样,这对于把一个大的应用团队切分成小的开发和运维团队很有用。

  2. 成果共享,实例私用(Shared Artifact, Private Instance)。在这种场景中,开发过程通过服务的很多实例间共享。有可能有一个开发团队和很多运维团队,或者很可能一个联合的运维团队,在共享专门的实例间工作。很多数据库属于这种类型,很多团队都在使用同一个MySQL安装的私有实例。

  3. 实例公用。这种场景下,机构中的一个团队为很多应用或者团队提供一个共享的服务。服务可能会将数据或者操作按照用户(多租户)进行分区,或者提供一个用途广泛的简单服务(如展示一个通用的品牌条HTML,提供机器学习模型等)。

  4. 大型服务(Big-S Service)。绝大多数的企业不会打造这种的服务,但是可能会使用他们。这是一个典型的“硬性”的多租户服务,用来构建来为大量的分散的客户提供服务。这种服务需要某种层次的记账和加固,通常在企业内是不必要的。SendGrid和Twilio属于这一类。


随着服务从一种实现细节变成一种企业内常见提供的基础实施,服务网络从一个每应用的概念变成了能跨越整个公司的概念。允许这种依赖是一个既有机遇也有忧患。

第六部分:安全

注:本文中没有覆盖到新出现的“云原生”中与安全相关方方面面。但同时,尽管我不是一个安全专家,安全却是我整个职业生涯都有在关注的事情。请把它当做是值得考虑的事项清单的一部分。

安全仍然是云原生领域中的一个大问题。以往的技术无法完美的应用,在起初云原生可能看起来在开倒车。但在这片无畏的领域,同时也充满了各种机遇。

容器镜像安全

有很多的工具可以帮助对容器镜像进行审计,以确保它们完整地包含了各种补丁。众多选择中,我没有什么很强个人偏好的。

真正的问题在于,在发现了一个有漏洞的镜像之后该怎么做呢?这一块市场尚未提供很好的解决方案。一旦有漏洞的镜像被检出,事情就从一个技术问题变成了一个程序/流程问题。你会想着去找出机构中哪些组件受到了影响,问题应该在容器镜像层级树的什么地方进行修补,以及用什么好办法来进行测试并且发布新的打好补丁的版本。

CD/CD(持续集成/持续部署)是应对良策中的重要内容,因为它可以快速自动地发布新镜像。其与编排系统的集成能让你找出哪些用户正在使用有漏洞的镜像。同时它也能让你验证生产环境上是否采用了已经修复好的版本。最后,部署系统的策略能帮助阻止那些使用有漏洞镜像的容器启动(在Kubernetes的中,这被称作准入(admission))。

微服务和网络安全

但即使集群上所有运行的软件都已经是修复过的,也并不能保证网络中有没有不受信任的活动。

在以动态调度、存时短暂为特征的容器世界中,传统的基于网络的安全工具无法发挥理想的效果。存时短暂的容器可能存活时间过短,以至于传统的扫描工具无法及时扫描到。又或者被扫描到了且扫描报告已经生成,奈何相关容器却已经不存在了。

在动态的编排器中,IP地址不再拥有长久的意味,且它们是可以被自动复用的。解决方案是将网络分析工具和编排器集成,让逻辑名称(结合其他元数据)可以和IP地址一并使用。这可能可以让报警更加容易处理一点。

很多网络技术都利用了封装技术来实现“一容器一IP”。这可能会给网络追踪和诊断工具带来问题。如果生产环境上部署了这样的网络系统,那这些工具必须要做出相应改进。幸运的是,VXLAN、VLAN中的很大一部分都都已经被标准化了,或者不采用封装或者虚拟化,以便这些系统都能利用这些支持工具。

然而,个人看法,最大的问题还是跟微服务相关的问题。当在生产环境中运行了很多服务的时候,很有必要确保某特定的服务只能被授权的客户端调用。另外,因为有了IP的重复使用,客户端需要能知道调用的服务是正确的服务。大体上说来,目前这还是一个没有解决的问题。要处理这个问题,有两个(并不互斥的)方案。

首先,让实现主机级别的防火墙规则(在任何的容器之外)的网络系统和选项更灵活,以便能细粒度控制访问策略,来控制某些容器能被其他哪些容器调用。我把这个称之为网络的微隔离(network micro-segmentation)。这里的挑战之一在于在动态调度下如何进行策略的配置。尽管尚处于起步阶段,有很多的公司正在努力简化这一点,通过在网络中添加支持,或者同编排器协作,又或者过在处于高层次的应用中进行定义。一个很大的问题是,服务的使用范围越广,微隔离的效果就越小。如果一个服务有成百的调用者,“拥有访问权即意味着得到授权(access implies authorization)”的简单模型不再适用。

第二个方案是让应用在实现数据中心内的认证和加密中扮演更加重要的作用。这对于那些客户端众多、并且在大的机构中已经形成“软多租户”状态的服务适用。这需要有一个生产环境服务的身份系统。我已经开始一个叫做SPIFFE(Secure Production Identity Framework For Everyone,给大家使用的生产环境安全身份系统框架)的业余项目。这些理念已经在Google这样的公司得到了验证,但是还没有在其他地方施行。

安全是一个很深入的话题,这里肯定有很多威胁和注意事项没有涉及到,还需要持续的探讨。

到这里,我们的云原生系列文章就结束了。欢迎大家在Medium留下自己的想法,或者在Twitter上联系jbeda,cmcluck或者heptio。

原文链接:


翻译:钟最龙

0 个评论

要回复文章请先登录注册