作者:
刘洋(炎寻) 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 或者多个应用边界中,便于统一做配置、限制、管理。

整体的运行模式如下所示:

4 个概念,1 个动作,让应用管理变得更简单_第1张图片

组件、运维特征、应用边界通过应用配置(Application Configuration)实例化,然后再通过 OAM 的实现层翻译为真实的资源。

怎么用?

使用 OAM 来管理云原生应用,其核心主要是围绕着“四个概念,一个动作”。

4 个概念,1 个动作,让应用管理变得更简单_第2张图片

四个概念

  • 应用组件(包含对工作负载的依赖声明);【开发人员关心】
  • 工作负载;【平台关心】
  • 运维特征;【平台关心】
  • 应用边界;【平台关心】

一个动作

  • 下发应用配置;【运维人员关心】

下发应用配置之后 OAM 平台将会实例化四个概念得到运行的应用。
 
一个 OAM 平台在对外提供 OAM 应用管理界面时,一定是预先已经准备好了“运维特征,应用边界”供运维人员使用,准备好了“工作负载”供开发者使用,同时开发者准备“组件”供运维人员使用。因此角色划分就很明了:

  • 开发者提供组件,使用平台的工作负载;
  • 运维人员关注一个动作实例化四个概念;
  • 平台方需要提供工作负载,运维特征,应用边界,并且托管用户的应用组件;

4 个概念,1 个动作,让应用管理变得更简单_第3张图片

例子

如何使用上面“四个概念,一个动作”来部署一个 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:latest

backend 的 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 Trait
apiVersion: 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: N

 

apiVersion: 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

下面我们来看看具体的组件声明如何定义。

定义

顶层属性

属性 类型 必填 默认值 描述
apiVersion string Y 特定oam spec版本,比如core.oam.dev/v1
kind string Y 类型,对于组件来说就是
ComponentSchematic
metadata Metadata Y 组件元数据
spec Spec Y 组件特定的定义属性

Metadata

属性 类型 必填 默认值 描述
name string Y
labels map[string]string N k/v对作为组件的lebels
annotations map[string]string N k/v对作为组件的描述信息

Spec

属性 类型 必填 默认值 描述
parameters []Parameter N 组件的可配置项
workloadType string Y 简明语义化的组件运行时描述,以K8s为例就是指定底层使用StatefulSet还是Deployment这样
osType string N linux 组件容器运行时依赖的操作系统类型,可选值:
- linux
- windows
arch string N amd64 组件容器运行时依赖的CPU架构,可选值:
- i386
- amd64
- arm
- arm64
containers []Container N 实现组件的OCI容器们
workloadSettings []WorkloadSettings N 需要传给工作负载运行时的非容器配置声明

上面的 workloadType 后面会有详细说明,这里简要来说就是开发者可以指定该 field 告诉运行时该组件如何被执行。工作负载命名的规范是 GROUP/VERSION.KIND,和 K8s 资源类型坐标一致,比如:

  • core.oam.dev/v1alpha1.Singleton

core.oam.dev 表示是 oam 内置的分组,oam 内置的表示任何 OAM 实现都支持该类型的工作负载,v1alpha1 表示依旧是 alpha 状态,类型是 Singleton,这表示运行时应该只运行一个组件实例,无论谁提供。

  • alibabacloud.com/v1.Function

alibabacloud.com 表示该对象是一个运营商特定的实现,不一定所有平台都实现,版本 v1 表示实现已经稳定,类型是 Function 表示运行时是 Alibaba Functions 提供的。

  • streams.oam.io/v1beta2.Kafka

表示该对象是一个第三方组织实现,不一定所有平台都实现,版本 v1beta2 表示实现已经趋于稳定,类型是 Kafka 表示运行时是开源组件 Kafka 提供的。

工作负载主要分为两类:

  • 核心工作负载类型

属于 core.oam.dev 分组,所有对象都需要 oam 平台实现,都是容器运行时,该 Spec 目前定义了如下核心工作负载类型:

名字 类型 是否对外提供服务 是否可以多副本运行 是否常驻
Server core.oam.dev/v1alpha1.Server Y Y Y
Singleton Server core.oam.dev/v1alpha1.SingletonServer Y N Y
Worker core.oam.dev/v1alpha1.Worker N Y Y
Singleton
Worker
core.oam.dev/v1alpha1.SingletonWorker N N Y
Task core.oam.dev/v1alpha1.Task N Y N
Singleton Task core.oam.dev/v1alpha1.SingletonTask N N N
  • Server:定义了容器运行时可以运行 0 或多个容器实例,该工作负载提供了冗余的可扩缩容的多副本常驻服务,在 K8s 平台可以使用 Deployment 或者 Statefulset 加上 Service 来实现;

  • Singleton Server:定义了容器运行时只能运行一个容器实例,该工作负载提供了无法冗余的单副本常驻服务,在 K8s 平台可以使用副本为 1 的 Statefulset 加上 Service 来实现;

  • Worker:定义了容器运行时可以运行 0 或多个容器实例,该工作负载提供了冗余的可扩缩容的多副本常驻实例但是不提供服务,在 K8s 平台可以使用 Deployment 或者 Statefulset 来实现;

  • Singleton Worker:定义了容器运行时只能运行一个容器实例,该工作负载提供了无法冗余的单副本常驻实例但是不提供服务,在 K8s 平台可以使用副本为 1 的 Statefulset 来实现;

  • Task:定义了非常驻可冗余的容器运行时,运行完就退出,可以赋予扩缩容特性,在 K8s 平台可以使用 Job 来实现;

  • Singleton Task:定义了非常驻非冗余的容器运行时,运行完就退出,在 K8s 平台可以使用 completions=1 的 Job 来实现。

核心工作负载必须给定 container 部分,实现核心工作负载的 OAM 平台必须不依赖 workloadSettings。

  • 扩展工作负载类型

扩展工作负载类型是平台运行时特定的,由各个 OAM 平台自定提供,当前版本的 Spec 不支持用户自定义工作负载类型,只能是平台方提供,扩展工作负载可以使用非容器运行时,workloadSettings 的目的就是为了扩展工作负载类型的配置。

工作负载的定义是包罗万象的,他允许任何部署的服务作为工作负载,无论是容器化还是虚拟机,基于此,我们可以将缓存,数据库,消息队列都作为工作负载,如果组件指定的工作负载在平台没有提供,应该快速失败将信息返回给用户。

除了 WorkloadType,可以看到组件 Spec 内嵌了 3 种结构体,下面来看看它们的定义:

1.Parameter

定义了该组件的所有可配置项,其定义如下:

属性 类型 必填 默认值 描述
name string Y
description string N 组件的简短描述
type string Y 参数类型,JSON定义的boolean, number, ... 
required boolean N false 参数值是否必须提供
default 同上面的type N 参数的默认值

2.Container

属性 类型 必填 默认值 描述
name string Y 容器名字,在组件内必须唯一
iamge string Y 镜像地址
resources Resources Y 镜像运行最小资源需求
env []Env N 环境变量
ports []Port N 暴露端口
livenessProde HealthProbe N 健康状态检查指令
readinessProbe HealthProbe N 流量可服务状态检查指令
cmd []string N 容器运行入口
args []string N 容器运行参数
config []ConfigFile N 容器内可以访问的配置文件
imagePullSecrets string N 拉取容器的凭证

其中 Resources 的定义如下:

属性 类型 必填 默认值 描述
cpu CPU Y 容器运行所需的cpu资源
memory Memory Y 容器运行所需的memory资源
gpu GPU N 容器运行所需的gpu资源
volumes []Volume N 容器运行所需的存储资源
extended []ExtendedResource N 容器运行所需的外部资源

其中 CPU/Memory/GPU 都是数值型,不赘述,Volume 的定义如下:

属性 类型 必填 默认值 描述
name string Y 数据卷的名字,引用的时候使用
mountPath string Y 实际在文件系统的挂载路径
accessMode string N RW 访问模式,RW或RO
sharingPolicy string N Exclusive 挂载共享策略,Exclusive或者Shared
disk Disk N 该数据卷使用底层磁盘资源的属性

Disk 指定存储是否需要持久化,最小的空间需求,定义如下:

属性 类型 必填 默认值 描述
required string Y 最小磁盘大小需求
ephemeral boolean N 是否需要挂载外部磁盘

ExtendedResource 描述特定实现的资源需求,比如 OAM 运行时平台可能提供特殊的硬件,这个字段允许容器声明需要这个特定的 offering:

属性 类型 必填 默认值 描述
required string Y 需要的条件
name string Y 资源名字,比如GV.K

Env,Port,HealthProbe 和 K8s 类似,这里不再赘述。

3.WorkloadSetting

工作负载的附加配置,用于非容器运行时(当然,也可以用于容器运行时工作负载的附加字段)。

属性 类型 必填 默认值 描述
name string Y 参数名
type string N string 参数类型,用于实现的hint
value any N 参数值,如果没有fromParam则用该值
fromParam string N 参数引用,覆盖value

这组配置会传给运行时,一个运行时可以返回错误,如果特定的限制没有满足,比如:

  • 丢失了期望的 k/v 对;
  • 不认识的 k/v 对;

例子

使用核心工作负载 Server 的组件声明

apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: frontend
  annotations:
    version: v1.0.0
    description: >
      Sample component schematic that describes the administrative interface for our Twitter bot.
spec:
  workloadType: core.oam.dev/v1alpha1.Server
  osType: linux
  parameters:
  - name: username
    description: Basic auth username for accessing the administrative interface
    type: string
    required: true
  - name: password
    description: Basic auth password for accessing the administrative interface
    type: string
    required: true
  - name: backend-address
    description: Host name or IP of the backend
    type: string
    required: true
  containers:
  - name: my-twitter-bot-frontend
    image:
      name: example/my-twitter-bot-frontend:1.0.0
      digest: sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b
    resources:
      cpu:
        required: 1.0
      memory:
        required: 100MB
    ports:
    - name: http
      value: 8080
    env:
    - name: USERNAME
      fromParam: 'username'
    - name: PASSWORD
      fromParam: 'password'
    - name: BACKEND_ADDRESS
      fromParam: 'backend-address'
    livenessProbe:
      httpGet:
        port: 8080
        path: /healthz
    readinessProbe:
      httpGet:
        port: 8080
        path: /healthz

使用扩展工作负载的组件声明

apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: alibabacloudFunctions
  annotations:
    version: v1.0.0
    description: "Extended workflow example"
spec:
  workloadType: alibabacloud.com/v1.Function
  parameters:
  - name: github-token
    description: GitHub API session key
    type: string
    required: true
  workloadSettings:
    - name: source
      value: git://git.example.com/function/myfunction.git
    - name: github_token
      fromParam: github-token

总结

组件声明是由开发者或者 OAM 平台给出,透出应用运行的可配置项、依赖的平台和工作负载,可以看成是一个声明了运行环境的函数定义,运维人员填写函数参数之后,组件就会按照声明的功能运行起来。

应用边界(Application scopes)

应用边界通过提供不同形式的应用边界以及共有的分组行为来将组件组成逻辑的应用,应用边界具备以下通用的特征:

  • 应用边界应该描述该组组件实例的共有行为和元数据;
  • 一个组件可以同时部署到多个不同类型的应用边界中;
  • 应用边界类型可以决定组件是否可以部署到多个相同的应用边界类型实例;
  • 应用边界可以用于不同组件分组以及不同 infra 能力之间的连接机制,比如 networking 或者外部能力(如验证服务);

下图说明了组件可以属于多个重叠的应用分组,最终创建出不同的应用边界。

4 个概念,1 个动作,让应用管理变得更简单_第4张图片

上图有两种应用边界类型:Network 与 Health,有四个组件分布在不同的应用边界实例中:

  • A、B、C 三个组件部署到了相同的 Health scope,该 scope 会收集所有属于这个边界的组件状态和信息,可以给 Traits 或者其他组件使用;
  • 组件 A 和 B、C、D 的网络边界是隔离的,这允许 infra 运维提供不同的 SDN 设置,控制不同分组的流量流入/流出规则;

类型

主要是有两种应用边界类型:

  • 核心应用边界类型
  • 扩展应用边界类型

