Kubernetes生产化之路-在Kubernetes上构建应用程序平台

通过上篇文章的介绍,大家应该能够感受到Kubernetes是企业进行数字换转型非常重要的一个工具组件,因此很多人可能在想,是不是Kubernetes就是企业数字化转型过程中唯一缺失的一环?反过来说就是只要我们将应用部署到Kubernetes上,所有的问题都可以得到解决?Unix操作系统有很多经典的设计哲学,其中最为人熟知的就是这个原则被描述为:make each program do one thing well的原则,Kubernetes在设计的过程中,很多地方都体现出小而美的这个原则。特别是我们第一篇关于Kubernetes介绍的文章中,笔者给大家介绍了Kubernetes设计的多个可扩展性的接口:CRI,CSI,CNI等。Kubernetes通过只定义这些接口需要提供的能力,并focus在容器编排这一件事情上,来让Kubernetes逐渐成为企业数字化转型的基础。

这种Foundation的能力彻底改变了未来若干年企业系统架构和部署方案,特别是云原生架构模式的流行,容器化,编排,微服务架构以及Devops这些概念终于可以有一个上层的umbrella,我们所说的云原生架构模式,设计模式,本质上就是容器化,动态基础设施,微服务架构,自动化以及容器编排这些概念的具体体现。而Kubernetes就是这些云原生内涵的基础,类似于老家(陕西宝鸡)农村自建房屋的基础。

虽然笔者没有设计过房屋,但是从软件设计的角度看,房屋的基础首先必须稳固,并且结构合理,并支持我们进行后续的装修工作。另外房屋的基础必须做好水电管道的铺设,线路的规划,当我们后续安装电器和卫生系统的时候,有合理的接口被预留并对接。虽然说房屋的基础非常重要,但是只有骨干结构的房屋不太适合人们居住,或者说我们需要在基础之上来构建房屋。在讨论如何在Kubernetes上构建适合应用程序部署的“房屋”之前,我们继续房屋的类比,看看通常意义上适合人们居住的房屋长啥样,如下图所示。

《图1.1 装修好的公寓,类似于Heroku提供的PAAS平台》

对于装修好的房间,我们随时都可以拎包入住,而Heroku提供的就是这种类型的服务,我们的应用程序几乎可以不做太多的修改就可以在Heroku上运行起来,虽然我们可能会基于自己的喜好来对房屋进行轻度的“软装”,这取决于我们,或者给我们作为用户提供了灵活度。如果我们对公寓的价格以及装修比较满意,类似于Heroku提供的服务,我们可以从第一天就能享受到Heroku这样的应用程序平台为我们带来的价值。

回到Kubernetes,由于Kubernetes只是提供了一个稳固的,设计良好的基础(Foundation),我们需要在这个基础上按照我们的喜好来构建适合企业应用程序部署,管理和监控的应用程序平台,Kubernetes提供的基础能力如下图所示:

《图1.2 Kubernetes类似于我们自建房屋的基础,为建设美好家园奠定了基础》

有了Kubernetes提供的这个基础,只要预算充足,时间允许,我们在这个基础就可以构建出强大的应用程序平台来运行企业的应用程序,这也意味着我们对整个平台的功能和架构有完整的控制,我们可以按照企业的短期和长期的业务诉求来设计应用程序平台。读到这里读者可能会问,具体要怎么建呢?咱们接下来通过分层的策略来看看这样的平台应该有那些层,以及每一层需要提供哪些功能。

中国有句古话,千里之行始于足下,我们先从最底层开始,需要解决的核心问题是支持Kubernetes平台运行需要的硬件基础设施。通常情况下我们可以选择在自己的数据中心一组物理机器上,或者类似于阿里云这样的云平台供应商,总之我们需要计算资源(机器,无论是物理还是虚拟),存储和网络能力。有了硬件基础设施,我们就在这些硬件上部署并安装启动Kubernetes,当然部署和安装Kubernetes是一个很大的话题,咱们不可能通过几句话来说清楚,大家可以参考官方文档,坦白讲官方文档的质量比我写出来的文章质量高多了。

