`
大涛学长
  • 浏览: 110835 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

4 个概念,1 个动作,让应用管理变得更简单

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

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

![1](https://yqfile.alicdn.com/fc92e2b50397d11f5f4e2e48ec4fd02ab9614d90.png)

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

怎么用?
====

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

![2](https://yqfile.alicdn.com/3f04113744fb426fc51034564a7ed595d8106244.png)

四个概念
----

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

一个动作
----

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

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

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

![3](https://yqfile.alicdn.com/55bc66b8b3bb788a9fcc874850e616118348ddef.png)

例子
--

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

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

 

*   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",
          }
        }
      }
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

 

*   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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

 

```
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"
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

 

*   应用运维部署整个应用

使用应用配置将上面的组件、边界、特征组合起来即可部署一个应用,这个由应用的运维人员提供:

```
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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

通过以上完整的使用流程我们可以看到:应用配置是真正部署应用的起点,在使用该配置的时候,对应的组件、应用边界、特征都要提前部署好,运维人员只需做一个组合即可。

服务组件(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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

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

```
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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

总结
--

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

应用边界(Application scopes)
========================

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

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

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

![4](https://yqfile.alicdn.com/629b2637f8e942a2ea895fd10e18bdb03a7f9f24.png)

上图有两种应用边界类型: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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

### 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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

### 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
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

总结
--

应用边界声明由 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
        }
      }
    }
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