核心应用边界类型

定义基本运行时行为的分组结构,它们拥有如下特征:

  • 必须是 core.oam.dev 命名空间;
  • 必须被全部实现;
  • 核心工作负载类型实例必须部署到所有核心应用边界类型实例中;
  • 运行时必须为每种应用边界类型提供默认的应用边界实例;
  • 运行时如果组件实例没有指定特定的应用边界,必须将该组件实例部署到默认应用边界实例上;

当前 Spec 定义的应用边界类型有:

Name Type Description
Network core.oam.dev/v1alpha1.Network 该边界将组件组织到一个子网边界并且定义统一的运行时网络模型,以及infra网络描述的定义和规则
Health core.oam.dev/v1alpha1.Health 该边界将组件组织到一个聚合的健康组中,信息可以用于回滚和升级

扩展应用边界类型

对于运行时来说是 optional 的,可以自定义。

定义

apiVersion,kind,metadata 和前面组件一致,不赘述,主要描述 Spec:

属性 类型 必填 默认值 描述
type string Y 应用边界类型
allowComponentOverlap bool Y 决定是否允许一个组件同时出现在多个该类型应用边界实例中
parameters []Parameter N 边界的可配置参数

例子

Network scope(core)

用于将组件划分到一个网络或者 SDN 中,网络本身必须被 infra 定义和运维,也可以被流量管理 Traits 查询,用于发现 service mesh 的可发现边界或者 API 网关的 API 边界:

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-id
      description: The id of the subnet within the 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: N

Health scope(core)

用于聚合组件的健康状态,可以设计的参数有健康阈值(超过该阈值的组件不健康则认为整个边界不健康),健康边界实例本身不会用健康状态做任何操作,它只是分组健康聚合器,其信息可以被查询并用于其他地方,比如:

  • 应用的变更 Traits 可以监控健康状态来决定何时回滚;
  • 监控 Traits 可以监控健康状态来触发报警。
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationScope
metadata:
  name: health
  annotations:
    version: v1.0.0
    description: "aggregated health state for a group of components."
spec:
  type: core.oam.dev/v1alpha1.HealthScope
  allowComponentOverlap: true
  parameters:
    - name: probe-method
      description: The method to probe the components, e.g. 'httpGet'.
      type: string
      required: true
    - name: probe-endpoint
      description: The endpoint to probe from the components, e.g. '/v1/health'.
      type: string
      required: true
    - name: probe-timeout
      description: The amount of time in seconds to wait when receiving a response before marked failure.
      type: integer
      required: false
    - name: probe-interval
      description: The amount of time in seconds between probing tries.
      type: integer
      required: false
    - name: failure-rate-threshold
      description: If the rate of failure of total probe results is above this threshold, declared 'failed'.
      type: double
      required: false
    - name: healthy-rate-threshold
      description: If the rate of healthy of total probe results is above this threshold, declared 'healthy'.
      type: double
      required: false
    - name: health-threshold-percentage
      description: The % of healthy components required to upgrade scope
      type: double
      required: false
    - name: required-healthy-components
      description: Comma-separated list of names of the components required to be healthy for the scope to be health.
      type: []string
      required: false

resource quota scope(extended)

限制分组内所有组件的资源使用总量上限。

apiVersion: core.oam.dev/v1alpha1
kind: ApplicationScope
metadata:
  name: myResourceQuotas
  annotations:
    version: v1.0.0
    description: "The production configuration for Corp CMS"
spec:
  type: resources.oam.dev/v1.ResourceQuotaScope
  allowComponentOverlap: false
  parameters:
    - name: CPU
      description: maximum CPU to be consumed by this scope
      type: double
      required: Y
    - name: Memory
      description: maximum memory to be consumed by this scope
      type: double
      required: Y

总结

应用边界声明由 OAM 平台提供,透出应用边界实例运行的可配置项,可以看成是一个函数定义,运维人员或者平台填写函数参数之后,应用边界就会按照声明的功能运行起来,对该边界内的组件们起作用。

应用特征(Traits)