有了Kubernetes集群之后,我们接下来要考虑的问题是在这个集群上具体要构建什么功能,基于笔者过往的经验,我们需要考虑的点如下图所示:

《图1.3 基于Kubernetes构建定制化PAAS的生产化之路》

有了Kubernetes集群,很快我们就需要回答如下这些问题:

- 如何确保服务之间加密通信,换句话说就是mTLS?

- 如何确保访问外部的请求数据有一致的source IP地址,或者说source CIDR?

- 如何提供自助式的系统监控和全链路跟踪服务给终端用户?

- 如何让普通开发人员尽快的使用Kubernetes集群来部署他们的应用,即使他们不是Kubernetes专家?

这个问题清单随着时间的推移会越来越长,但是如果你作为平台级的负责人或者架构师,摆在我们面前的非常重要的问题是:我们如何决策哪些功能放在平台级别,哪些功能让应用程序自己解决?决策的依据还是来自于企业的应用部署和管理需求,我们需要首先深入理解现有部署和管理流程中存在问题,然后对比在平台级别或者应用级别那种方式的对企业的价值更高。

不过有些功能可能没有办法直接来对比价值,那么我们还可以通过逆向思维来分析,如果我们无法在平台级别解决这个问题,有多少应用程序会受到影响?以及平台级别不提供此功能,对开发团队的影响到底是什么?通过这种方式,我们就可以把企业最关心的,通用的,价值最高的功能罗列出来,作为备选项来形成应用程序平台的功能清单。

接着我们可以基于功能清单在Kubernetes集群上构建我们的应用程序平台,笔者需要特别强调的是,在开发的过程中,我们需要和平台的最终用户:企业的开发和运维人员保持紧密的沟通和协作,特别是当新功能开发出来之后,我们需要第一时间邀请开发和运维人员试用并给出直接的反馈,并且即便是这些新功能部署到生产环境之后,这种及时反馈和倾听客户声音的模式应该保留。

无论是什么类型的应用程序,我们都不应该抱有一劳永逸的幻想,应该实时的和应用的直接用户保持沟通,不断补充缺失的功能,或者优化已有的功能。对基于Kubernetes集群的应用程序平台来说,当我们从用户的角度考虑是,本质上在考虑我们期待用户(开发和运维人员)在什么抽象级别上Kubernetes集群进行交互发,这个问题的答案也决定了我们应该在Kubernetes构建什么的抽象层,来屏蔽底层的复杂细节。极端情况是企业的开发和运维都是Kubernetes高手,那么我们的确在不做任何抽象的情况下,依然能够维持一套稳定的Kubernetes集群来支持生产系统部署和管理工作。

笔者有幸在微软工作过几年,主要围绕SharePoint来帮助企业构建文档管理平台,做SharePoint这个行业有一句黑话:如果用户使用企业管理Portal的时候能感觉出来底层使用的是SharePoint,那么这个项目就是失败的。因此做SharePoint的第一条原则就是,如何通过定制页面样式来让SharePoint让客户觉得不是在用SharePoint,而是用DotNet开发的一个内容管理Portal。

在基于Kubernetes的应用程序平台场景下,这句话依然有效,如果使用我们平台的用户能够觉察到底层使用的是Kubernetes集群,那么我们的工作就失败了。虽然说这句话有点极端,但在特定场景下,比如对于构建的服务或者产品来说,底层是否使用了容器调度平台不重要,那么屏蔽底层的实现细节对于用户来说绝对是非常必要的。举个例子来说明一下,假设我们基于Kubernetes来构建数据库管理系统,并支持多种数据库类型,那么数据库实例具体运行在Kubernetes,Mesos还是Bosh,从使用者开发人员的角度来看,这个根本不重要,重要的是对应类型的数据库必须满足预期。

不过事无绝对,作为平台的设计和构建者,我们需要在Kubernetes集群的不同组件上做出抽象粒度的决策,来满足最终使用用户的预期,下图为我们提供抽象粒度的可视化说明:

《图1.4 基于Kubernetes集群构建的PASS平台抽象区间》

要准确的定位平台的抽象层次是个相当耗时的一项工作,我们需要考虑很多因素,不过参考当前成熟的PAAS平台或许能够找到一些蛛丝马迹。Cloud Foundry平台提供了成熟的用户体验,我们只需要通过一句直观的cf push,就可以将应用程序构建,打包,部署并开放给外部系统访问。正是由于Cound Foundry追求这种极致的用户体验(抽象),将Kubernetes当成Cound Foundry底层平台的一种具体实现形式就显得理所当然了。

另外一种笔者经常看到的模式是通过PAAS来屏蔽底层多平台的差异性,有些企业因为已经重度投资了Kubernetes之前的技术平台,比如Mesos等,因此我们可以在Kubernetes和Mesos等平台之上构建一层抽象层,以期给开发人员提供一致的使用体验,让开发人员不用关心自己开发的应用程序具体部署到哪个平台上,这种模式的另外一个好处是避免平台绑定。不过任何事情都有两面,这种跨平台的抽象能力依赖于大量的定制化开发,并且这种定制化开发对软件开发工程能力的要求非常高。

从开发人员的角度看,虽然说基于跨技术栈的抽象层降低了使用的复杂度,但是也蒙蔽了开发和运维人员的双眼,就如同强大的IDE集成开发环境提升了我们的开发效率,但同时也让很多同学离开这些工具,就不知道如何编译运行源代码,需要我们从使用者的角度做好权衡。过渡的抽象反而会弄巧成拙,需要花费大量的开发资源不说,会让我们的平台变得复杂,脆弱并最终无法维护而被动成为遗留系统。

上图的最左边是另外一个极端,特别对于Kubernetes原生能力使用成熟度高的团队,我们完全可以为团队直接提供独享的集群,团队可以基于已有对Kubernetes的专业技能,来解决配置,管理和运维等工作。不过基于笔者过去几年的经验,这种模式非常少见,很大一部分原因是Kubernetes本身的复杂性造成的,不是每个开发团队都有能力自行运维和管理Kubernetes集群。这一点大家可以从Kubernetes的安装手册上看出来,有大量的参数需要配置和优化来确保Kubernetes能够按照我们的预期运行。

最后,抽象并不是一个二元选择(是或者否),很多时候我们需要结合实际的情况来进行分析,构建抽象的能力解决开发和运维人员的问题,那么我们就选择构建抽象功能;提供灵活度让开发人员自行抉择如何解决部署和运维的问题,那么就提供Kubernetes原生的接口给开发人员,以期在复杂度和灵活度,易用性之间达成平衡。

因此构建基于Kubernetes技术栈的PAAS平台最大的挑战在于如何判断哪些功能在应用程序层处理,哪些在PAAS平台层处理,并且不存在通用的规则,我们只能case by case的逐一讨论和处理。咱们来举个例子说明一下,服务间通信是微服务架构下需要重点设计的部分,特别对安全性要求高的应用,除了南北流量必须使用TLS传输层安全之外,东西流量也需要使用mTLS来进行传输层安全防护。

这就需要我们从应用架构设计层面,或者底层的PAAS层面,提供对应的解决方案(workload identity,证书颁发,证书替换)来支持应用的流量安全问题。如果我们负责平台建设,那么我们就需要深入的理解所有需要传输加密的场景,评估是否从平台级别解决这个问题是个好的选择。具体来说我们可以通过Servie Mesh方案,来提供服务之间调用的安全保障,实施这个方案之前,我们需要进行正反两个方面的分析:

服务网格(Servie Mesh)的优势是:

- 开发语言(Java,Golang等)不在需要引入三方包来支持mTLS加密机制

- 所有在平台上运行的应用程序(无论什么编程语言)都可以使用mTLS机制来确保服务间通信安全

- 极大的简化了应用程序开发团队的复杂度

服务网格(Servie Mesh)带来的问题:

- 服务网格本身是一套复杂的分布式系统,运维工作量和复杂度增加

- 服务网格有点大炮打蚊子,因为服务网格除了提供identity和通信加密之外,还提供很多其他功能

- 服务网格的提供的identity API可能和现有的系统不兼容

因此我们不应该,也不合适追求让所有的功能都构建在平台层面,而应该结合具体的功能场景来分析,构建在平台层面是否能够为企业带来价值,长期看缩减成本,以及降低开和运维人员使用的复杂度。

文章的最后咱们以构建基于Kubernetes的PAAS平台具体需要哪些模块来收尾,由于PAAS平台本身所处的层面,模块清单中包含了从基础设施到平台服务的所有组件,如下图所示:

《图1.5 基于Kubernetes构建的PAAS平台核心模块》

读者需要注意的是上图只是为大家罗列出笔者认为核心的组件,并不代表这是一份完整的清单,并且有些组件可能在具体的场景下需要分析和取舍。例如container networking和runtime运行时是每个集群必须的组件,要不然服务能力无法被访问,无论是集群内部和外部。有些例如secret management这样的模块我们可能不需要重新实现,因为可以直接集成阿里云提供的KMS服务来实现秘钥的管理,分发和替换。

说到秘钥管理,大家应该注意到图1.5中并未体现出任何安全组件,并不是说安全对PAAS平台不重要,而是笔者认为安全是一个体系化的工程,是结果并不是过程,因此我们需要从IAAS层之上的每一层都考虑安全方案,为了让读者对图1.5展示的组件框架有更加清晰的了解,我们来提纲挈领的对每个模块进行简单介绍。

- IAAS/Datacenter and Kubernetes层,这两部分是整个PAAS平台的基础,类似于咱们前边类比的房屋的基础结构。大家不要觉得这些是基础就不重要,相反只有基础扎实了,我们在其之上构建的应用系统才能提供预期的服务。特别是随着云计算平台变得像水电煤一样触手可得,构建PAAS平台的时候,我们的重心已经从如何搭建机房转变成选择什么样的Kubernetes部署方案和拓扑结构。大白话说就是我们采用什么方案来创建,管理和运维Kubernetes集群。

- Contianer runtime,容器运行时是工作节点上运行应用程序最重要的组件,可选的运行时包括但不限于:CRI-O,Containerd以及Docker等。Kubernetes平台为我们提供了灵活的容器运行时选择权,因为所有兼容CRI接口的运行时都可以被Kubernetes使用来调度运行应用程序。Kubernetes设置支持某些极端的场景,比如多应用的隔离性要求非常高,那么我们可以选择则在micro-vm类型的runtime上运行我们的应用程序。

- Contianer networking,容器网络组件要解决的核心问题有两个:1,容器实例的IP地址分发;2,请求的路由。Kubernetes上的网络方案和运行时类似,我们可以选择兼容CNI的所有可用的网络插件,比如Calico,Cilum等。通过在Kubernetes集群的节点上安装网络插件,kubelet就可以为容器实例启动的时候申请IP地址,甚至有些网络插件在POD的虚拟网络上实现了Service抽象能力。

- Storage integration,存储集成模块解决的当on-host磁盘空间不足的时候,我们如何集成外部的存储能力。云原生架构最佳实践指导我们应用尽量要无状态,但是没有状态的系统也没有啥实际的业务价值,只是应用的状态被转嫁给了有状态的数据库这样的应用。因此从这个角度看,数据库类型的应用要求存储不应该依赖于应用的状态和调度,而应该有极强的确定性和持久性。拜Kubernetes定义的CSI接口提供的灵活性,我们可以集成很多采用很多技术的存储组件,比如vSAN,EBS,Ceph等。

- Service routing,服务路由要解决的核心问题是将访问流量路由到部署在Kubernetes集群中的POD上,默认情况下Kubernetes提供了Service API,这是我们构建更高级负载均衡能力的基础。比如我们基于容器底层的网络插架来构建L7的负载均衡能力,Ingress Controller就具备这种7层的负载功能。当然Service Mesh也提供了丰富的服务路由能力,以及服务间安全机制mTLS,服务的可见性,服务治理机制包括但不限于断路器模式等。

