云原生12要素应用模型
大家可能听过Netflix的故事,在AWS Region故障的时候,它的服务仍然没受到什么影响,能继续对外提供流媒体服务。他们遵循的就是云原生应用与云平台的契约:即使云平台再可靠,也不会100%可用,而上层应用需要通过架构设计来保证业务连续。
具体而言,就是云原生应用要具备12要素(https://12factor.net/ )才能满足以上契约。
- 使用版本控制管理代码
- 应用基于代码库和依赖声明式的打包
- 不同环境的应用包应保持不变,无需重新打包
- 应用使用到的后端服务(如数据库、NoSQL、缓存、消息中间件等)可以自助使用(创建、绑定使用、解绑、删除等),服务不绑定于某个IP,通过逻辑的名字如DNS, 注册中心的名称来调用
- 应用尽量保持无状态,状态(如用户Session、缓存)不依赖于本机内存(易失性的),应保存在公共的缓存服务器或NoSQL数据库中,没有本地文件系统(易失性的)的I/O,如特定路径的文件读写等
- 应用优先使用水平伸缩,通过增加/减少实例数量实现扩缩容
- 应用能快速启动,支持优雅终止,保持在1分钟以内;不同应用可以独立启动和停止,无特定顺序
- 不同环境(如开发、集成测试、用户验收测试、预生产、生产环境等)尽量保持等价一致
- 日志输出到STDOUT/STDERR,由平台工具进行日志聚合
- 管理操作(如创建数据库schema,初始化数据等)也作为一次性的任务来执行
Pivotal在自身的实践中,又增加了3个要素:
- 优先设计服务的API,并保持稳定和兼容
- 应用应对外暴露遥感(Telemetry)接口,提供可观测性(Observability),如是否健康(以本地检查为主,不含外部依赖)、是否就绪、指标输出、埋点跟踪等
- 租户的安全隔离,基于角色的认证和授权(RBAC)
Netflix也开源贡献出自己内部使用的框架,通过与Pivotal合作Spring Cloud Netflix项目,在广大的Java/Spring开发者社区普及了云原生应用的12要素。无论应用是部署到公有云还是私有云,无论是否容器化部署,都应该尽可能满足这12要素,这样应用才能更充分的利用底层云平台,而底层的云平台也才能更好的调度应用,提供更好的云服务。
Kubernetes的应用模型
很多企业新开发的应用现在也基本都会选择部署到Kubernetes平台,而不是直接部署到云平台之上,因为Kubernetes屏蔽了底层云平台的细节,提供了更高层的抽象,包括应用模型,也契合了云原生应用12要素的要求。
但与此同时,开发人员也已经意识到Kubernetes的学习曲线还是太陡峭了,Kubernetes对开发人员而言太复杂了,要做到生产就绪真心不容易。
假设一个微服务的开发人员,当他好不容易实现了业务逻辑,要把应用部署到Kubernetes的时候,起码还要做两件事:
1.构建容器:比如使用Dockerfile构建出容器镜像,并保存到企业镜像库中
2.配置部署yaml:通常包括Deployment,Service或Ingress,ConfigMap/Secret, ServiceAccount, Role/RoleBinding等
当他在准备这些Kubernetes的 yaml文件的时候,其实就是在利用Kubernetes的原生抽象模型,然后交给Kubernetes去做调度和部署。
一个典型的部署应用到Kubernetes的yaml大致是这样的:
大约需要配置50行yaml,包括:
- 应用的名字
- 镜像的位置
- 环境变量
- 资源的需求
- 监听的端口
- Liveness/Readiness Probe
- 实例的数量
- 服务访问的方式(如负载均衡类型)等。
很多开发人员其实不知道可能还需要考虑一些更高级的配置才能生产就绪,比如NetworkPolicy,SecurityContext,PodDisruptionBudget等。
Knative的应用模型
社区也意识到Kubernetes对开发体验不够友好,所以出现了类似Knative这样更高层次的抽象,如Knative Service, Knative Route, Knative Revison, Knative Configuration,底层仍然利用Kubernetes的Deployment,ReplicaSet,Pod等原生抽象。
从部署的yaml文件来看,Pod部分基本上保持不变,总体而言,比原生k8s yaml要配的内容少一些,并且短一些,比如不需要单独配置服务访问的方式,而是通过Knative的Route来自动生成访问域名;也不需要配置实例数量,而是依赖Knative自动伸缩。
Cloud Foundry的应用模型
在Kubernetes没有成为容器调度的事实标准之前,上一代的PaaS平台以Cloud Foundry为代表,在Cloud Foundry的用户中一直流传这样一句格言:“这是我的代码,帮我在云上部署运行应用,我不关心到底是怎么实现的 (Here is my source code, Run it on the cloud for me, I do not care how…)”,用来形容Cloud Foundry比较友好的开发者体验。
我们来看一个典型的Cloud Foundry的部署Java Spring应用的文件manifest.yaml
开发者只需指定应用的名字、应用的代码(如python代码)或部署包(如Java jar包)的路径、资源的需求(内存)、实例的数量、绑定的服务、环境变量等,剩下的就由平台来自动配置了。当然,开发者还可以做更多配置比如访问路由的域名、健康检查的方式、启动命令等,具体可参见:https://docs.pivotal.io/appli...
TAP的应用模型
TAP作为新一代PaaS平台,主要基于Kubernetes技术体系,以Knative作为云原生运行时,但同时也继承了Cloud Foundry的开发体验,试图博采众长,青出于蓝而胜于蓝。
一个典型的TAP应用的部署文件workload.yaml是这样的:
可以看出,TAP的开发体验更接近于Cloud Foundry,都需要指定指定应用的名字、资源的需求(CPU / Memory limits/requests)、绑定的服务(ServiceClaims)、环境变量。
不一样的是不需要指定应用的部署包的路径,而是指定代码在版本库的位置(或代码在镜像库中的位置),相当于覆盖了CI/CD的完整流程。
当然,如果只需使用CD部分的功能的话,也可以直接指定CI流程构建出的jar包或镜像在镜像库的位置。TAP同时也支持GitOps的部署模式,自动拉取版本库的变化,并在集群中应用执行并确保一致。
如果眼尖的话,就可以注意到workload yaml并没有像Cloud Foundry的manfest.yaml那样指定应用实例的数量,而是依赖于Knative的自动伸缩特性。对于不采用缩容到零的需要长期运行的应用,其实可以通过指定实例数量的上下限(增加annotationautoscaling.knative.dev/minScale和maxScale)来调整伸缩的范围。
为与Cloud Foundry保持向下兼容,TAP有一个专门的Cloud Foundry的适配器(Adapter),可以直接使用Cloud Foundry的manfest.yaml来部署到TAP上。
需要特别指出的是,在label中指定应用类型(workload type),可以自动选择TAP中的供应链,比如web 类型的应用就会执行内置的一条基础供应链ootb_supplychain_basic,即:
在做实际部署的时候,默认使用Knative Service的方式部署,当然也可以沿用原来的方式选择以原生Kubernetes的方式部署。
实际生成的部署yaml是这样的(以Knative Service为例,有删减):
Workload的配置经过流水线自动转化成了Knative Service的配置,并添加了表示元数据的Annotation/Labels, 用于可观测行的环境变量JAVA_TOOL_OPTIONS以及Readiniess Probe等。
服务绑定
云原生12要素中第4要素建议把后端服务作为可附加的资源来使用(Treat backing services as attached resources)。也就是:大多数应用应该实现为无状态的,所以应用的状态就需要保存在后端服务中,如数据库、消息中间件等有状态的持久化存储。应用通过绑定后端服务去使用这些服务来读写状态数据。
以Java应用访问MySQL数据库为例:
- 传统的方式是由开发人员在配置文件中配置连接字符串,如URL、username、password,然后在应用中读取配置项,生成DataSource,创建出Connection 供应用使用;做得好的微服务,一般把配置信息保存在统一的配置中心然后在应用启动时动态获取。
- 在Kubernetes中,则需配置ConfigMap/Secret对象(或环境变量),在应用中读取后使用。
- Cloud Foundry则不需要这些具体配置,只要绑定(bind)服务实例的名字即可。服务实例对应的具体的访问地址、用户名和密码是以环境变量的形式自动注入到应用实例中的。如果应用实例采用了Spring Cloud Binding类库,会自动生成DataSource,不采用Spring类库的话,就要自己写程序解析这些环境变量然后生成DataSource。
- 与Cloud Foundry类似的,TAP只需声明使用(Claim)的服务实例的类型和名字,具体配置连接字符串会自动以Secret的方式mount到应用实例供使用。同样的,如果应用实例采用了Spring Cloud Binding类库,也会自动生成DataSource。
当然,服务的提供需要符合k8s-binding-spec (https://github.com/servicebin... ),这是由我们和IBM/Redhat共同开源的规范,用于标准化应用访问服务的方式。
采用服务绑定的方式,开发人员不需要直接接触到密码这样的安全敏感信息,也不需要注意配置文件中密码的的保密,更不会不小心将包含密码的配置文件提交到版本库保存,更安全;应用和服务实例的绑定关系可以在不同环境下保持不变(虽然同名的服务实例在不同环境下其实是不同的),更稳定。服务实例在非生产环境可以由开发人员自服务,按需创建,在生产环境则可由运维团队统一管理和创建。
如果采用的是共有云平台提供的服务,也不需要直接使用云平台的SDK,而是通过统一的Service Broker抽象层去使用,避免与云平台的紧耦合。
总结与展望
TAP是一个比较新的产品,还在持续的迭代和发展中,其应用模型支持的抽象也会越来越丰富。比如现在支持的主要应用类型是web应用,实现了source-to-url和image-to-url,很快就会扩展支持更多类型的应用(如function, tcp等),以及Jenkins/ArgoCD集成等场景。
以上初步介绍了TAP的应用模型,我们会在后续的系列文章中进一步介绍TAP的其它组件,敬请关注与期待!如果您有任何反馈,也请联系我们!
作者简介:
罗治年,VMWare大中华区应用现代化部门的资深云原生应用架构师,有20多年的软件研发和架构设计经验,曾先后就职于埃森哲、毕博管理咨询、迪士尼、Pivotal等公司,长期从事企业IT规划,企业级系统架构设计,及系统研发和实施管理等工作。近期主要专注于采用敏捷开发方法实现微服务云原生应用的设计和开发,对传统应用实现现代化并实现云上迁移拥有丰富的实战经验,并获得诸多技术认证,包括:Spring Professional, Kubernetes 管理员 (CKA) 、AWS Professional、DevOps Professional和Cloud Foundry 专家等。
来源|公众号:VMwareTanzu云原生