OAM Spec 的实现平台应该提供 Traits 给组件工作负载增强运维操作,一个 Trait 是一种自由的运行时,增强工作负载提供额外的功能,比如流量路由规则、自动扩缩容规则、升级策略等,这让应用运维具备根据需求配置组件,不需要开发者参与的能力。一个独立的 Trait 可以绑定 1 或多个工作负载类型,它可以声明哪些工作负载类型才能使用该Trait。

规则

  • 目前并没有机制来显示约定组件的多个 Traits 组合,也就是一个组件应用了 Trait A 无法要求 Trait B 必须应用于该组件,如果在运行时发生存在 Trait A 但是 Trait B 不存在,应该标记 Trait A 失败;
  • Traits 应该按照定义的顺序施加到组件上;
  • 应用部署只有当所有组件和其 Traits 都正常运行起来才能标记为部署成功;
  • OAM 平台应该支持组件施加多个 Traits,这些 Traits 可能是相同的类型;
  • OAM 对 Trait 的实现没有任何限制,Trait 一般作用于应用的安装和升级时;

分类

目前 Traits 主要分为三类:

  • Core Traits: core Traits 属于 core.oam.dev 分组,是一些必要的运维特征,所有 OAM 平台必须实现;
  • Standard Traits: standard Traits 属于 standard.oam.dev 分组里面,是一些常用的运维特征,推荐 OAM 平台实现;
  • Extensions Traits: extension Traits 是自定义 Traits,其分组也是自定义,是平台特定的运维特征(通常是特定 OAM 平台差异性)的体现。

定义

apiVersion,kind,metadata 和前面组件一致,不赘述,主要描述 Spec:

属性 类型 必填 默认值 描述
appliesTo []string N ["*"] 该Trait可以应用的工作负载类型
properties []Properties N Trait的可配置参数,使用JSON Schema来表达。

例子

Manual Scaler(core)

apiVersion: core.oam.dev/v1alpha1
kind: Trait
metadata:
  name: ManualScaler
  annotations:
    version: v1.0.0
    description: "Allow operators to manually scale a workloads that allow multiple replicas."
spec:
  appliesTo:
    - core.oam.dev/v1alpha1.Server
    - core.oam.dev/v1alpha1.Worker
    - core.oam.dev/v1alpha1.Task
  properties: |
    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "required": ["replicaCount],
      "properties": {
        "replicaCount": {
          "type": "integer",
          "description": "the target number of replicas to scale a component to.",
          "minimum": 0
        }
      }
    }

上面是一个手动扩缩容服务的 Trait,只有一个参数就是 replicaCount。

总结

应用特征声明由 OAM 平台提供,透出应用特征的可配置项,标明了可作用于的工作负载,可以看成函数定义,运维人员或者平台填写实参之后,应用特征就会按照声明的功能运行起来,对绑定的组件起作用。

应用配置(Application Configuration)

应用配置主要是描述应用如何被部署的,一个组件可以部署到任意的运行时,我们称一个组件的一次部署为实例,每次组件部署的时候必须有应用配置。

应用配置由应用运维管理,提供当前组件实例的信息:

  • 特定组件的基本信息:名字、版本、描述;
  • 组件及其相关组件定义 parameters 的赋值;
  • 组件要施加的 Trait 以及 Trait 的配置。

概念

实例与升级(Instances and upgrades)

一个实例是组件的可追溯部署,当组件部署时创建,后续该组件的升级都是修改该实例,回滚/重新部署都属于升级,实例都会有名字方便引用。当一个实例首次创建时,处于初始发行 (release) 状态,每次升级操作之后,一个新的发行就会创建。 

发行(Releases)

任何对组件本身或者其配置的变更都会创建一个新的发行,一个发行就是应用配置以及它对组件、应用特征、应用边界的定义,当一个发行被部署,对应的组件、应用特征和应用边界也会被部署。

基于该定义,平台需要保证以下变更语义:

  • 如果新的发行包含了旧发行不存在的组件,平台需要创建该组件;
  • 如果新的发行不包含旧发行存在的组件,平台需要删除该组件;
  • 应用特征和应用边界与组件的变更语义一致。

运行时与应用配置(Runtime and Application Configuration)

