题图摄于北京
本篇转发TAP系列文章之六,Tanzu Application Platform (TAP) 的应用模型。
✦
云原生 12 要素应用模型
✦
大家可能听过 Netflix 的故事,在 AWS Region 故障的时候,它的服务仍然没受到什么影响,能继续对外提供流媒体服务。
他们遵循的就是云原生应用与云平台的契约:即使云平台再可靠,也不会 100%可用,而上层应用需要通过架构设计来保证业务连续。
具体而言
就是云原生应用
要具备 12 要素
才能满足以上契约
· 使用版本控制管理代码
· 应用基于代码库和依赖声明式的打包
· 不同环境的应用包应保持不变,无需重新打包
· 配置与代码分离,尤其是与环境有关的配置,没有硬编码
· 应用使用到的后端服务(如数据库、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 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 包)的路径、资源的需求(内存)、实例的数量、绑定的服务、环境变量等,剩下的就由平台来自动配置了。
当然,开发者还可以做更多配置比如访问路由的域名、健康检查的方式、启动命令等···
✦
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 的自动伸缩特性。对于不采用缩容到零的需要长期运行的应用,其实可以通过指定实例数量的上下限(增加 annotation:autoscaling.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 ,这是由我们和 IBM/Redhat 共同开源的规范,用于标准化应用访问服务的方式。
采用服务绑定的方式,开发人员不需要直接接触到密码这样的安全敏感信息,也不需要注意配置文件中密码的的保密,更不会不小心将包含密码的配置文件提交到版本库保存,更安全;应用和服务实例的绑定关系可以在不同环境下保持不变(虽然同名的服务实例在不同环境下其实是不同的),更稳定。服务实例在非生产环境可以由开发人员自服务,按需创建,在生产环境则可由运维团队统一管理和创建。
如果采用的是共有云平台提供的服务,也不需要直接使用云平台的 SDK,而是通过统一的 Service Broker 抽象层去使用,避免与云平台的紧耦合。
✦
总结与展望
✦
TAP 是一个比较新的产品,还在持续的迭代和发展中,其应用模型支持的抽象也会越来越丰富。比如现在支持的主要应用类型是 web 应用,实现了 source-to-url 和 image-to-url,很快就会扩展支持更多类型的应用(如 function,tcp 等),以及 Jenkins/ArgoCD 集成等场景。
以上初步介绍了 TAP 的应用模型,我们会在后续的系列文章中进一步介绍 TAP 的其它组件,敬请关注与期待!如果您有任何反馈,也请联系我们!
要想了解云原生、机器学习和区块链等技术原理,请立即长按以下二维码,关注本公众号亨利笔记 ( henglibiji ),以免错过更新。