上面是一个手动扩缩容服务的 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)]"
```

![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")

总结
--

应用配置定义由运维人员或者 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 标准;
*   推动开源生态建设,以标准化的方式帮助“应用”高效和高质量地交付到任何平台上去。

 

 

 

[原文链接](https://yq.aliyun.com/articles/728222?utm_content=g_1000091250)

本文为云栖社区原创内容,未经允许不得转载。
分享到:
评论

相关推荐

    android应用获得执行root权限动作__socket_service

    然而,自从Android 4.4(KitKat)开始,谷歌为了加强系统的安全性,对获取root权限进行了严格限制,使得直接集成su命令变得更加困难。 标题"android应用获得执行root权限动作__socket_service"表明我们要讨论如何在...

    CAD动态块之链动作

    - 这种联动效果正是通过链动作实现的,它让设计变得更加智能和自动化。 #### 五、链动作的关键点总结 - **参数设置**:确保所有需要联动的参数都已经正确设置了“链动作”属性。 - **对象选择集**:将需要联动的...

    开发您的第一个 Eclipse RCP 应用程序

    自 Eclipse V3.1 发布以来,构建 RCP 应用程序变得更加便捷。本文档将详细介绍如何从零开始创建一个简单的 RCP 应用程序,并逐步扩展其功能。 #### 目标读者 本教程面向希望了解并使用 **Eclipse RCP** 的开发人员...

    3dmax 动作库

    这种技术使得创建自然的角色动作变得更为简便,特别是对于像走路、跑步这样需要连续、流畅的动作序列。 在“15 Character walks and runs”这个压缩包中,我们可以推测包含的是15个不同的行走和跑步动画。这些动作...

    photoshop动作集

    Photoshop动作集是Photoshop软件中的一个重要特性,它允许用户记录一系列操作步骤,然后通过一个简单的点击来重复这些复杂的操作,...通过深入学习和实践,你可以创建属于自己的动作库,让创意工作变得更加高效和有趣。

    3dmax 动作 BIP文件

    BIP文件的主要优势在于其灵活性和可编辑性,使得创建、修改和复用动作变得极为便捷。 首先,我们来看压缩包内的文件名称,这些文件涵盖了各种人物动作,例如: 1. "15 Character walks and runs.rar":包含15种...

    证件照尺寸及PS动作集

    总的来说,“证件照尺寸及PS动作集”是摄影师、设计师或个人处理证件照需求的理想工具,它将复杂的图片处理工作简化,让制作标准证件照变得轻松快捷。只需简单的操作,就能批量处理多张照片,节省了大量的时间和精力...

    蜜蜂:蜜蜂用于构建现代Web管理员应用程序变得更加轻松有趣

    Bee用于构建现代Web管理员应用程序变得更加容易和有趣。 概念 前一级分离,通讯方式采用rpc。 插件机制,框架只做rpc基础通讯服务,插件打包。 插件 首要动作,调用一个动作就是执行一个动作,如获取新闻列表,发布...

    详解Stateflow建模与应用实例,简单stateflow建模实例,matlab

    这种可视化建模方法使得设计和理解复杂的系统变得更加直观。Stateflow结合了状态图和流程图,能够清晰地展示系统行为和状态之间的转换。 Stateflow的核心概念包括状态、转换、事件和数据。状态代表系统的不同行为或...

    zend framework源代码(简单的图书管理系统)

    - Zend Framework 提供了 `Zend_Translate` 组件,使得应用支持多语言变得简单。如果这个系统有国际化功能,你可以找到相关的配置和翻译文件。 通过研究这个简单的图书管理系统源代码,开发者可以学习到 Zend ...

    android中的窗口activity高级应用

    **Translucence Window**(半透明窗口)是Android 5.0(Lollipop)引入的新特性,它允许Activity的背景变得半透明,从而实现过渡效果和沉浸式体验。通过设置Window的属性,如FLAG_TRANSLUCENT_STATUS和FLAG_...

    易语言简单注册F1热键

    易语言是一种专为中国人设计的编程语言,它的目标是让编程变得简单、直观,使得非专业程序员也能快速上手。在易语言中,"简单注册F1热键"是指通过编程实现按下F1键时执行特定功能的过程。下面将详细解释这个过程中的...

    kpc卡通图片管理  

    它通过丰富的组织方式、强大的搜索功能和便捷的共享手段,让图片管理变得更加简单,极大地提升了用户在处理卡通图片时的工作效率。无论你是业余爱好者还是专业设计师,这款应用都能成为你管理卡通图片资源的得力助手...

    swift-Flow轻松管理iOS页面流让代码逻辑性更强

    在大型iOS应用中,管理这种流程可以变得相当复杂,特别是在处理条件分支、多个步骤和用户交互时。FlowKit是由Filip Zawada开发的一个工具,它提供了一种结构化的方式来处理这些情况,使得代码更清晰,更易于测试和...

    Java规则引擎工作原理及应用.pdf

    规则引擎的这种设计使得业务逻辑的修改和维护变得简单,无需改动应用程序代码,只需更新规则库即可。 在实际应用中,Java规则引擎可以广泛应用于金融、保险、供应链管理等领域,用于自动化决策流程,如信用评分、...

    分布式变电站概念及其在电力系统中的应用.pdf

    因此,为了处理这些日益庞大的数据信息,分布式理论变得至关重要。 分布式变电站概念的成功应用,首先需要找到符合其概念的电力设备集合。这些集合的特点包括电力数据归属特定集合、数据分布具有一定空间性、数据间...

    Flash动作脚本字典(chm)

    随着时间的推移,ActionScript经历了从AS1到AS3的重大升级,语法变得更加严谨,功能也更加强大,尤其是在Flash Professional CS3中引入了ActionScript 3.0,这标志着ActionScript成为一个完全的面向对象的编程语言,...

    mba课程之管理学附录1.doc

    然而,系统间的复杂关系可能使得实际应用变得困难。 7. 经验主义学派:通过案例研究寻找成功的管理实践,但难以提炼出普遍适用的管理原则。 8. 权变理论学派:强调管理方法应随环境变化而调整,但识别何时改变管理...

    推选图像自动化处理PPT资料.ppt

    图像自动化处理是数字图像处理领域中的一个重要概念,它旨在通过预先...通过理解并熟练掌握“动作”的创建、编辑、播放和存储,用户可以构建自己的工作流程,简化复杂的图像编辑任务,让图像处理变得更高效、更便捷。

    react中使用vuex管理状态

    Redux是一个非常流行的状态管理库,它遵循单向数据流的原则,使得状态变化变得可预测和可追踪。尽管Redux并非专门为React设计,但它与React的集成非常紧密,可以很好地满足React应用的状态管理需求。在React中使用...

Global site tag (gtag.js) - Google Analytics