一个组件可以部署到多个不同的运行时,在每个运行时中应用配置的实例与应用配置之间是 1:1 的关系,应用配置由应用运维管理,包含 3 个主要部分:

  • 参数:运维人员在部署时可以定义的参数;
  • 应用边界列表:一组应用边界列表,每个应用边界定义对应的参数;
  • 组件实例定义:定义一个组件实例如何部署,这个定义本身有 3 个部分:

    • 组件参数的定义;
    • Traits 列表:每个 Trait 定义对应的参数;
    • 应用边界列表:该组件应该部署到的应用边界列表。

定义

apiVersion,kind,metadata 和前面组件一致,不赘述,主要描述 Spec:

属性 类型 必填 默认值 描述
variables []Variable N 可以在参数值和属性中引用的变量
scopes []Scope N 应用边界定义
components []Component N 组件实例定义

variables 就是一个 k/v 对,一个集中的地方定义运维的变量,在运维配置的其他地方都可以用 fromVariable(VARNAME) 引用:

属性 类型 必填 默认值 描述
name string Y 变量名字
value string Y 标量值

scopes 定义该运维配置将要创建的应用边界,其定义为:

属性 类型 必填 默认值 描述
name string Y 应用边界名字
type string Y 应用边界的GROUP/VERSION.KIND
properties Properties N 覆盖边界的参数

components 是组件实例定义,而不是组件定义:

属性 类型 必填 默认值 描述
componentName string Y 组件名
instanceName string Y 组件实例名
parameterValues []ParameterValue N 覆盖组件的参数
Traits []Trait N 指定组件实例绑定的Traits
applicationScopes []string N 指定组件运行的应用边界

Trait 在这里的定义是:

属性 类型 必填 默认值 描述
name string Y Trait实例名
properties Properties N 覆盖Trait的参数

例子

apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: my-app-deployment
  annotations:
    version: v1.0.0
    description: "Description of this deployment"
spec:
  variables:
    - name: VAR_NAME
      value: SUPPLIED_VALUE
  scopes:
    - name: core.oam.dev/v1alpha1.Network
      parameterValues:
        - name: PARAM_NAME
          value: SUPPLIED_VALUE
  components:
    - componentName: my-web-app-component
      instanceName: my-app-frontent
      parameterValues:
        - name: PARAMETER_NAME
          value: SUPPLIED_VALUE
        - name: ANOTHER_PARAMETER
          value: "[fromVariable(VAR_NAME)]"
      traits:
        - name: Ingress
          properties:
            CUSTOM_OBJECT:
              DATA: "[fromVariable(VAR_NAME)]"

总结

应用配置定义由运维人员或者 OAM 平台提供,描述应用的部署,可以看成是一个函数调用,运维人员或者 OAM 平台填写实参之后,调用之前定义的组件、应用特征、应用边界等函数,这些实例一起作用对外提供应用服务。

工作负载类型(Workload Types)

Workload 类型和 Trait 一样由平台提供,所以用户可以查看平台提供哪些工作负载,对于平台用户来说工作负载类型无法扩展,只能由平台开发者扩展提供,因此平台一定不允许用户创建自定义的工作负载类型。

定义

apiVersion,kind,metadata 和前面组件类似,不赘述,这里主要描述 Spec,定义组件如何使用工作负载类型,除此之外暴露了底层工作负载运行时的可配置参数:

属性 类型 必填 默认值 描述
group string Y 该工作负载类型所属的group
names Names Y 该工作负载类型的关联名字信息
settings []Setting N 该工作负载的设置选项

Names 就是描述了对应类型的不同形式名字引用:

属性 类型 必填 默认值 描述
kind string Y 工作负载类型的正确引用名字,比如Singleton
singular string N 单数形式的可读名字,比如singleton
plural string N 复数形式的可读名字,比如singletons

Setting 描述工作负载可配置部分,类似前面组件的 Parameters,都是 schema:

属性 类型 必填 默认值 描述
name string Y 配置名,每个workload类型必须唯一
description string N 配置说明
type string Y 配置类型
required bool N false 是否必须提供
default indicated by type N 默认值

价值

