1. Purpose and Goals #
Open Application Model 的目标是定义一种标准的、与基础设施无关的方法,用于描述跨混合环境、云甚至边缘设备的应用部署。
该模型要解决的核心问题是如何组成分布式应用程序,然后成功地将其交给负责操作的人员。问题不在于如何编写程序,而在于如何采用面向服务(或面向微服务)架构的组件,并简化围绕此类应用的工作流程。
例如,当代的云应用程序可能由几十个微服务组成,每个微服务负责广义上的 application 的一个独立部分。此类应用程序需要进行配置、部署、审核、更新和删除。有时必须将应用程序作为一个整体来处理,有时则需要更精细的粒度。最重要的是,此类应用程序通常不是由一个人或一个团队管理,而是由多个团队管理,他们必须通力合作,以实现可靠性、稳定性和及时性。
该模型提供了对此类 workflow 的描述,描述本身具有声明性、可扩展性和最佳清晰度。此外,它还提出了操作此类应用程序的模式和流程。该模型将重点关注 cloud native(即 highly distributed)应用,涵盖公共云技术、内部部署解决方案以及物联网/边缘技术。这为现代应用交付系统奠定了坚实的基础,为云原生应用部署提供了标准但更高层次的描述。
Non-goals 包括
- 定义或规定特定的协调工具。
- 定义运行资源的模式,例如(但不限于):
- Secrets (secure, encrypted values)
- Networks
- Volumes
- 描述或定义运行时基础设施本身。
2. Overview and Terminology #
本节概述了 Open Application Model (OAM) 及其术语。本节首先确定了运行云本地应用程序过程中涉及的组织角色。然后,介绍本文档中使用的特定术语。
本文档提出的云本地应用程序定义模型如下:
云本地应用程序是一系列相互关联但又互不关联的组件(服务、任务、工作者)的集合,当这些组件与配置耦合并在合适的运行时基础架构中实例化时,可共同完成统一的功能目的。
在当前版本中,该应用模式定义了以下内容:
- Components 代表一个可运行的单元。
- Workload types 标识组件可执行的不同工作负载
- Traits 是覆盖层,可通过附加的特定操作功能来增强组件。Traits 代表操作员关注的问题,而不是开发人员/软件所有者关注的问题。
- Application scopes 通过对具有共同属性或依赖性的组件进行分组,代表了应用的边界。
- Application configuration 集合了一组组件实例、它们的特性、它们所在的应用范围,并结合了配置参数和元数据。
因此,应用程序是具有一系列运行特征的组件的集合,并被集中到一个或多个应用程序边界中。
3. Component Model #
本节定义组件模型。
组件描述了可作为更大的分布式应用程序的一部分进行实例化的功能单元。应用程序部分将介绍如何将组件组合在一起以及如何配置这些组件的实例,而本节将重点介绍组件模型本身。
组件定义(ComponentDefinition)实体的作用是允许组件提供者以基础设施中立格式声明此类执行单元的运行时特性。
例如,应用程序中的每个微服务都被描述为一个组件。请注意,ComponentDefinition 本身并不是该微服务的实例,而是该微服务可配置属性的声明。这些可配置属性应作为参数列表公开,以便应用团队在部署时设置并实例化该组件。
在实践中,一个简单的容器化工作负载、一个 Helm 图表或一个云数据库都可以建模为一个组件。
Top-Level Attributes #
以下是提供组件定义顶层信息的属性。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | A string that identifies the version of the schema the object should have. The core types uses core.oam.dev/v1beta1 in this version of model |
|
kind |
string |
Y | Must be ComponentDefinition |
|
metadata |
Metadata |
Y | Entity metadata. | |
spec |
Spec |
Y | The specification for the component definition. |
Metadata #
该元数据部分由几个 top-level keys 组成。元数据提供有关对象内容的信息。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | A name for the schematic. name is subject to the restrictions listed beneath this table. |
|
labels |
map[string]string |
N | A set of string key/value pairs used as arbitrary labels on this component. See the “Label format” section immediately below. | |
annotations |
map[string]string |
N | A set of string key/value pairs used as arbitrary descriptive text associated with this object. See the “Annotations format” section immediately below. |
name
:group、kind、name 的组合必须是唯一的。两个不同的种类(例如组件和特质)可以使用相同的名称,而不会造成冲突。Version 不是区分因素。
Okay: 每种类型都允许使用名称 foo
。
apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
name: foo
---
apiVersion: core.oam.dev/v1alpha2
kind: Trait
metadata:
name: foo
Okay: 每个组(one.dev
和 other.dev
)都允许使用名称为 foo
的组件。
apiVersion: one.dev/v1alpha2
kind: Component
metadata:
name: foo
---
apiVersion: other.dev/v1alpha2
kind: Component
metadata:
name: foo
NOT Okay: Version 不是命名空间限定符。
apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
name: foo
---
apiVersion: core.oam.dev/v1
kind: Component
metadata:
name: foo
name 字段的格式必须如下:
name 字段为必填字段,必须在 63 个字符以内,以字母数字字符([a-z0-9A-Z])开头和结尾,中间包含破折号 (-)、下划线 (_)、点 (.)和字母数字。
除非另有说明,否则 name 在 Group/Version/Kind 中必须是唯一的。
- Label:标签遵循 Kubernetes 规范 进行标记:
有效的标签密钥有两部分:可选的前缀和名称,以斜线 (/) 分隔。名称部分是必需的,长度必须在 63 个字符以内,以字母数字字符([a-z0-9A-Z])开头和结尾,中间包含破折号 (-)、下划线 (_)、点 (.)和字母数字。前缀为可选项。如果指定了前缀,则前缀必须是 DNS 子域:由点(.)分隔的一系列 DNS 标签,总长度不超过 253 个字符,后跟斜线 (/)。
- Annotations:注解提供了一种在对象元数据中附加任意文本的机制。注释对象遵循 Kubernetes 规范:
注释是键/值对。有效的注解键有两个部分:可选的前缀和名称,以斜线(/)分隔。名称部分是必填项,必须少于或等于 63 个字符,以字母数字字符([a-z0-9A-Z])开头和结尾,中间包含破折号 (-)、下划线 (_)、点 (.)和字母数字。前缀为可选项。如果指定了前缀,则前缀必须是 DNS 子域:由点(.)分隔的一系列 DNS 标签,总长度不超过 253 个字符,后跟斜线 (/)。
下列注释标签是 _predefined_ 和 RECOMMENDED
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
description |
string |
N | A short description of the component. | |
version |
string |
N | A user provided string defining the semantic version of the component, e.g. the release version of this software |
如果未提供 version
,则根据
SemVer 规范,默认版本假定为 0.1.0
。
例子:
metadata:
name: alpine-task
labels:
app: my-app-v1 # Non-normative example
annotations:
version: "1.0.1"
description: A task that is backed by an Alpine Linux filesystem
元数据部分用于所有示意图。它也与 Kubernetes 元数据部分兼容。但请注意,Kubernetes 元数据是 OAM 元数据的超集,包含 OAM 无法识别的属性。
Group, Version, and Kind #
本文档中描述的许多 API 对象都使用了一种名为 “Group, Version, Kind” 的命名方案。该方案由 Kubernetes 推广,为 API 对象的命名间距和版本提供了一致的方式,在此用于 OAM API 对象的版本控制。本节将介绍该方案。
Group #
组是收集多个相关 kinds 的 namespace 。组使用 DNS 命名约定。组的示例有
- components.oam.dev
- functions.azure.com
- my.dev
oam.dev 域下的所有组都被视为 OAM 对象的保留组。此处指定的所有对象都属于该域中的组。
组必须是全局唯一的。
Version #
version 字符串是 API 的版本。按照通用范例,API 仅按主版本号进行版本控制。API 版本中省略了次版本号和补丁号。底层引擎的实际次要版本和补丁版本可能会重复,但它们必须重复此版本以处理任何破坏性更改。换句话说,主版本号是兼容性的保证,次版本号和补丁号不应改变这一保证。因此,API 的用户不能指定比主版本更细的粒度。
API 版本号的前缀总是 v,后面跟一个或多个数字。
- v1
- v2
- v973
主要版本号中还有两个附加修饰符:
alphaN
(其中 N 为一位或多位数)表示该功能是试验性的,可能会被移除,但其当前的兼容性标记为 1。betaN
(其中 N 为一位或多位数)表示该特征尚未稳定。N 是兼容性标记。
标记为 alpha
或 beta
的 API 版本被认为是不稳定的,容易出现破坏性更改。
一次只能使用两种修改器中的一种:
v1alpha1
v973beta231
兼容性只能通过精确匹配来确定。v1
与 v2
、v1alpha1
或 v1beta2
不兼容。
版本没有唯一性要求。
Kind #
种类是类型的名称。例如,组件的种类是 “组件”(Component),而特质的种类是 “特质”(Trait)。种类总是由首字母大写的单词组成,每个单词的第一个字母都要大写。种类应大写首字母缩写的每个字母(例如,HTTP,而不是 Http)。
在一个组中,种类必须是唯一的。
Group/Version/Kind 的表述 #
Group/Version/Kind 的完全限定表示法是 GROUP/VERSION.KIND。下面是一些示例:
local.dev/v7alpha2.Proxy
cache.example.com/v1.Redis
azure.com/v2.Functions
在示意图中,Group 和 Version 在一个字段中显示,Kind 在另一个字段中显示:
apiVersion: local.dev/v7alpha2
kind: Proxy
在极少数情况下,有必要链接组和种类,但不指定版本。例如,在声明默认工作负载时就需要这样做。一般情况下,不推荐这种行为,但在必要时,本规范会遵循 Kubernetes 的模式,用复数种类名称和组来构建 DNS 名称:
Proxies.local.dev # allowed but discouraged
这种形式不被接受为完全合格版本的替代形式。只有在模型明确规定接受这种形式的情况下才会被接受。
Spec #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
workload |
WorkloadTypeDescriptor |
Y | Identifier to workload type of this component. | |
schematic |
Schematic | Y | Schematic information for this component. |
WorkloadTypeDescriptor #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
type |
string |
Y | A reference to a WorkloadDefinition via name. |
|
definition |
WorkloadGVK | Y | Mutually exclusive to type , a reference to WorkloadDefinition via group, version, and kind. |
WorkloadGVK #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | The API version the workload type. | |
kind |
string |
Y | The API kind of the workload type. |
Schematic #
本节声明了一个组件的示意图,该组件可在以后的部署工作流程中作为应用程序的一部分实例化。请注意,OAM 本身并不强制要求如何实现示意图,只要它能做到以下几点即可:
- 为可部署单元建模;
- 公开 JSON 模式或等效参数列表。
在 KubeVela 中,目前支持以下方案实现(“cue”、“helm “和 “kube”)。
Example #
下面是一个完整的基于 CUE 的组件定义示例,名为 “webserver”:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: webserver
annotations:
definition.oam.dev/description: "webserver is a combo of Deployment + Service"
spec:
workload:
definition:
apiVersion: apps/v1
kind: Deployment
schematic:
cue:
template: |
output: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: {
selector: matchLabels: {
"app.oam.dev/component": context.name
}
template: {
metadata: labels: {
"app.oam.dev/component": context.name
}
spec: {
containers: [{
name: context.name
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
if parameter["env"] != _|_ {
env: parameter.env
}
if context["config"] != _|_ {
env: context.config
}
ports: [{
containerPort: parameter.port
}]
if parameter["cpu"] != _|_ {
resources: {
limits:
cpu: parameter.cpu
requests:
cpu: parameter.cpu
}
}
}]
}
}
}
}
// an extra template
outputs: service: {
apiVersion: "v1"
kind: "Service"
spec: {
selector: {
"app.oam.dev/component": context.name
}
ports: [
{
port: parameter.port
targetPort: parameter.port
},
]
}
}
parameter: {
image: string
cmd?: [...string]
port: *80 | int
env?: [...{
name: string
value?: string
valueFrom?: {
secretKeyRef: {
name: string
key: string
}
}
}]
cpu?: string
}
在平台中安装了上述 webserver
后,用户就可以在应用程序中部署该组件,如下所示:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: webserver-demo
spec:
components:
- name: hello-world
type: webserver # claim to deploy webserver component definition
properties: # setting parameter values
image: crccheck/hello-world
port: 8000 # this port will be automatically exposed to public
env:
- name: "foo"
value: "bar"
cpu: "100m"
4. Workload Types #
工作负载类型(Workload Types)的使用将在组件模型部分介绍,但这里将对工作负载类型定义进行说明。
工作负载类型是给定组件定义的关键特征。工作负载类型由平台提供,以便用户检查平台并了解有哪些工作负载类型可供使用。请注意,工作负载类型不可扩展给最终用户(只能扩展给平台操作员)。因此,终端用户不得创建新的工作负载类型。
Workload Definition #
工作负载类型以 “工作负载定义”(WorkloadDefinition
)的形式呈现,此外,为了便于发现,工作负载定义还带有
特性信息,可提示平台如何将
特性 附加到引用该工作负载类型的给定组件上(即特性系统的适用于特性)。
Top-Level Attributes #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | A string that identifies the version of the schema the object should have. The core types uses core.oam.dev/v1beta1 in this version of model |
|
kind |
string |
Y | Must be WorkloadDefinition |
|
metadata |
Metadata |
Y | Entity metadata. | |
spec |
Spec |
Y | The specification for the workload definition. |
Spec #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
definitionRef |
DefinitionRef |
Y | Identifier to workload capability in the platform. |
DefinitionRef #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
N | Name identifier of the workload capability. Mutually exclusive to apiVersion and kind . |
|
apiVersion |
string |
N | API version of the workload capability. | |
kind |
string |
N | Kind of the workload capability. |
Characteristics of Workload Types #
下面工作负载定义中保留的 “metadata.labels” 用于表示工作负载类型的 _the distinguishing characteristics_。
这些特征将通过 traits 系统的 _applies to_ 功能来实现。
Label | Type | Explain |
---|---|---|
workload.oam.dev/replicable |
boolean | Whether they are replicable. If not, no replication or scaling traits may be assigned. |
workload.oam.dev/daemonized |
boolean | Whether they are daemonized. For daemon types, if the workload exits, this is considered a fault, and the system must fix it. For non-daemonized types, exit is considered a success if no error is reported. |
workload.oam.dev/exposed |
boolean | Whether they are exposed, i.e. have a service endpoint with a stable name for network traffic. Workload types that have a service endpoint need a virtual IP address (VIP) with a DNS name to represent the component as a whole, addressable within their network scope and can be assigned traffic routing traits. |
workload.oam.dev/podspecable |
boolean | Whether this workload can be addressed by Kubernetes PodSpec . If yes, the implementation could manipulate the workload by leveraging PodSpec structure, instead of being agnostic of the workload’s schematic. |
Categories of Workload Types #
Workload Type 可分为几类。
Officially Maintained Workload Type #
正式维护的工作负载类型必须属于 “oam.dev “组。
下面是一个官方维护工作负载类型的示例:
kind: WorkloadDefinition
metadata:
name: Server
spec:
definitionRef:
name: containerizedworkloads.core.oam.dev
该 Workload Type 的说明:
Name | Category | Schema | Exposed | Replicable | Daemonized |
---|---|---|---|---|---|
Server | Core | ContainerizedWorkload | Yes | Yes | Yes |
Extended Workload Type #
每个 OAM 运行时都可以定义本组之外的工作负载类型,它们将被视为扩展工作负载类型。扩展工作负载类型的名称和模式完全由 OAM 实施自行决定。
下面是一个示例:
kind: WorkloadDefinition
metadata:
name: redis.cache.aliyun.com
spec:
definitionRef:
name: redis.cache.aliyun.com # this is an extended workload type
对于扩展工作负载类型,建议使用以下约定:
- 使用
Group/Version/Kind
唯一标识工作负载能力。 name
遵循 Group/Version/Kind 中描述的格式。WorkloadDefinition
的name
与它所指向的name
相同。
kind: WorkloadDefinition
metadata:
name: schema.example.com
spec:
definitionRef:
name: schema.example.com
Server #
服务器是一种 OAM 核心工作负载类型,用于定义长期运行、可扩展的工作负载,这些负载有一个名称稳定的网络端点,用于接收整个组件的网络流量。常见用例包括暴露 API 的网络应用程序和服务。服务器工作负载类型具有以下特征:
- 定义了一个容器运行时,同一容器的零个或多个副本可同时运行。
- 应用程序操作员可以通过应用和配置可用特性来增加或减少组件副本的数量。
- 服务器是守护进程化的。无论错误代码如何,运行时都必须尝试重新启动退出的副本。
- 服务器有一个网络端点,该端点具有自动分配的虚拟 IP 地址(VIP)和 DNS 名称,可在组件所属的网络范围内寻址。
如果服务器没有在至少一个容器上提供至少一个端口,实现就应该发出错误信息。
How to use? #
- 在基于 OAM 的平台中安装以下工作负载定义:
apiVersion: core.oam.dev/v1beta1
kind: WorkloadDefinition
metadata:
name: Server
spec:
definitionRef:
name: containerizedworkloads.core.oam.dev # the reference of schema for this workload type. In Kubernetes it should be a full name of API resource
- 将组件引用服务器定义为工作负载:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: webserver
annotations:
definition.oam.dev/description: "webserver is a combo of Deployment + Service"
spec:
workload:
type: Server # reference above workload definition by name.
schematic:
... # CUE code to define user schematic.
Containerized Workload #
ContainerizedWorkload 是无服务器容器风格的工作负载定义,可作为 Azure ACI、AWS Fargate 或 Kubernetes 的简单无状态工作负载等运行时平台的长期运行容器化工作负载类型的模式。
注意:根据设计,ContainerizedWorkload 模式并不等同于 Kubernetes Pod 规范。作为无服务器风格工作负载的模式,ContainerizedWorkload 计划只关注面向开发人员的基元,并且是自包含的,因此开发人员无需定义 ConfigMap 或 Secret 等对象。此外,它默认公开容器端口。因此,如果您正在为基于 Kubernetes 的 PaaS 寻找通用工作负载模式,我们建议您查看 PodSpecWorkload 规范。
以下是 ContainerizedWorkload 的规范示意图。
Top-Level Attributes of a containerized workload #
这些属性提供了有关容器化工作负载的顶级信息。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | core.oam.dev/v1alpha2 |
|
kind |
string |
Y | ContainerizedWorkload |
|
metadata |
Metadata |
Y | containerized workload metadata. | |
spec |
Spec |
Y | A container for the containerized workload spec. |
Spec #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
osType |
string |
N | The OS required to host (all of) the component’s containers (since containers share a kernel with the underlying host). Possible values include:
|
|
arch |
string |
N | The CPU architecture required to host (all of) the component’s containers (since containers share physical hardware with the underlying host). Possible values include:
|
|
containers |
[]Container |
Y | The OCI container(s) that implement the component. |
Container #
本节介绍为该组件运行容器化工作负载所需的运行时配置。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | The container’s name. Must be unique per component. | |
image |
string |
Y | A path-like or URI-like representation of the location of an OCI image. Where applicable, this MAY be prefixed with a registry address, SHOULD be suffixed with a tag. | |
resources |
Resources |
Y | Resources required by the container. | |
cmd |
[]string |
N | Entrypoint array. | |
args |
[]string |
N | Arguments to the entrypoint. The container image’s CMD is used if this is not provided. | |
env |
[]Env |
N | Environment variables for the container. | |
config |
[]ConfigFile |
N | Locations to write configuration as files accessible within the container | |
ports |
[]Port |
N | Ports exposed by the container. | |
livenessProbe |
HealthProbe |
N | Instructions for assessing whether the container is alive. | |
readinessProbe |
HealthProbe |
N | Instructions for assessing whether the container is in a suitable state to serve traffic. | |
imagePullSecret |
string |
N | Key that can be used to retrieve the credentials for pulling this secret. |
Resources #
资源描述附加到运行时的计算资源。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
cpu |
CPU |
Y | Specifies the attributes of the cpu resource required for the container. | |
memory |
Memory |
Y | Specifies the attributes of the memory resource required for the container. | |
gpu |
GPU |
N | Specifies the attributes of the gpu resources required for the container. | |
volumes |
[]Volume |
N | Specifies the attributes of the volumes that the container uses. | |
extended |
[]ExtendedResource |
N | Implementation-specific extended resource requirements |
对于底层平台无法满足的任何资源,平台必须返回错误并停止部署。资源被视为一种需求,无法满足该需求意味着运行时不得部署应用程序。例如,如果应用程序请求 1P
内存,而内存量不可用,则应用程序部署必须失败。同样,如果应用程序需要 1
个 GPU,而运行时没有提供 GPU,则应用程序部署必须失败。
CPU #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
required |
double |
Y | The minimum number of logical cpus required for running this container. |
Memory #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
required |
string |
Y | The minimum amount of memory required for running this container. The value should be a positive integer with/without unit suffix: P, T, G, M, K. If no unit is given, it defaults to ‘bytes’. |
GPU #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
required |
double |
Y | The minimum number of gpus required for running this container. |
Volume #
卷描述了名称、挂载卷的位置、访问模式(如读/写或只读)以及挂载的共享策略。它还描述了卷所需的底层磁盘属性。
路径格式取决于消费组件的操作系统,但实现应支持类似 UNIX 的路径表示法。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | Specifies the name used to reference the path. | |
mountPath |
string |
Y | Specifies the actual mount path in the filesystem. | |
accessMode |
string |
N | RW |
Specifies the access mode. Allowed values are RW (read/write) and RO (read-only). |
sharingPolicy |
string |
N | Exclusive |
The sharing policy for the mount, indicating if it is expected to be shared or not. Allowed values are Exclusive and Shared . |
disk |
Disk |
N | Specifies the attributes of the underneath disk resources required by the volume. |
Example:
name: "configuration"
mountPath: /etc/config
accessMode: RO
sharingPolicy: Shared
disk:
required: "2G"
ephemeral: n
上述操作要求在路径 /etc/config
下挂载一个只读卷,该卷由一个至少提供 2G 非短暂存储空间的卷支持。
Disk #
磁盘指定卷所使用磁盘的属性。它描述了最小磁盘大小和磁盘是否短暂等信息。短暂磁盘表示组件需要节点上最小的磁盘大小才能运行。例如,图像处理组件可能需要节点上更大的缓存才能运行,这时可以使用短暂磁盘。当短暂磁盘设置为假时,表示将使用外部磁盘。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
required |
string |
Y | The minimum disk size required for running this container. The value should be a positive value, greater than zero. | |
ephemeral |
boolean |
N | Specifies whether external disk needs to be mounted or not. |
ExtendedResource #
扩展资源是针对特定实施资源的资源需求声明。例如,兼容 OAM 的平台可能会暴露出特殊的硬件。通过该字段,容器可以指出,为了让容器运行,需要提供此类特殊硬件。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | The name of the resource, as a Group/Version/Kind | |
required |
string |
Y | The required condition. |
name
字段必须是标识特定资源的 group/version/kind。
Example:
extended:
- name: ext.example.com/v1.MotionSensor
required: "1"
- name: ext.example.com/v2beta4.ServoModel
required: z141155-t100
如果已命名的扩展资源因故不可用,则在创建组件实例时,实现必须返回错误信息。
Env #
Env 将环境变量描述为 name/value 的字符串。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | The environment variable name. Must be unique per container. | |
value |
string |
N | The environment variable value. |
name
字段必须由有效的 Unicode 字母和数字字符以及 _
和 -
组成。
Example:
env:
- name: "ADMIN_USER"
value: "admin" # This is a literal value
ConfigFile #
ConfigFile 描述了容器内可用文件的路径,以及将写入该文件的数据。这提供了一种将配置文件注入容器的方法。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
path |
string |
Y | An absolute path within the container. | |
value |
string |
N | The data to be written into the file at the specified path. If this is not supplied, fromParam must be supplied |
|
fromParam |
string |
N | The parameter whose value should be written into this file as a value |
path 字段必须包含一个遵守底层操作系统路径规则的路径。如果给出的是相对路径,实现必须假定该路径是相对于容器根目录的。如果使用这样的路径会违反安全措施或路径布局要求,实现可能会产生错误
Example:
config:
- path: "/etc/access/default_user.txt"
value: "admin" # This is a literal value
- path: "/var/run/db-data"
fromParam: "sourceData" # This will cause the value to be read from the parameter whose name is `sourceData`
如果同时指定了 fromParam
和 value
,则必须优先使用 fromParam
,即使参数值是空值。如果两者都未指定,运行时必须产生错误。
Port #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | A descriptive name for the port. Must be unique per container. | |
containerPort |
integer |
Y | The port number. Must be unique per container. | |
protocol |
string |
N | TCP |
Indicates the transport layer protocol used by the server listening on the port. Valid values are TCP and UDP . |
name
字段必须是 ASCII 字符集(0061-007A)中的小写字母字符。
HealthProbe #
HealthProbe 描述了如何执行探测操作以确定组件的健康状况。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
exec |
Exec |
N | Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute. |
|
httpGet |
HTTPGet |
N | Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute. |
|
tcpSocket |
TCPSocket |
N | Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute. |
|
initialDelaySeconds |
integer |
N | 0 |
Number of seconds after the container is started before the first probe is initiated. |
periodSeconds |
integer |
N | 10 |
How often, in seconds, to execute the probe. |
timeoutSeconds |
integer |
N | 1 |
Number of seconds after which the probe times out. |
successThreshold |
integer |
N | 1 |
Minimum consecutive successes for the probe to be considered successful after having failed. |
failureThreshold |
integer |
N | 3 |
Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe). |
Exec #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
command |
[]string |
Y | A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures. |
HTTPGet #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
path |
string |
Y | The endpoint, relative to the port , to which the HTTP GET request should be directed. |
|
port |
integer |
Y | The TCP socket within the container to which the HTTP GET request should be directed. | |
httpHeaders |
[]HTTPHeader |
N | Optional HTTP headers. |
HTTPHeader #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
Y | An HTTP header name. This must be unique per HTTP GET-based probe. | |
value |
string |
Y | An HTTP header value. |
name
和 value
都必须遵守 HTTP/1.1 关于有效[标头值]的规范(
https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2)
TCPSocket #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
port |
integer |
Y | The TCP socket within the container that should be probed to assess container health. |
端口必须是大于 0 的整数。
Units for Time, CPU, Memory, and Disk #
本规范的某些地方使用了某些度量单位。本节将介绍这些计量单位。
Timing (Intervals) #
对于计时,默认的时间单位是秒,用整数表示。
CPU count #
CPU 数量用浮点数表示,其中 “1 “表示一个 CPU,“2 “表示两个 CPU,“0.5 “表示半个 CPU。
该单位的确切含义因平台而异。 实施者应考虑逻辑 cpu 相当于一个 AWS vCPU 或一个 Azure vCore 或一个 GCP Core 或一个 IBM vCPU。允许使用分数值。如果运行时不支持小数单位,则必须向上舍入(上限函数)到下一个整数值。
Memory and Disk #
内存和磁盘空间使用 bytes/kilo/mega/giga/tera/peta 的符号,只使用主要单位:
1024
is 1024 bytes88K
is 88 kilobytes5M
is 5 megabytes7G
is 7 gigabytes100T
is 100 terabytes9999P
is 9999 petabytes
如果在单位字母后附加 B
,则必须忽略。因此,5M
和 5MB
被视为相同。大小写并不重要。15k
和 15K
必须视为相同的值。
Containerized Workload JSON schema #
对于非 Kubernetes 运行时实现,可以使用 JSON schema 作为有效的容器化工作负载模式。
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://oam.dev/v1/oam.workload.schema.json",
"title": "Open Application Model Containerized Workload JSON Schema",
"type": "object",
"properties": {
"apiVersion": {
"type": "string",
"description": "The specific version of the Open Application Model specification in use. This version of the specification covers apiVersions in core.oam.dev/v1alpha2."
},
"kind": {
"type": "string",
"description": "must be ContainerizedWorkload.",
"enum": ["ContainerizedWorkload"]
},
"metadata": {
"type": "object",
"description": "ContainerizedWorkload metadata.",
"properties": {
"name": {
"type":"string"
},
"labels": {
"type":"object",
"description": "A set of string key/value pairs used as arbitrary labels on this workload.",
"additionalProperties": { "type": "string" }
},
"annotations": {
"type":"object",
"description": "A set of string key/value pairs used as arbitrary annotations on this workload.",
"additionalProperties": { "type": "string" }
}
}
},
"spec": {
"type": "object",
"description": "A container for all remaining attributes.",
"additionalProperties": {
"$ref": "#/definitions/containerSpec"
}
}
},
"required": ["apiVersion", "kind", "metadata", "spec"],
"additionalProperties": false,
"definitions": {
"containerSpec": {
"osType":{
"type": "string",
"default": "linux",
"description": "The OS required to host (all of) the component's containers (since containers share a kernel with the underlying host).",
"enum": ["linux", "windows"]
},
"arch": {
"type": "string",
"default": "amd64",
"description": "The CPU architecture required to host (all of) the component's containers (since containers share physical hardware with the underlying host).",
"enum": ["i386", "amd64", "arm", "arm64"]
},
"containers":{
"type": "array",
"description": "The OCI container(s) that implement the component.",
"items": {
"$ref": "#/definitions/container"
}
},
"required": ["containers"],
"additionalProperties": false
},
"container": {
"type": "object",
"description": "The runtime configuration necessary to run a containerized workload for this component.",
"properties": {
"name": {
"type": "string",
"description": "The container's name. Must be unique per component."
},
"image": {
"type": "string",
"description": "A path-like or URI-like representation of the location of an OCI image. Where applicable, this MAY be prefixed with a registry address, SHOULD be suffixed with a tag, and MUST be suffixed with a digest in OCI format. The digest may be used to compute the integrity of the image."
},
"resources": {
"type": "object",
"description": "Resources required by the container.",
"properties": {
"cpu": {
"description": "Specifies the attributes of the cpu resource required for the container.",
"type": "object",
"properties": {
"required": {
"type": "number",
"description": "The minimum number of logical cpus required for running this container."
}
}
},
"memory": {
"description": "Specifies the attributes of the memory resource required for the container.",
"type": "object",
"properties": {
"required": {
"type": "string",
"description": "The minimum amount of memory in MB required for running this container. The value should be a positive integer, greater than zero."
}
}
},
"gpu": {
"description": "Specifies the attributes of the gpu resources required for the container.",
"type": "object",
"properties": {
"required": {
"type": "number",
"description": "The minimum number of gpus required for running this container."
}
}
},
"volumes": {
"description": "Specifies the attributes of the volumes that the container uses.",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"description": "Specifies the name used to reference the path.",
"type": "string"
},
"mountPath": {
"description": "Specifies the actual mount path in the filesystem.",
"type": "string"
},
"accessMode": {
"description": "Specifies the access mode.",
"type": "string",
"default": "RW",
"enum": ["RW", "RO"]
},
"sharingPolicy": {
"description": "The sharing policy for the mount, indicating if it is expected to be shared or not.",
"type": "string",
"default": "Exclusive",
"enum": ["Exclusive", "Shared"]
},
"disk": {
"description": "Specifies the actual mount path in the filesystem.",
"type": "object",
"properties": {
"required": {
"type": "string",
"description": "The minimum disk size required for running this container. The value should be a positive integer, greater than zero."
},
"ephemeral": {
"type": "boolean",
"description": "Specifies whether external disk needs to be mounted or not."
}
}
}
},
"required": ["name", "mountPath"]
}
}
}
},
"cmd": {
"type": "string",
"items": {
"type": "string"
}
},
"args": {
"type": "string",
"items": {
"type": "string"
}
},
"env": {
"description": "Environment variables",
"type": "array",
"items": {
"type": "object",
"description": "a name/value pair",
"properties": {
"name": {
"description": "The environment variable name. Must be unique per container.",
"type": "string",
"maxLength": 256
},
"value": {
"description": "The environment variable value. If this is not supplied, fromParam must be supplied",
"type": "string",
"$comment": "POSIX may dictate a max length on this",
"maxLength": 2048
},
"fromParam": {
"description": "The parameter whose value should be substituted into this variable as a value",
"type": "string"
}
},
"required": ["name"],
"additionalProperties": false
}
},
"config": {
"description": "Configuration files",
"type": "array",
"items": {
"type": "object",
"description": "a path and the value to be written into a file at that location",
"properties": {
"name": {
"description": "The file name as an absolute path. Must be unique per container.",
"type": "string",
"$comment": "format of this value is operating system dependent.",
"maxLength": 256
},
"value": {
"description": "The value to be written into the file. If this is not supplied, fromParam must be supplied",
"type": "string"
},
"fromParam": {
"description": "The parameter whose value should be written into this file as a value",
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"ports": {
"description": "Ports exposed by the container.",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"description": "A descriptive name for the port. Must be unique per container.",
"type": "string"
},
"containerPort": {
"description": "The port number. Must be unique per container.",
"type": "integer"
},
"protocol": {
"description": "Indicates the transport layer protocol used by the server listening on the port.",
"type": "string",
"default": "TCP",
"enum": ["TCP", "UDP"]
}
},
"required": ["name", "containerPort"],
"additionalProperties": false
}
},
"livenessProbe": {
"description": "Instructions for assessing whether the container is alive.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/healthProbe"
}
},
"readinessProbe": {
"description": "Instructions for assessing whether the container is in a suitable state to serve traffic.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/healthProbe"
}
}
},
"required": ["name", "image", "resources"],
"additionalProperties": false
},
"healthProbe": {
"description": "Health Probe describes how a probing operation is to be executed as a way of determining the health of a component.",
"type": "object",
"properties": {
"exec": {
"description": "Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute.",
"type": "object",
"properties": {
"command": {
"description": "A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.",
"type": "string",
"items": {
"type": "string"
}
}
}
},
"httpGet": {
"description": "Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute.",
"type": "object",
"properties": {
"path":{
"description": "The endpoint, relative to the port, to which the HTTP GET request should be directed.",
"type": "string"
},
"port": {
"description": "The TCP socket within the container to which the HTTP GET request should be directed.",
"type":"integer"
},
"httpHeaders":{
"type": "object",
"description": "Optional HTTP headers.",
"properties": {
"name": {
"description": "An HTTP header name. This must be unique per HTTP GET-based probe.",
"type": "string"
},
"value": {
"description": "An HTTP header value.",
"type": "string"
}
},
"required": ["value", "name"],
"additionalProperties": false
}
},
"required": ["path", "port"],
"additionalProperties": false
},
"tcpSocket": {
"description": "Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute.",
"type": "object",
"properties": {
"port": {
"description": "The TCP socket within the container that should be probed to assess container health.",
"type": "integer"
}
},
"required": ["port"],
"additionalProperties": false
},
"initialDelaySeconds": {
"description": "Number of seconds after the container is started before the first probe is initiated.",
"type": "integer",
"default": 0
},
"periodSeconds": {
"description": "How often, in seconds, to execute the probe.",
"type": "integer",
"default": 10
},
"timeoutSeconds": {
"description": "Number of seconds after which the probe times out.",
"type":"integer",
"default": 1
},
"successThreshold": {
"description": "Minimum consecutive successes for the probe to be considered successful after having failed.",
"type": "integer",
"default": 1
},
"failureThreshold": {
"description": "Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).",
"type": "integer",
"default": 3
}
}
}
}
}
The Relationship of Component and Workload Type #
简而言之,组件是工作负载类型的封装,预先定义了面向用户的示意图。组件需要明确声明其工作负载类型,以便 OAM 系统正常工作,即 OAM 特性系统将检查该工作负载类型信息,以验证特定特性是否可附加到该组件上。
下面的示例可能有助于更好地解释它们之间的关系:
- Web 服务组件封装了 Kubernetes 部署和服务。
- 在这种情况下,
apps/v1.Deployment
就是该组件的工作负载类型。
- 在这种情况下,
- 封装 Kubernetes 部署的 后端 Worker 组件。
- 在这种情况下,
apps/v1.Deployment
仍是该组件的工作负载类型。
- 在这种情况下,
- 将 Kubernetes 部署 + Ingress 作为模板的 Helm 图表也是一个组件。
- 该组件的工作负载类型是
apps/v1.Deployment
。
- 该组件的工作负载类型是
从另一个角度看,组件定义 总是由某些组件提供商或软件构建商定义,而 工作负载定义 作为一种平台能力,总是由基础设施运营商维护。因此,OAM 平台的工作负载定义数量通常非常有限,而且变化不大,但总是有无数的组件定义由不同的提供商和用户维护。
5. Application Scopes #
Application scopes 通过提供具有共同 group 行为的不同形式的应用边界,将组件组合成逻辑应用。
Application scopes 具有以下一般特征:
- 在定义一组组件实例共有的行为或元数据时,应使用 Application scopes。
- 一个组件可以同时部署到多个不同类型的应用范围中。
- Application scopes 类型可决定组件是否可同时部署到同一应用范围类型的多个实例中。
- Application scopes 可用作组件组与基础设施(如网络)或外部功能(如身份提供商)所提供功能之间的连接机制。
下图说明了如何将组件分组到重叠的应用范围中以创建不同的应用边界:
此示例显示了两个作用域类型,其中分布着四个组件。
组件 A、B 和 C 部署在同一个健康作用域中。健康作用域将收集其组成组件的总体健康信息,并在组件升级操作期间对这些信息进行评估。需要根据一组组件的总体健康状况进行评估和/或执行操作的特性或组件可进一步使用健康范围提供的查询信息。这是应用程序的一个基本分组结构,它为组件之间的依赖关系提供了一个宽松的定义。
组件 A 在自己的网络范围内与组件 B、C 和 D 隔离。这样,基础设施运营商就可以为不同的组件组提供不同的 SDN 设置,例如对后端组件的入站/出站规则进行更严格的限制。
Defining an application scope #
应用范围由 ScopeDefinition 实体表示。
Top-Level Attributes #
这些属性提供了有关应用范围的顶级信息。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | A string that identifies the version of the schema the object should have. The core types uses core.oam.dev/v1beta1 in this version of model. |
|
kind |
string |
Y | Must end with ScopeDefinition . |
|
metadata |
Metadata |
Y | Entity metadata. | |
spec |
Spec |
Y | A specification for scope attributes. |
Spec #
该规范定义了作用域的构成部分。
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
allowComponentOverlap |
bool |
N | true | Determines whether a component is allowed to be in multiple instances of this scope type simultaneously. When false, the runtime implementation MUST produce an error and stop deployment if an attempt is made to place a component into more than one instance of this scope type simultaneously. |
definitionRef |
DefinitionRef |
Y | Identifier to scope capability in the platform. |
DefinitionRef #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
N | Name identifier of the scope capability. Mutually exclusive to apiVersion and kind . |
|
apiVersion |
string |
N | API version of the scope capability. | |
kind |
string |
N | Kind of the scope capability. |
Examples #
apiVersion: core.oam.dev/v1beta1
kind: ScopeDefinition
metadata:
name: healthscopes.core.oam.dev
spec:
allowComponentOverlap: true
definitionRef:
name: healthscopes.core.oam.dev
Health Scope #
健康范围将组件分组为一个总体健康组。组内各组件的总体健康状况为升级和回滚机制提供信息。
健康范围汇总了组件的健康状态。可以设置健康范围的参数,以确定必须有多少百分比的组件处于不健康状态,才能认为整个范围处于不健康状态。
健康作用域本身不会根据健康状态采取任何行动。它只是一个群组健康状况聚合器,可被应用程序的其他进程和部分查询和使用,例如:应用程序升级特质可监控健康范围的总体健康状况,并决定何时启动自动回滚。监控应用程序可以监控健康状况范围的总体健康状况以发出警报。
Properties #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
probe-timeout |
int32 |
Y | ProbeTimeout is the amount of time in seconds to wait when receiving a response before marked failure. | |
probe-interval |
int32 |
Y | ProbeInterval is the amount of time in seconds between probing tries. |
Instance Example #
例如,健康范围实例可以如下所示:
apiVersion: core.oam.dev/v1alpha2
kind: HealthScope
metadata:
name: example-health-scope
spec:
probe-timeout: 5
probe-interval: 5
Usage Example #
应用程序可以如下引用上述作用域实例:
kind: Application
metadata:
name: example-appconfig
spec:
components:
- componentName: example-component
traits:
...
scopes:
healthscopes.core.oam.dev: example-health-scope
Network Scope #
网络范围将组件归入网络子网边界,并定义一般运行时网络模型。网络定义、规则和策略由基础设施的网络或 SDN 描述。
网络范围将组件组合在一起,并将它们连接到网络或 SDN。网络本身必须由应用运营商定义和运营。
流量管理功能也可查询网络范围,以确定服务网格的可发现性边界或 API 网关的 API 边界。
如果未指定网络范围,平台必须将应用程序加入默认网络。在默认网络中,应用配置中的所有组件必须能够相互通信,健康探测器必须能够与定义了健康检查规则的任何组件联系。不过,加入的网络与平台有关。例如,基于集群的环境(如 Kubernetes)会声明集群范围内的网络。相反,真正的无服务器实现可能会将组件加入到仅包括组件和健康检查探针的网络中。
Properties #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
networkId |
string |
Y | The id of the network, e.g. vpc-id, VNet name. | |
subnetIds |
array |
Y | 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. | |
internetGatewayType |
string |
Y | The type of the gateway, options are ‘public’, ’nat’. Empty string means no gateway. |
Instance Example #
例如,网络作用域实例可以如下所示:
apiVersion: standard.oam.dev/v1alpha2
kind: NetworkScope
metadata:
name: example-vpc-network
spec:
allowComponentOverlap: true # can be skipped
networkId: cool-vpc-network
subnetIds:
- cool-subnetwork
- cooler-subnetwork
- coolest-subnetwork
internetGatewayType: nat
Usage Example #
应用程序可以如下引用上述作用域实例:
kind: Application
metadata:
name: example-appconfig
spec:
components:
- componentName: example-component
traits:
...
scopes:
networkscopes.standard.oam.dev: example-vpc-network
6. Traits #
trait 是一种可自行决定的运行时叠加,可通过操作功能增强组件工作负载实例。它为应用程序操作员提供了一个机会,让他们可以对组件的配置做出特定的决定,而无需让组件提供者参与其中或破坏组件封装。
例如,在 WordPress Helm 图表中注入一个 sidecar container 的 sidecar 特性。
特性可以是适用于单个组件的分布式应用程序的任何配置,如流量路由规则(如负载平衡策略、网络入口路由、断路、速率限制)、自动扩展策略、升级策略等。
Trait Definition #
本节具有规范性,因为性状是系统中可检查(也可能是可共享)的部分。所有特征都必须可以用以下格式表示。
特质的定义与组件一样使用示意图。与工作负载定义一样,每个特质定义都有一个 definitionRef,它是对定义特质配置选项的模式的引用。
Top-Level Attributes #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | A string that identifies the version of the schema the object should have. The core types uses core.oam.dev/v1beta1 in this version of documentation. |
|
kind |
string |
Y | Must be TraitDefinition |
|
metadata |
Metadata |
Y | Information about the trait. | |
spec |
Spec |
Y | A specification for trait attributes. |
Spec #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
appliesToWorkloads |
[]string |
* | The collection of
workload type characteristics to which this trait applies. If this field is empty or unspecified or specified as "*" , it means applies any workload type. A trait must apply to at least one workload type. This attribute must contain at least one value. |
|
conflictsWith |
[]string |
N | A list of traits that would be conflict with this trait when applied to same workload type. For example, autoscaling may conflict with cron-autoscaling . |
|
definitionRef |
DefinitionRef |
Y | Identifier of the trait capability. |
DefinitionRef #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string |
N | Name identifier of the trait. Mutually exclusive to apiVersion and kind . |
|
apiVersion |
string |
N | API version of the trait. | |
kind |
string |
N | Kind of trait. |
建议使用
Group/Version/Kind 来唯一标识特征能力。如果使用 definitionRef.name
,则必须包含可用于唯一标识的信息。
Application 部分将详细介绍用户如何将特质附加到组件。
Implementing a Trait System #
警告:本节仅面向 OAM 平台实施者(即 KubVela),如果您只对 OAM 概念感兴趣,请跳过本节。
在本文档中,_traits system_ 被定义为运行时通过 traits 对组件实例应用和管理操作行为的方式。
模型的运行时实现必须提供将操作行为附加到组件实例的特征系统。
当一个组件上有一个以上的特征时,它必须是:
- 按照规定的顺序应用特征
- 确定兼容性,如果特性组合无法满足要求则失败
特征按顺序应用,如果某些特征集表示依赖关系(例如,必须在 SSL 之前设置入口),则可通过设置明确的顺序来解决。
一个组件实例只能有一个给定特质类型的配置。
在完成所有特性配置之前,部署不应被标记为已完成。例如,如果网络服务器组件是与自动分级器特质一起部署的,那么在 (a) 网络服务器本身运行(由健康检查确定)和 (b) 自动分级器特质运行(由底层平台确定)之前,网络服务器不应被视为 “正在运行”。
组件可指定多个特性;因此,运行时必须支持对组件应用零个或多个特性。如果底层运行时无法满足所请求的特性,运行时必须产生错误并停止部署。
没有明确要求组合特质的机制。例如,一个特质(ssl
)不能指定它要求另一个特质(ingress
)应用于一个对象。但是,如果一个特质在没有另一个特质的情况下无法履行合约,那么特质的_实现_就应该失败。系统可以支持一种机制,在这种机制中,一个特质(sslIngress
)由两个不同的特质实现(ssl
和ingress
)不透明地支持。
在运行时可用的任何特征都必须在该运行时实现。例如,运行时中的 autoscaling
特性不可能没有实现。
Categories of Traits #
特质系统旨在作为运行时操作能力的扩展点,同时也为组件提供通用的、在某些情况下是必要的操作功能。
特质的实施细节超出了本文档的范围。不过,为了允许运行时实施任意特质,同时保持一定程度的通用性,以实现不同运行时的应用程序可移植性,特质按以下方式进行分类:
- Core traits:这一类定义了一组类型名称为
core.oam.dev
组的特质定义。这些特性提供了某些工作负载类型和组件功能运行所必需的操作功能。运行时必须实现本模型中定义的这些特征。 - Standard traits:该类别定义了一组类型名称为
standard.oam.dev
组的特征定义。这些特征提供了应用程序操作员常用的操作功能,并由大多数运行时实现。建议运行时使用本模型中定义的标准特性定义,以提供与标准特性定义中所列功能相当的操作功能。换句话说,如果运行时正在实现具有行为foo
的特质,而行为foo
的标准特质定义已经存在,那么运行时就应该使用标准foo
特质定义。应用程序操作员如果希望最大限度地提高应用程序的可移植性,就应该使用这些 trait 定义。虽然这并不能保证应用程序的可移植性,但其目的是提高运行时之间的可移植性。 - Extension traits:除核心特质和标准特质定义的特质外,该类别还允许运行时定义自己独有的特质定义集。运行时可选择以任意定义实现该类别中的任意一组特质。扩展特质类型名称不得位于
core.oam.dev
或standard.oam.dev
组中。
Trait Characteristics #
单个特征可与特定工作负载绑定(也可适用于所有工作负载)。特性可以声明适用于哪种工作负载。
本模型并不对特质的简单或复杂程度设定要求。有些特性可能会提供大量可配置选项,而有些特性则可能不提供任何可配置选项。也不要求特质与其底层技术一样详尽。例如,自动缩放技术的实现可能会提供多种方法来确定组件何时应该缩放。但该特性可能只涉及其中的几种。同样,也可以定义多个自动缩放特性(每个特性都有唯一的名称),每个特性显示不同的可配置选项子集。或者,一个大的特质可以显示所有可能的自动缩放配置。
OAM 平台的实现应能以下面解释的格式发现所有支持的特征。
7. Application #
应用程序实体定义了应用程序部署后将被实例化的组件列表。
用户将指定每个组件的最终参数以及用于增强其功能或改变其行为的特性。此外,还可以指定一组范围,将不同的组件子集分组。
Top-Level Attributes #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
apiVersion |
string |
Y | A string that identifies the version of the schema the object should have. The core types uses core.oam.dev/v1beta1 in this version of documentation. |
|
kind |
string |
Y | Must be Application |
|
metadata |
Metadata |
Y | Information about the application. | |
spec |
Spec |
Y | A specification for application attributes. |
Spec #
The specification of applications defines components to create, traits attached to each components, and a set of scopes the components drop in.
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
components |
[]Component |
Y | Component specification. |
Component #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
name |
string | Y | The name of the component instance. | |
type |
string | Y | A reference to the
component definition that will be instantiated by the application. Optionally a namespaced component may be referenced as <namespace>/<component_name> . The meaning of namespace is implementation specific depending on the runtime being used and the target platform where entities are created. In the case of the Kubernetes, the concept of namespace matches the existing one in Kubernetes. |
|
properties |
Properties |
Y | A set of values assigned to the parameters exposed from the component schematic. | |
traits |
[]Trait |
N | The traits to attach to this component instance. | |
scopes |
map[string]string |
N | A map with the scopes the component belongs to. The map uses the qualified scope definition name as key (e.g., “scope.company.com”), and the name of the scope as value (e.g., “myscope”). Notice that this reference implies that an entity of the target scope with the specific name exists. |
name
必须遵循这些命名规则:
name 字段为必填字段,必须为 63 个字符或更少,以字母数字字符([a-z0-9A-Z])开头和结尾,中间包含破折号 (-)、下划线 (_)、点 (.)和字母数字。
Trait #
Attribute | Type | Required | Default Value | Description |
---|---|---|---|---|
type |
string | N | A reference to the name of trait definition. For one type of trait, there could be only one configuration in one component. | |
properties |
Properties |
Y | The properties values to use this trait. |
注意 _Traits_ 不需要名称,因为 OAM 运行时负责实例化 traits。组件的名称预计将用于相关特质。
Properties #
属性指定了与实体属性相关联的值。
当与 _Components_ 关联时,所设置的值将覆盖组件的参数,这些参数遵循 component schematic 中定义的示意图。
当属性用于 _Traits_ 或 _Scopes_ 时,它们会设置这些实体实例化所需的值。其结构由 definition reference 决定。它可能是一个简单的值,也可能是一个复杂的对象。属性根据适合于特质或范围的模式进行验证。
Example #
下面是一个完整的 YAML 文件示例,其中表达了组件、特性和作用域。该示例展示了组件的四个可配置元素:type, properties, traits, and scopes。
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: my-example-app
annotations:
version: v1.0.0
description: "Brief description of the application"
spec:
components:
- name: publicweb
type: web-ui
properties: # properties targeting component parameters.
image: example/web-ui:v1.0.2@sha256:verytrustworthyhash
param_1: "enabled" # param_1 is defined on the web-ui component
traits:
- type: ingress # ingress trait providing a public endpoint for the publicweb component of the application.
properties: # properties are defined by the trait CRD spec. This example assumes path and port.
path: /
port: 8080
scopes:
"healthscopes.core.oam.dev": "app-health" # An application level health scope including both components.
- name: backend
type: company/test-backend # test-backend is referenced from other namespace
properties:
debug: "true" # debug is a parameter defined in the test-backend component.
traits:
- type: scaler # scaler trait to specify the number of replicas for the backend component
properties:
replicas: 4
scopes:
"healthscopes.core.oam.dev": "app-health" # An application level health scope including both components.
上例展示了一个完整的应用程序,包括其作用域、组件及其特性。该应用程序假定存在两个 component definitions,分别代表网络用户界面和假想应用程序的后台。两个 traits 用于增强组件的功能。首先是后台的scaler,用于设置复制因子;其次是ingress,用于将应用程序对外开放。此外,还使用 HealthScope 来检查网络和后端组件的状态。这是本例中任意设置的一组组件,并不一定需要所有组件。
在实例化应用程序时,运行时将根据目标系统(如 Kubernetes)生成所需的实体,并执行与每个实体(如 traits)相关的模式。由于 _Traits_ 可以作为扩展添加到现有环境中,因此运行时必须能够创建任何通过 TraitDefinition 在系统中注册的 Trait 实体。
Component Instances #
_component instance_ 是在应用程序部署过程中创建的。它是在组件与配置一起部署时创建的。
- 每次部署组件时,它都必须与应用程序一起部署。本规范的这一部分描述的是配置。
- 组件的每次后续 _upgrade_ 都会修改给定的实例,如果组件附加了任何修订感知特征,则会生成新的实例修订版。
- 当实例首次创建时,它处于 _initial revision state_。每次升级操作发生时,我们都会说该实例发生了新的_修订_。但是,我们并不假定在某个地方一定存储有相应的
revision
对象。
Releases #
在 Twelve-Factor Applications 中,一个版本被定义为一个 build plus a set of configs。也就是说,对构建或配置的任何更改都会产生一个新版本。在开放式应用程序模型中,类似的情况是 component、trait和scope objects与应用程序结合在一起共同构成一个版本。
对于 OAM 应用程序,一个版本是这样定义的:
版本是一个已命名的应用程序及其相关组件、作用域和特性的描述。
此外,随着应用程序的发布,其组件实例也会被发布。
为适应发布的这一定义,OAM 平台应做出以下假设:
- 应用程序是可变的。
- 对 application 的任何更改(概念上)都会导致新版本的发布,并取代旧版本。
- 如果更新了应用程序,而新版本包含了原应用程序中没有的组件,则必须创建组件实例。
- 特质同样应该根据相同的准则进行附加和分离
- 组件与应用范围的关系应根据相同的准则应用或删除
8. Practical Considerations #
Proposal Stages and the Maturity of the Specification #
该模型目前处于 “草稿 “阶段,正在向 “工作稿 “迈进。工作草案之后,该规范将成为最终规范(如 1.0)。
- 在草稿阶段,模型的任何部分都不被认为是稳定的
- 在工作草案期间,可能会添加功能和修复问题。可能会出现破坏性修改,但这是不正常的情况
- 在最终规范期间,模型只会更新勘误、语法修正和明确标注的 “说明性文字”。
一旦发布了最终规范(如 1.0),新版本的规范(如 1.1)可能会在粗稿阶段开始。
Media Types #
示意图的媒体类型将在模型进入工作草案状态时确定。媒体类型初步将采用以下形式:
- application/oam.TYPE.v1+json,其中 TYPE 替换为类型名称(如组件)。
Security #
该模式目前的形式并没有规定一套具体的安全政策。不过,它确实为安全的某些方面提供了指导:
- OCI/Docker 图像必须尽可能用 SHA 引用
- 文件格式必须转换为规范格式,以便进行哈希运算
- 满足这两个条件后,就可以构建系统,其中原理图的摘要验证将保持系统所有组件的不变性。也就是说,如果示意图用哈希值引用了图像,那么验证示意图摘要的过程也能确保所提取的图像与生成示意图时使用的引用相同。
其他安全细节,如网络传输安全或静态数据的安全,超出了本模型的考虑范围。
9. Design Principles #
Open Application Model 遵循一系列设计原则,以确保模型的清晰度、丰富性和可扩展性。
Separation of Concerns #
Separation of Concerns 是一种设计理念,在这种理念下,架构选择是根据所要解决的离散问题来进行的。像 component 和 application,或 schematics 和 configuration 这样的工件划分,都是按照功能或行为划分的。通过确定不同用户群体的角色和责任,将规格划分为与问题空间相匹配的概念。
Runtime Neutrality #
Separation of Concerns 与运行时间无关。它不假定任何运行时特定的功能。相反,它旨在为应用程序所有者和操作员提供一个通用词汇表,以描述所需的拓扑结构和行为,而不受任何特定平台的影响。
Balance (Elegance) #
在确保关注点分离的同时,OAM 力求避免对在较小团队中担任多种角色的用户造成不必要的复杂性。简单的方案应只需投入最少的时间和精力即可实现,但复杂的方案应在不需要重新平台化的情况下加以解决。
OAM 提供了多个抽象层,这样就可以独立于开发人员的关注点来捕捉操作关注点。
Reusability #
OAM 原理图中的组件可重复使用和共享。此外,这些组件独立于其所描述的代码,因此可以重复使用代码(容器),并防止出现 “锁定 “情况。
该模型作为一个整体,旨在提供应用程序的 “分布式”,使同一个应用程序可以在不同的平台上执行而无需改动。应用程序的这种可移植性旨在使以下情况不仅成为可能,而且变得容易:
- 将应用程序从开发人员工作站转移到生产集群或服务中
- 从一个实施方案迁移到另一个实施方案,无需更改代码
- 创建类似市场的环境,将应用程序部署到客户平台上
Application Models Are Not Programming Models #
应用程序模型和编程模型之间有明显的区别。应用程序模型描述应用程序的组成及其组件的拓扑结构。它不涉及如何实现每个组件(语言、设计模式等)。
另一方面,编程模型描述的是单个软件是如何组成的。开发人员使用它来实现应用程序组件。开放式应用程序模型提供了一种不需要编程模型的应用程序模型。