- Secret management,secret管理模块解决的核心问题是敏感数据的管理,分发。虽说Kubernetes原生提供了Secret这种类型的对象,但是由于实现机制的问题,直接使用原生的能力并不安全。大部分企业都会选择集成阿里云的KMS服务或者自建Vault这样的开源套件。

- Identity模块解决的核心问题就是用户和应用的认证。对于用户认证来说很容易理解,传统架构中的LDAP,以及云平台提供的IAM系统都提供用户认证的能力。而对于应用的认证(workload)的认证先对会复杂一些,特别是在安全性要求高的场景,比如零信任网络模型场景下,解决伪装攻击(imersonation attak)如果没有identity机制的支持,就非常具有挑战性。我们的应用程序可以集成identity provider提供的接口来表明自己的身份,以及通过mTLS来解决客户端和服务端之间的相互认证问题。

- Authorization/Admission control,认证问题解决后的下个问题是授权,当我们访问服务资源的是偶,我们需要有种机制来协助服务端判断客户端是否有权限访问此服务,比如API Server提供的Restful资源。Kubernetes提供了RBAC机制来控制资源的访问权限,我们可以在resource好verb粒度上来控制,但是如果客户有自定义的控制逻辑,该如何应对呢?Kubernetes提供了Admission control来解决这个问题,你可以把Admission control想象成通往Kubernetes核心资源的大门,大门的授权逻辑(是否可以让你进入)取决于某个外部系统实现的逻辑,当我们需要访问某个资源的时候,Admision control会把请求的信息发给某个外部的系统,然后基于外部系统的响应来判断是否放行。

- Software supply chain,软件供应链的概念取自物流仓储领域供应链概念,覆盖了软件全生命周期,从代码编写到打包部署以及应用运行。通常我们借助于CI和CD工具的流水线(pipeline)来自动化软件交付的流水线。由于Kubernetes集群构建的PAAS平台主要用来部署企业的软件应用,因此CICD如何和Kubernetes集成起来是最为重要的集成点。另外我们还需要关注镜像的存储,镜像的安全扫描和签名,确保恶意的代码不会被注入到镜像中,进而当镜像被容器实例运行起来,造成安全问题。

- Observability模块解决的核心问题是如何让开发和运维人员能够通过透出的指标,准确的了解集群或者应用的运行状态。可见性理论上包含三个子模块,分别是logs日志,metrics系统指标以及tracing追踪。日志大家都很熟悉,就不说了;指标是把从系统里抓取的数据放到时间轴,来观测系统的某项状态。追踪在分布式系统中特别重要,因为如果应用系统被拆分成多个服务以及需要访问多个中间件服务,tracking可以帮助我们记录一次服务调用过程中,多个服务之间交互的细节。当我们收集到了多个服务之间调用的数据,可以通过可视化的系统来监控系统的实时运行状态,并在突发异常情况下告警。

- Developer abstractions,最后,我们基于Kubernetes集群构建的PAAS平台主要是服务开发和运维团队,Deverlper abstraction就是这最后一公里,决定了我们提供什么样的服务(抽象服务)来支持开发和运维人员日常的系统开发和运维管理工作。如咱们前边所属,这部分需要结合企业的现状来设计,Kubernetes成熟度高的企业,可以选择暴露更多的Kubernetes原生能力给用户;反过来如何企业的开发和运维团队刚刚接触Kubernetes,也没有意愿深入的学习,那么构建更加易用的抽象服务能力对企业来说更有价值。

好了,咱们Kubernetes生产化之路的第一部分三篇文章就结束了,接下来笔者会逐项来介绍这些核心组件,咱们下篇先从容器运行时开始,敬请期待!

你可能感兴趣的:(Kubernetes生产化之路-在Kubernetes上构建应用程序平台)