作者:
刘洋(炎寻) EDAS-OAM 架构与开发负责人
邓洪超 OAM spec maintainer
孙健波(天元) OAM spec maintainer
随着以 K8s 为主的云原生基础架构遍地生根,越来越多的团队开始基于 K8s 搭建持续部署、自助式发布体验的应用管理平台。然而,在 K8s 交付和管理应用方面,目前还缺乏一个统一的标准,这最终促使我们与微软联合推出了首个云原生应用标准定义与架构模型 - OAM。本文作者将从基本概念以及各个模块的封装设计与用法等角度出发来详细解读 OAM。OAM 主要有三个特点: 开发和运维关注点分离:开发者关注业务逻辑,运维人员关注运维能力,让不同角色更专注于领域知识和能力; 平台无关与高可扩展:应用定义与平台实现解耦,应用描述支持跨平台实现和可扩展性; 模块化应用部署和运维特征:应用部署和运维能力可以描述成高层抽象模块,开发和运维可以自由组合和支持模块化实现。OAM 综合考虑了在公有云、私有云以及边缘云上应用交付的解决方案,提出了通用的模型,让各平台可以在统一的高层抽象上透出应用部署和运维能力,解决跨平台的应用交付问题。同时,OAM 以标准化的方式沟通和连接应用开发者、运维人员、应用基础设施,让云原生应用交付和管理流程更加连贯、一致。
角色分类OAM 将应用相关的人员划分为 3 个角色: 应用开发:关注应用代码开发和运行配置,是应用代码的领域专家,应用开发完成后打包(比如镜像)交给应用运维; 应用运维:关注配置和运行应用实例的生命周期,比如灰度发布、监控、报警等操作,是应用运维专家; 平台运维:关注应用运行平台的能力和稳定性,是底层(比如 Kubernetes 运维/优化,OS 等)的领域专家。
核心概念OAM 包含以下核心概念:
服务组件(Component Schematics)应用开发使用服务组件来声明应用的属性(配置项),运维人员定义这些属性之后就能按照组件声明得到运行的组件实例,组件声明包含以下信息: 工作负载类型(Workload type):表明该组件运行时的工作负载依赖; 元数据(Metadata):面向组件用户的一些描述性信息; 资源需求(Resource requirements):组件运行的最小资源需求,比如最小内存,CPU 和文件挂载需求; 参数(Parameters):可以被运维人员配置的参数; 工作负载定义(Workload definition):工作负载运行的一些定义,比如可运行包定义(ICO images, Function等)。
应用边界(Application Scopes)运维人员使用应用边界将组件组成松耦合的应用,可以赋予这组组件一些共用的属性和依赖,应用边界声明包含以下信息: 元数据(Metadata):面向应用边界用户的一些描述性信息。 类型(Type):边界类型,不同类型提供不同的能力; 参数(Parameters):可以被运维人员配置的参数。
运维特征(Traits)运维人员使用运维特征赋予组件实例特定的运维能力,比如自动扩缩容,一个 Trait 可能仅限特定的工作负载类型,它们代表了系统运维方面的特性,而不是开发的特性,比如开发者知道自己的组件是否可以扩缩容,但是运维可以决定是手动扩缩容还是自动扩缩容,特征声明包含以下信息: 元数据(Metadata):面向特征用户的一些描述性信息; 适用工作负载列表(Applies-to list):该特征可以应用的工作负载列表; 属性(Properties):可以被运维人员配置的属性。
工作负载类型和配置(Workload types and configurations)描述特定工作负载的底层运行时,平台需要能够提供对应工作负载的运行时,工作负载声明包含以下信息: 元数据(Metadata):面向工作负载用户的一些描述性信息; 工作负载设置(Workload Setting):可以被运维人员配置的设置。
应用配置(Application configuration)运维人员使用应用配置将组件、特征和应用边界的组合在一起实例化部署,应用配置声明包含以下信息: 元数据(Metadata):面向应用配置用户的一些描述性信息; 参数覆盖(Parameter overrides):可以理解为变量定义,可以被组件、特征、应用边界的参数引用; 组件设置(Component):构成应用的全部组件都在这里设置; 绑定组件的运维特征配置(Trait Configuration):绑定的特征列表及其参数。OAM 认为:一个云原生应用由一组相互关联但又离散独立的组件构成,这些组件实例化在合适的运行时上,由配置来控制行为并共同协作提供统一的功能。更加具体的说:一个 Application 由一组 Components 构成,每个 Component 的运行时由 Workload 描述,每个 Component 可以施加 Traits 来获取额外的运维能力,同时我们可以使用 Application scopes 将 Components 划分到 1 或者多个应用边界中,便于统一做配置、限制、管理。整体的运行模式如下所示:
组件、运维特征、应用边界通过应用配置(Application Configuration)实例化,然后再通过 OAM 的实现层翻译为真实的资源。
怎么用?使用 OAM 来管理云原生应用,其核心主要是围绕着“四个概念,一个动作”。![]()
四个概念 应用组件(包含对工作负载的依赖声明);【开发人员关心】 工作负载;【平台关心】 运维特征;【平台关心】 应用边界;【平台关心】
一个动作 下发应用配置;【运维人员关心】下发应用配置之后 OAM 平台将会实例化四个概念得到运行的应用。 一个 OAM 平台在对外提供 OAM 应用管理界面时,一定是预先已经准备好了“运维特征,应用边界”供运维人员使用,准备好了“工作负载”供开发者使用,同时开发者准备“组件”供运维人员使用。因此角色划分就很明了: 开发者提供组件,使用平台的工作负载; 运维人员关注一个动作实例化四个概念; 平台方需要提供工作负载,运维特征,应用边界,并且托管用户的应用组件;![]()
例子如何使用上面“四个概念,一个动作”来部署一个 OAM 应用呢?我们假想一个场景,用户的应用有两个组件:frontend 和 backend,其中 frontend 组件需要域名访问、自动扩缩容能力,并且 frontend 要访问 backend,两者应该在同一个 vpc 内,基于这个场景我们需要有: 开发者创建 2 个组件frontend 的 workload 类型是 Server 容器服务(OAM 平台提供该工作负载):apiVersion: core.oam.dev/v1alpha1 kind: ComponentSchematic metadata: name: frontend annotations: version: v1.0.0 description: "A simple webserver" spec: workloadType: core.oam.dev/v1.Server parameters: - name: message description: The message to display in the web app. type: string value: "Hello from my app, too" containers: - name: web env: - name: MESSAGE fromParam: message image: name: example/charybdis-single:latestbackend 的 workload 是 Cassandra(OAM 平台提供该工作负载):apiVersion: core.oam.dev/v1alpha1 kind: ComponentSchematic metadata: name: backend annotations: version: v1.0.0 description: "Cassandra database" spec: workloadType: data.oam.dev/v1.Cassandra parameters: - name: maxStalenessPrefix description: Max stale requests. type: int value: 100000 - name: defaultConsistencyLevel description: The default consistency level type: string value: "Eventual" workloadSettings: - name: maxStalenessPrefix fromParam: maxStalenessPrefix - name: defaultConsistencyLevel fromParam: defaultConsistencyLevel OAM 平台提供 Ingress TraitapiVersion: core.oam.dev/v1alpha1 kind: Trait metadata: name: Ingress spec: type: core.oam.dev/v1beta1.Ingress appliesTo: - core.oam.dev/v1alpha1.Server parameters: properties: | { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "host": { "type": "string", "description": "ingress hosts", }, "path": { "type": "string", "description": "ingress path", } } } OAM 平台提供网络应用边界apiVersion: core.oam.dev/v1alpha1 kind: ApplicationScope metadata: name: network annotations: version: v1.0.0 description: "network boundary that a group components reside in" spec: type: core.oam.dev/v1.NetworkScope allowComponentOverlap: false parameters: - name: network-id description: The id of the network, e.g. vpc-id, VNet name. type: string required: Y - name: subnet-ids description: > A comma separated list of IDs of the subnets within the network. For example, "vsw-123" or ""vsw-123,vsw-456". There could be more than one subnet because there is a limit in the number of IPs in a subnet. If IPs are taken up, operators need to add another subnet into this network. type: string required: Y - name: internet-gateway-type description: The type of the gateway, options are 'public', 'nat'. Empty string means no gateway. type: string required: NapiVersion: core.oam.dev/v1alpha1 kind: ApplicationConfiguration metadata: name: my-vpc-network spec: variables: - name: networkName value: "my-vpc" scopes: - name: network type: core.oam.dev/v1alpha1.Network properties: - name: network-id value: "[fromVariable(networkName)]" - name: subnet-ids value: "my-subnet1, my-subnet2" 应用运维部署整个应用使用应用配置将上面的组件、边界、特征组合起来即可部署一个应用,这个由应用的运维人员提供:apiVersion: core.oam.dev/v1alpha1 kind: ApplicationConfiguration metadata: name: custom-single-app annotations: version: v1.0.0 description: "Customized version of single-app" spec: variables: - name: message value: "Well hello there" - name: domainName value: "www.example.com" components: - componentName: frontend instanceName: web-front-end parameterValues: - name: message value: "[fromVariable(message)]" traits: - name: Ingress properties: - name: host value: "[fromVaraible(domainName)]" - name: path value: "/" applicationScopes: - my-vpc-network - componentName: backend instanceName: database applicationScopes: - my-vpc-network通过以上完整的使用流程我们可以看到:应用配置是真正部署应用的起点,在使用该配置的时候,对应的组件、应用边界、特征都要提前部署好,运维人员只需做一个组合即可。
服务组件(Component Schematic)服务组件的意义是让开发者声明离散执行单元的运行时特性,可以用 JSON/YAML 格式来表示,其声明遵循 Kubernetes API 规范,区别在于: 定义字段是 Kubernetes 的子集,因为 OAM 是更高的抽象; OAM Spec 的底层运行时可以不是 Kubernetes下面我们来看看具体的组件声明如何定义。
定义
顶层属性属性类型必填默认值描述apiVersionstringY特定oam spec版本,比如core.oam.dev/v1kindstringY类型,对于组件来说就是ComponentSchematicmetadataMetadataY组件元数据specSpecY组件特定的定义属性
Metadata属性类型必填默认值描述namestringYlabelsmap[string]stringNk/v对作为组件的lebelsannotationsmap[string]stringNk/v对作为组件的描述信息
Spec属性类型必填默认值描述parameters[]ParameterN组件的可配置项workloadTypestringY简明语义化的组件运行时描述,以K8s为例就是指定底层使用StatefulSet还是Deployment这样osTypestringNlinux组件容器运行时依赖的操作系统类型,可选值:- linux- windowsarchstringNamd64组件容器运行时依赖的CPU架构,可选值:- i386- amd64- arm- arm64containers[]ContainerN实现组件的OCI容器们workloadSettings[]WorkloadSettingsN需要传给工作负载运行时的非容器配置声明上面的 workloadType 后面会有详细说明,这里简要来说就是开发者可以指定该 field 告诉运行时该组件如何被执行。工作负载命名的规范是 GROUP/VERSION.KIND,和 K8s 资源类型坐标一致,比如: core.oam.dev/v1alpha1.Singletoncore.oam.dev 表示是 oam 内置的分组,oam 内置的表示任何 OAM 实现都支持该类型的工作负载,v1alpha1 表示依旧是 alpha 状态,类型是 Singleton,这表示运行时应该只运行一个组件实例,无论谁提供。 alibabacloud.com/v1.Functionalibabacloud.com 表示该对象是一个运营商特定的实现,不一定所有平台都实现,版本 v1 表示实现已经稳定,类型是 Function 表示运行时是 Alibaba Functions 提供的。 streams.oam.io/v1beta2.Kafka表示该对象是一个第三方组织实现,不一定所有平台都实现,版本 v1beta2 表示实现已经趋于稳定,类型是 Kafka 表示运行时是开源组件 Kafka 提供的。工作负载主要分为两类: 核心工作负载类型属于 core.oam.dev 分组,所有对象都需要 oam 平台实现,都是容器运行时,该 Spec 目前定义了如下核心工作负载类型:名字类型是否对外提供服务是否可以多副本运行是否常驻Servercore.oam.dev/v1alpha1.ServerYYYSingleton Servercore.oam.dev/v1alpha1.SingletonServerYNYWorkercore.oam.dev/v1alpha1.WorkerNYYSingletonWorkercore.oam.dev/v1alpha1.SingletonWorkerNNYTaskcore.oam.dev/v1alpha1.TaskNYNSingleton Taskcore.oam.dev/v1alpha1.SingletonTaskNNN Server:定义了容器运行时可以运行 0 或多个容器实例,该工作负载提供了冗余的可扩缩容的多副本常驻服务,在 K8s 平台可以使用 Deployment 或者 Statefulset 加上 Service 来实现; Singleton Server:定义了容器运行时只能运行一个容器实例,该工作负载提供了无法冗余的单副本常驻服务,在 K8s 平台可以使用副本为 1 的 Statefulset 加上 Service 来实现; Worker:定义了容器运行时可以运行 0 或多个容器实例,该工作负载提供了冗余的可扩缩容的多副本常驻实例但是不提供服务,在 K8s 平台可以使用 Deployment 或者 Statefulset 来实现; Singleton Worker:定义了容器运行时只能运行一个容器实例,该工作负载提供了无法冗余的单副本常驻实例但是不提供服务,在 K8s 平台可以使用副本为 1 的 Statefulset 来实现;