通过上面的介绍,我们了解了 OAM Spec 里面的基本概念和定义,以及如何使用它们来描述应用交付和运维流程。然而,OAM 能给我们带来什么样的价值呢?我们评判一个好的架构体系,不仅是因为它在技术上更先进,更主要的是它能够解决一些实际问题,为用户带来价值。所以,接下来我们将总结一下这方面的内容。

OAM 的价值要从下往上三个层面来说起。

1. 从基础设施层面

基础设施,指的是像 K8s 这类的提供基础服务能力与抽象的一层服务体系。拿 K8s 来说,它提供了许多种类的基础服务和强大的扩展能力来灵活扩展其他基础服务。

但是,使用基础设施的运维人员很快就发现 K8s 存在一个问题:缺乏统一的机制来注册和管理自定义扩展能力。这些扩展能力的表达方式不够统一,有些是 CRD、有些是 annotation、有些是 Config...

这种乱象使得基础设施用户不知道平台上都提供了哪些能力,不知道怎么使用这些能力,更不知道这些能力互相之间的兼容组合关系。

OAM 提供了抽象(如 Workload/Trait 等)来统一定义和管理这些能力。有了 OAM,各平台实现就有了统一的标准规范去透出公共的或差异化的能力:公共的基础服务像容器部署、监控、日志、灰度发布;差异化的、高级复杂的能力像 CronHPA(周期性定时变化的强化版 HPA)。

2. 从应用运维者层面

应用运维,指的是像给应用加上网络接入、复杂均衡、弹性伸缩、甚至是建站等运维操作。但是,运维的一个痛点就是原来这些能力并不是跨平台的:这导致在不同平台、不同环境下去部署和运维应用的操作,是不互通和不兼容的。

上面这个问题,是客户应用、尤其是传统 ERP 应用上云的一大阻碍。我们做 OAM 的一个初衷,就是通过一套标准定义,让不同的平台实现也通过统一的方式透出。我们希望:哪怕一个应用不是生在云上、长在云上,也能够赶上这趟通往云原生未来的列车,拥抱云带来的变化和红利!

OAM 提供的抽象和模型,是我们通往统一、标准的应用架构的强有力工具。这些标准能力以后都会通过 OAM 输出,让运维人员轻易去实现跨平台部署。

3. 从应用开发者层面

应用开发,指的就是业务逻辑开发,这是业务产生价值的核心位置。

也正因如此,我们希望,应用开发者能够专注于业务开发,而不需要关心运维细节。但是,K8s 提供的 API,并没有很好地分离开发和运维的关注点,开发和运维之间需要来回沟通以避免产生误解和冲突。

OAM 分离了开发和运维的关注点,很好地解决了以上问题,让整个发布流程更加连贯、高效。

下一步

目前,OAM 已经在阿里云 EDAS 等多个项目中进行了数月的内部落地尝试。我们希望通过一套统一、标准的应用定义体系,承载云应用管理项目产品与外部资源关系的高效管理体验,并将这种体验统一带给了基于 Function、ECS、Kubernetes 等不同运行时的应用管理流程;通过应用特征系统,将多个阿里云独有的能力进行了模块化,大大提高了阿里云基础设施能力的交付效率。

经过了前一段努力的铺垫,我们也慢慢明确了接下来的工作方向:

  • 将接入更多的云产品服务,为用户将跨平台应用交付的能力最大化;
  • 提供 OAM framework 等工具和框架,帮助新的 OAM 平台开发者去快速、简单地搭建 OAM 服务,接入 OAM 标准;
  • 推动开源生态建设,以标准化的方式帮助“应用”高效和高质量地交付到任何平台上去。

社区共建

为了能够让社区更加高效、健康的运转下去,我们非常期待得到您的反馈,并与大家密切协作,针对 Kubernetes 和任意云环境打造一个简单、可移植、可复用的应用模型。参与方式:

  • 通过 Gitter 直接参与讨论:https://gitter.im/oam-dev/;
  • 选择钉钉扫码进入 OAM 项目中文讨论群。

欢迎你与我们一起共建这个全新的应用管理生态!

“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

更多相关信息,请关注“阿里巴巴云原生”。