云原生时代的DevOps平台设计之道
开发人员与运维人员是 IT 领域很重要的两大人群,他们都会参与到各种业务系统的建设过程中去。DevOps 是近年间火爆起来的一种新理念,这种理念被很多人错误的解读为“由开发人员(Dev)学习一大堆新的技能,从而掌握运维人员(Ops)该处理的事情”。然而能力越大,责任越大,当维持生产环境稳定为要位的运维责任落到开发人员的肩头时,多数程序员发出了 扯淡的DevOps,我们 开发者根本不想做运维! 的呼喊。那么在云原生时代,到底应该怎样达成 DevOps 的体验呢?我的观点是由平台工程来衔接这两大人群,各自做好各自领域的事情。
令人“厌恶”的DevOps
首先,我非常希望你能先看一看引言中提到的 扯淡的DevOps,我们开发者根本不想做运维! 这篇文章。这篇文章从亚马逊云科技社区参与负责人 Emily Freeman 的一条推特入手,观察了很多留言后,得出了文章标题这种类似咆哮一般的结论。从绝大多数回复这条推特的 IT 从业者的口中,我听到了对于将运维职责强加给开发人员这种 DevOps 体验深恶痛绝。
开发人员对于 “谁构建,谁运行” 这种大义凛然的话表示无感,对于学习运维领域的新技能,亦或是将自己加入轮班支持人员的行列都感觉力不从心;运维人员的本职工作被剥离之后,则对本专业的前景惶惶不安,会害怕运维团队的重新洗牌。
开发与运维,的的确确是两个不同的工种,有着类似“车床工与管道工”的区别。
开发人员 | 运维人员 | |
---|---|---|
专业技能 | 开发语言、开发框架、中间件、数据库 | 硬件、操作系统、网络、存储、虚拟化 |
日常工作 | 理解需求、开发文档写作 、开发代码 | 安装部署、监控、日志、问题排查、变更 |
文化标签 | 自由、创造 | 保守、责任 |
一些公司认为从表格中把大量的运维人员管辖的工作,一股脑的“左移”给开发人员就是 DevOps。在专业技能和日常工作领域带来的缺口,可以通过开发人员的勤劳学习加以补足,然而在文化标签领域的冲突,将会是导致开发人员厌恶这种 DevOps 体验的根本原因。
DevOps 的真意与平台工程
在我看来,DevOps 的真意是利用软件工程思维,解决复杂且繁重的运维问题。真正适合做 DevOps 工作的人,是具备一定软件工程能力的运维专家,在这里,对运维能力的要求更重要。
DevOps 工程师,可以通过设计或选择一款平台产品,来将复杂的运维工作抽象为产品化的运维特征。从这个角度上讲,开发人员将会是这个平台产品的用户,他们能够在不了解复杂基础设施的情况下,操作并维护应用程序。DevOps 工程师,应该是更懂开发人员需求的运维工程师。
在追根溯源,找到了这条推特之后,我了解到了更多 IT 业内人士对 DevOps 的看法,从中找到了很多和我有共鸣的声音。
To me that's a sign we haven't made ops intuitive/easy enough for most devs to be able to handle it.
对我来说,这表明我们还没有让运维变得足够直观/简单,以至于大多数开发人员都无法处理它。
—— @Liz Fong-Jones (方禮真)
The "platform" should do the heavy lifting ops, lacking a real platform the ops team (DevOps/are/platform team) is the platform. Devs can then focus on the application level operations of their apps using the knobs and levers provided by the platform.
“平台”应该做繁重的运维,缺乏真正的平台时运维团队就是平台(DevOps/are/platform team)。然后,开发人员可以使用平台提供的旋钮和杠杆专注于其应用程序的应用程序级操作。
—— @pczarkowski
IT 行业近年来的发展趋势,一直是不断以平台能力的提升,来解决复杂基础设施的使用问题的。最开始,程序开发人员需要面对的是一台物理服务器,在缺乏运维能力的情况下,会由运维人员处理有关服务器的一切,包括操作系统、网络配置等等。而到现在,程序开发人员已经很少需要跟服务器打交道,甚至我见过的很多程序员并不掌握任何有关命令行的知识,就可以面向服务器开发应用系统。这种转变让程序开发人员更加专注于业务代码本身,不必分神去做一些繁重且琐碎的运维事务。带来这种转变的,是处于发展过程中的平台工程,在让基础设施不断变得简单易用。
最原始的裸机时代,并没有开发运维之分。从底层基础设施,一直到最顶层的业务系统都是同一批人在处理,这一批老程序员可以被称为真正的全栈工程师。但毫无疑问,每一个开发人员,都希望能够抛却运维工作,更专注于自己开发的代码。
云计算时代的兴起以虚拟化技术为基础,软件定义基础设施变得炙手可热起来。运维人员 通过建设并维护一套 IaaS 云平台,将计算资源进行池化。开发人员按需申请自己需要的虚拟机,从而得到一个操作系统界面来进行交互。与操作系统打交道,对开发人员依然是个巨大的挑战,在 IT 领域,操作系统就像一座漂浮在海上的冰山,看似只露出冰山一角,然而其庞大的知识领域“身躯”都隐藏在海平面以下。和裸机时代相比较,开发人员和运维人员已经是两个完全不同的群体,开发人员已经可以将自己的大部分精力放在业务系统上了。值得一提的是,对操作系统的掌控是不折不扣的运维领域技能。
容器技术以及 Kuberentes 的横空出世,成为了云计算时代的分水岭。软件定义基础设施的技术手段已经被发挥到了极致,并且成为了现阶段运维人员的标配技能。运维人员通过建设并维护一套 PaaS 云平台,终于将操作系统这一座最后的“大山”从开发人员的身上搬开。开发人员可以将更多的精力放在业务系统上了,除了他们依然需要学习容器技术和 Kubernetes ,至少他们要学会如何面向 Kubernetes 编写业务系统所需的声明式配置文件。运维人员也通过 PaaS 云平台得到了自己想要的能力,容器技术和 Kubernetes 为他们带来了弹性、便捷性的巨大提升。
跟随时代的变迁,我得出了一个结论:从开发人员与运维人员的关系上来看,IT 领域的演变,就是运维人员通过不断向上接手开发人员眼中“跟开发无关的杂活”,来不断为开发人员减负。开发人员在得到了解放后,可以将视角更多的聚焦在自己开发的业务系统上,释放出自己的创造力。
那么跟随结论中的趋势,解放开发人员负担的脚步绝对不会停止。DevOps 的工作,就是以开发人员为用户群体,打造一套可以让开发人员毫无障碍的使用基础设施的“云原生平 台“。
云原生是一种面向云设计应用的思想理念,充分发挥云效能,组织内 IT 人员相互协作构建弹性可靠、松耦合、易管理、可观测的云应用系统,最终目标是提升软件交付效率,降低运维复杂度。相比上文中提到的 PaaS 平台,起码要能够避免开发人员去编写复杂的 Kubernetes 声明式配置文件。
现有开源产品情况
在云原生平台领域,已经有不少项目在深耕。在这里我列举了三个各具特色的云原生领域的平台级产品:Rancher、KubeSphere、Rainbond ,后续的具体设计思路中,也会关注已有产品的实现。
这三款开源产品中,Rancher 是元祖级容器管理平台,加入 SUSE 后,能够明显感觉在云原生生态领域不断发力,Rancher 在各个层次可以集成的云原生领域工具集已经非常丰富。Rancher 专注于帮助 DevOps 团队面对多集群情况下的运维和安全挑战,从这一点来说,Rancher 更偏向于运维人员的使用体验,而非面向开发人员提供更高的易用性。
KubeSphere 是来自青云的 “面向云原生应用的 容器混合云”。除了对 Kubernetes 集群内的各种资源的管理能力之外,Kubesphere 主打即插即用的插件式生态扩展能力。
Rainbond 由北京好雨科技出品,从其介绍来看,它是一款主打易用性的云原生多云管理平台。
降低业 务部署难度
诚实地讲,为现代开发语言开发而来的业务系统制作容器镜像并不是什么难以掌握的技能。但是不可否认的是,绝大多数 IT 从业人员依然会将制作镜像这件事情归为运维人员管理,而不是开发人员要关心的事情。
那么 DevOps 工程师就有必要考虑,如何在开发人员对容器技术一无所知的情况下,使之可以直接从代码开始部署业务系统。
在这个方面,Heroku 是无可争议的先行者。Heroku 是一个支持多种编程语言的云平台即服务产品。在2010年被 Salesforce.com 收购。 Heroku 作为最元祖的云平台之一,从2007年6月起开发,当时它仅支持 Ruby,但后来增加了对 Java、Node.js、Scala、Clojure、Python 以及 PHP 和 Perl 的支持。
开发人员在使用云原生平台时,只需要在界面中填写代码仓库的地址,对应的用户名密码等基础信息,就可以等待代码构建成为镜像,并自由的运行在 Kubernetes 云环境中去。
现有开源产品也在这方面给予了一定的支持:
Rancher | KubeSphere | Rainbond | |
---|---|---|---|
实现方式 | 通过集成 Epinio 项目,继而深入集成了Paketo Buildpacks 来实现源码构建 | 提供定制化的基础镜像来结合用户代码构建项目 | 基于 Heroku buildpack 定制的源码构建能力 |
支持语言类型 | Java、GraalVM、Golang、.NetCore、Nodejs、PHP、Ruby、Python | Java、Nodejs、Python | Java、Golang、.NetCore、Nodejs、PHP、Python、Html静态 |
使用体验 | 全部命令行操作,使用复杂 | 图形化操作,直接提供代码地址,构建产出镜像,进而部署业务 | 图形化操作,提供代码地址即可完成构建与部署,构建参数可配置,自由度高 |
更进一步的设计,是将代码的提交、检测、部署等流程都集成到 CI/CD 流水线中去,开发人员只需要进行代码的提交,后续的流程会自动触发完成,生成检测报告,并完成代码的上线部署。而与之相关的第三方工具集,由 DevOps 团队负责进行维护,开发人员可以充分的发扬拿来主义——拿来用就好。
在这方面 KubeSphere 做的更加全面,通过集成 Jenkins 实现了基于图形化的流水线配置,这种方式对于以前就在使用 Jenkins 的团队很友好。并且这种实现继承了 Jenkins 生态原有的高自由度,可以更好的将其他第三方CI工具纳入流程之中。
Rainbond 通过在构建流程中加入自制的自动触发能力,也可以完成部分流水线工作。这种配置相对编写 Jenkinsfile 来说更简单一些,能够满足一些基本场景。然而其扩展性和自由度不足,能够接纳的第三方CI工具不够丰富。
Rancher 并没有在产品中集成 CI 方面的能力,在 CD 方面通过集成 fleet 项目来实现 GitOps ,使用的门槛较高。
这样的使用体验还有一个优点,从始至终都不需要开发人员去编写格式严苛的 Kubernetes 声明式配置文件。这对开发人员而言无疑是一个极大的利好,Kubernetes 虽好,但学习曲线非常陡峭。Kubernetes 默认通过 yaml 格式的声明式配置文件来部署业务系统,其中绝大多数的字段定义都是运维特征的体现,换句话说,绝大多数的字段定义,都不应该是开发人员关注的事情。
DevOps 工程师应该抓住开发人员使用 Kubernetes 的痛点,避免其接触复杂运维事务。云原生平台理应提供这种使用体验,让开发人员对 Kubernetes 完全无感知的情况下,完成业务系统的部署工作。换句话说,让 Kubernetes 变得对开发人员“透明”。
从这个方面来说,通过对三款开源云原生平台的体验,发现 Rancher 和 KubeSphere 虽说均可以基于图形化界面来部署自己的业务组件,然而这些图形化配置只是 yaml 声明式配置文件的 “翻译”,对于 Kubernetes 不够熟悉的用户想要顺利使用,还是有一定的门槛。Rainbond 这一点则做的非常不错,部署业务时完全感受不到 Kubernetes 的存在,对于不熟悉 Kubernetes 的用户而言非常友好。然而产品化定制的程度越高,跟随 Kubernetes 前进的脚步就越难,上游 Kubernetes 不断在推出新的功能特性,如何将新特性抽象成为用户易于理解的功能将会是个挑战。最新版本的 Rainbond 推出了 Kubernetes 属性这一功能特性,允许用户以 yaml 形式编辑 workload ,也是为打破自设的“天花板”。
降低操作基础设施的难度
既然要设计一款平台级的软件产品,那么就需要将复杂且不易被掌握的技术,抽象成为用户易于理解的功能。DevOps 工程师设计的云原生平台产品,首要任务之一,是能够降低开发人员使用基础设施的门槛。这个章节主要讨论的,是开发人员自 行管理自己业务系统的运维特征。
就拿存储这件事来说,开发人员到底关注什么呢?
围绕存储这个概念,我们可以说出一堆名词,块设备、文件系统、对象存储、本地磁盘、磁盘阵列、NFS、Ceph等等。这些名词毋庸置疑都与存储相关,也的确会被各种业务系统所使用,但我相信,绝大多数的开发人员对这些名词并不关心。
作为用户,开发人员只关心一件事情,我所负责的业务系统,指定目录中的数据需要被持久化的保存下来。
大多数情况下,业务系统涉及到的存储场景都应该是简单的。在云原生时代,我们甚至呼吁开发人员在开发业务系统的时候,应该尽量做到“无状态化”,即在业务系统中,不存在限制实例横向扩容的状态数据,至少做到不同实例之间,数据可以共享。根据这一点,DevOps 工程师们完全可以为开发人员提供一个能够适应大多数场景的默认存储类型,各个云厂商提供的 NAS 类型存储是个很好的选择。
使用复杂存储的场景更多见于业务系统所调用的各种中间件中,比如数据库需要高速稳定的块设备类型存储,再比如大数据领域的“存算分离”场景下对接对象存储等等。然而在大多数场景下,这些复杂中间件的维护并不是开发人员应该关心的事情。它们由专门的运维人员进行维护,开发人员只需要得到访问它们的地址即可。
所以在这种简单存储场景下,开发人员最好可以仅仅填写一下自己需要被持久化的目录地址,由云原生平台来实现底层存储的配置。对存储基础设施的操作,开发人员并不需要关心。
从使用体验上来说,Rancher 和 KubeSphere 可选择的存储类型很多,这是因为它们的产品生态优于 Rainbond ,比如 Rancher Lab 直接推出了轻量级的存储解决方案 Longhorn,对于各大公有云厂商提供的存储产品驱动也都有集成。 Rainbond 依然在易用性方面做的够好,实现了上文中仅关注业务系统持久化目录的使用体验。然而仅对 NFS 类型的存储支持比较完善,对于其他类型的存储支持,需要在底层基础设施中自建驱动,安装起来不如前二者方便。
易于理解的应用模型
从工作负载层面上讲,Kubernetes 只通过 Deployment、Statefulset 等抽象描述单个组件的特征,然而多数情况下,开发人员开发出的业务系统会包含若干微服务组件。那么如何对整个业务系统进行统一的管理就变成了一个问题。解决方案之一,就是通过云原生应用平台,在单个组件之上,抽象出应用这个概念。应用应该是由人为规定的一组服务组件(workload)组成,服务组件之间具有显式或隐式的关联调用关系,彼此之间有机组合成为一个整体,作为一套完整的业务系统对外提供服务。
开发人员可以将所有的服务组件视作一个整体来进行管理,而非机械的单独管理每一个服务组件,这种操作体验无疑会更简单,也便于开发人员理解。对应用的管理可以包括统一的生命周期管理、统一的安装升级卸载,灵活拼装服务组件之间的调用关系,更合理的处理业务系统的交付流程。
目前 Kubernetes 领域内较为成熟的交付工具 Helm ,其设计也暗合此类模型,一条简单的 helm install xxx
命令,即可安装起一大堆组件以及围绕这些 组件的配置。
Rancher 并没有实现自己的应用模型,其应用的安装方式集成了 Helm ,并没有体现出应用管理能力。
KubeSphere 则更进一步,在项目下的应用负载中提出了应用的概念。在应用中可以通过 Helm 或自建的形式部署服务,集成了微服务治理、单个组件的版本控制、路由管理、灰度发布等能力。其对 Helm 模板的支持,使得其从理论上可以支持任何市面上已有的 Helm Chart 包的安装部署。
Rainbond 的应用概念是最完善的,除了常规的生命周期操作、整个应用级的版本控制这样的常规能力之外,还有些非常易用的自研功能,能够简化开发人员对自己应用的管理。比如基于泛解析域名机制实现的对外服务域名,点击开启对外服务,就会生成一个公网可用的域名访问自己的应用,这比一层一层的配置 Ingress 规则容易太多。又比如应用复制能力,可以批量的将整套应用复制到另一个工作空间,而不必重新手动搭一套。
应用模板是 KubeSphere 和 Rainbond 均提出的一个概念,应用模板存在的意义是可以将开发好的应用复制到不同的环境中去,是一种制备新一代制品并交付分发的技术。应用模板的基础使用体验,是可以快速方便的安装整套应用系统,最好是一键化的体验,KubeSphere 和 Rainbond 都提供了应用商店,供用户快速安装一些已经制作好的应用模板。应用模板更高层次的使用体验,则是开发人员可以无任何技术负担的开发出自己的应用模板,而不仅仅是从应用商店拉取别人制作好的应用模板。