
作者 | 孫健波(天元)阿裡雲技術專家
導讀:OAM Spec 經曆了近 3 個月的疊代,
v1alpha2版本終于釋出啦!新版本在堅持 OAM Spec 平台無關的基礎上,整體變得更 Kubernetes 友好化,很大程度上平衡了标準與可擴充性,更好的支援 CRD。如果你已經編寫了現成的 CRD Operator,可以平滑的接入到 OAM 體系中,并且享受到 OAM 模型的紅利。
目前 OAM 已經成為了包括阿裡、微軟、Upbond、諧雲等多家公司建構雲産品的核心架構。他們通過 OAM 建構了“以應用為中心”、使用者友好化的 Kubernetes PaaS;充分發揮 OAM 的标準化與可擴充性,實作 OAM 核心 Controller 的同時,快速接入了已有的 Operator 能力;通過 OAM 橫向打通多個子產品,破除了原有 Operator 彼此孤立、無法複用的窘境。
- 了解 OAM 的背景及由來,可以參考 《深度解讀!阿裡統一應用管理架構更新的教訓與實踐》 ;
- OAM 能為終端使用者帶來哪些價值?可以參考 《OAM 深入解讀:OAM 為雲原生應用帶來哪些價值?》
下面言歸正傳,讓我們來看一下 v1alpha2 到底做了哪些改動?
主要改動說明
為了友善大家閱讀,這裡隻羅列了最主要的改動點,一些細節還是以上遊
OAM Spec Github倉庫為準。
術語說明
- CRD(Custom Resource Definition):在 OAM 中說的 CRD 是一種泛指的自定義資源描述定義。在 K8s 的 OAM 實作中可以完全對應 K8s 的 CRD,在非 K8s 的實作中,OAM 的 CRD 需要包含 APIVersion/Kind 并且能夠描述字段進行校驗;
- CR (Custom Resource),OAM 中的 CR 是 CRD 的一個執行個體,是符合 CRD 中字段格式定義的一個資源描述。在 K8s 的 OAM 實作中可以完全對應 K8s 的 CR,在 非 K8s 的實作中,可以需要對齊 APIVersion/Kind 和字段格式定義。
主要改動 1 使用 Reference 模型定義 Workload、Trait 和 Scope
v1alpha1 原先的方式是這樣的:
// 老版本,僅對比使用
apiVersion: core.oam.dev/v1alpha1
kind: WorkloadType
metadata:
name: OpenFaaS
annotations:
version: v1.0.0
description: "OpenFaaS a Workload which can serve workload running as functions"
spec:
group: openfaas.com
version: v1alpha2
names:
kind: Function
singular: function
plural: functions
workloadSettings: |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"name", "image"
],
"properties": {
"name": {
"type": "string",
"description": "the name to the function"
},
"image": {
"type": "string",
"description": "the docker image of the function"
}
}
}
在原先的模式中,group/version/kind 分别是字段,spec 的校驗通過 jsonschema 表示,整體的格式實際上類似 CRD,但不完全一緻。
新版 v1alpha2 中徹底改為了引用模型,通過
WorkloadDefinition
TraitDefinition
ScopeDefinition
的形式,描述了一個引用關系。可以直接引用一個 CRD,name 就是 CRD 的名稱。對于非 K8s 的 OAM 實作來說,這裡的名字則是一個索引,可以找到類似 CRD 的校驗檔案,校驗檔案中包含 apiVersion 和 kind,以及相應的 schema 校驗。
- Workload
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: containerisedworkload.core.oam.dev
spec:
definitionRef:
# Name of CRD.
name: containerisedworkload.core.oam.dev
- Trait
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: manualscalertrait.core.oam.dev
spec:
appliesToWorkloads:
- containerizedworkload.core.oam.dev
definitionRef:
name: manualscalertrait.core.oam.dev
- Scope
apiVersion: core.oam.dev/v1alpha2
kind: ScopeDefinition
metadata:
name: networkscope.core.oam.dev
spec:
allowComponentOverlap: true
definitionRef:
name: networkscope.core.oam.dev
注意:
- 這裡對于 K8s 的 OAM 實作來說,name 就是 K8s 裡面 CRD 的 name,由
組成。社群的最佳實踐是一個 CRD 隻有一個 version 在叢集中運作,一般新 version 都會向前相容,更新時都一次性替換到最新 version。如果确實有 2 個 version 同時存在的場景,使用者也可以通過<plural-kind>.<group>
的方式進一步選擇;kubectl get crd <name>
- Definition 這一層不面向 end user,主要給平台實作使用,對于非 K8s 實作來說,如果存在多個 version 的場景,OAM 的實作平台可以給終端使用者展示不同 version 的選擇。
主要改動 2 直接嵌入 K8s CR 作為 Component 和 Trait 執行個體
原先的方式在 Workload 和 Trait 層面我們都隻把 CR 的 spec 部分拿出來,分别放在
workloadSettings
和
properties
字段裡。
這樣的方式雖然已經可以“推導”出 K8s CR,但是不利于 K8s 生态内的 CRD 接入,需要換種格式重新定義一遍 spec。
// 老版本,僅對比使用
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
name: rediscluster
spec:
workloadType: cache.crossplane.io/v1alpha1.RedisCluster
workloadSettings:
engineVersion: 1.0
region: cn
// 老版本,僅對比使用
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:
components:
- componentName: frontend
instanceName: web-front-end
parameterValues:
traits:
- name: manual-scaler
properties:
replicaCount: 5
現在的方式則直接嵌入 CR,可以看到,在
workload
trait
字段下面是完整的 CR 描述。
apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
name: example-server
spec:
prameters:
- name: xxx
fieldPaths:
- "spec.osType"
workload:
apiVersion: core.oam.dev/v1alpha2
kind: Server
spec:
osType: linux
containers:
- name: my-cool-server
image:
name: example/very-cool-server:1.0.0
ports:
- name: http
value: 8080
env:
- name: CACHE_SECRET
apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
name: cool-example
spec:
components:
- componentName: example-server
traits:
- trait:
apiVersion: core.oam.dev/v1alpha2
kind: ManualScalerTrait
spec:
replicaCount: 3
這樣的好處很明顯:
- 可以很容易的對接現有 K8s 體系裡的 CRD,甚至包括 K8s 原生的
(作為自定義 workload 接入)等資源;Deployment
- K8s CR 層面的字段定義是成熟的,解析和校驗也完全交由 CRD 體系。
這裡大家注意到 traits 下面是
[]trait{CR}
而不是
[]CR
的結構,多了一層看似無用的
trait
字段,主要由如下 2 個原因:
- 為後續在 trait 這個次元做擴充留下空間,比如可能的編排(
) 等。ordering
- 非 K8s 體系在這一層可以不嚴格按照 CR 的寫法來,完全自定義,不綁定 K8s 描述格式。
主要改動 3 參數傳遞使用 jsonPath 替換原先的 fromParam
研發能夠留出字段給運維覆寫,一直是 OAM 很重要的功能。
展現在 OAM Spec 的流程上就是:研發在 Component 裡面定義 parameter,運維在 AppConfig 裡面通過 parameterValue 去覆寫對應的參數。
最初的參數傳遞,是在每個字段後面有個
fromParam
字段,對于支援了自定義 schema 後,這樣的方式顯然是無法覆寫所有場景的:
// 老版本,僅對比使用
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
name: rediscluster
spec:
workloadType: cache.crossplane.io/v1alpha1.RedisCluster
parameters:
- name: engineVersion
type: string
workloadSettings:
- name: engineVersion
type: string
fromParam: engineVersion
後來我們曾經提議過這樣的方案:
// 老版本,僅對比使用
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
name: rediscluster
spec:
workloadType: cache.crossplane.io/v1alpha1.RedisCluster
parameters:
- name: engineVersion
type: string
workloadSettings:
engineVersion: "[fromParam(engineVersion)]"
這個方案最大的問題是 靜态的 IaD (Infrastructure as Data) 裡面加入了動态的函數,給了解和使用帶來了複雜性。
經過多方面的讨論,在新方案裡我們通過 JsonPath 的形式描述要注入的參數位置,在使用者了解上保證了 AppConfig 是靜态的。
apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
name: example-server
spec:
workload:
apiVersion: core.oam.dev/v1alpha2
kind: Server
spec:
containers:
- name: my-cool-server
image:
name: example/very-cool-server:1.0.0
ports:
- name: http
value: 8080
env:
- name: CACHE_SECRET
value: cache
parameters:
- name: instanceName
required: true
fieldPaths:
- ".metadata.name"
- name: cacheSecret
required: true
fieldPaths:
- ".workload.spec.containers[0].env[0].value"
fieldPaths 是個數組,每個元素定義了參數和對應 Workload 裡的字段。
apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
name: my-app-deployment
spec:
components:
- componentName: example-server
parameterValues:
- name: cacheSecret
value: new-cache
在 AppConfig 中還是走 parameterValues 去覆寫 Component 中的 parameter。
主要改動 4 ComponentSchematic 名稱改為 Component
原先元件這個概念叫 ComponentSchematic,這樣命名的主要原因是裡面混了一些文法描述和選擇,比如針對 Core Workload(
container
)和針對擴充 Workload (
workloadSettings
),寫法上不一樣的,
container
裡是定義具體的參數,
workloadSettings
更像是 schema(參數怎麼填的說明)。v1alpha1 版本的 WorkloadSetting 還融入了 type/description之類的,更顯得模棱兩可。
// 老版本,僅對比使用
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
name: rediscluster
spec:
containers:
...
workloadSettings:
- name: engineVersion
type: string
description: engine version
fromParam: engineVersion
...
在 v1alpha2 版本中,元件這個概念改為 Component,明确為 Workload 的執行個體,所有文法定義都是在WorkloadDefinition 中引用的實際 CRD 定義的。
在 K8s 實作中,WorkloadDefinition 就是引用 CRD ,Component.spec.workload 裡就是寫 CRD 對應的執行個體 CR。
apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
name: example-server
spec:
workload:
apiVersion: core.oam.dev/v1alpha2
kind: Server
spec:
...
主要改動 5 Scope 由 CR 單獨建立,不再由 AppConfig 建立
v1alpha1 中的 Scope 是由 AppConfig 建立的,從例子中可以看出,本質上它也是個 CR,可以“推導”建立出 CR來。但是由于 Scope 的定位是可以容納不同 AppConfig 中的 Component,且 Scope 本身并非一個 App,是以使用 AppConfig 建立 Scope 一直是不太合适的。
// 老版本,僅對比使用
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:
network-id: "[fromVariable(networkName)]"
subnet-ids: "my-subnet1, my-subnet2"
v1alpha2 新版本全面使用 CR 來對應執行個體,為了讓 Scope 的概念更清晰,更友善對應不同類型的 Scope,将 Scope 拿出來直接由 ScopeDefinition 定義的 CRD 對應的 CR 建立。例子如下所示:
apiVersion: core.oam.dev/v1alpha2
kind: ScopeDefinition
metadata:
name: networkscope.core.oam.dev
spec:
allowComponentOverlap: true
definitionRef:
name: networkscope.core.oam.dev
apiVersion: core.oam.dev/v1alpha2
kind: NetworkScope
metadata:
name: example-vpc-network
labels:
region: us-west
environment: production
spec:
networkId: cool-vpc-network
subnetIds:
- cool-subnetwork
- cooler-subnetwork
- coolest-subnetwork
internetGatewayType: nat
在 AppConfig 中使用 scope 引用如下所示:
apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
name: custom-single-app
annotations:
version: v1.0.0
description: "Customized version of single-app"
spec:
components:
- componentName: frontend
scopes:
- scopeRef:
apiVersion: core.oam.dev/v1alpha2
kind: NetworkScope
name: my-vpc-network
- componentName: backend
scopes:
- scopeRef:
apiVersion: core.oam.dev/v1alpha2
kind: NetworkScope
name: my-vpc-network
主要改動 6 移除 Variable 清單和 [fromVariable()] 動态函數
v1alpha1 版本中有 Variable 是為了 AppConfig 裡開源引用一些公共變量減少備援,是以加入了 Variable 清單。但實踐來看,減少的備援并沒有明顯減少 OAM spec 的複雜性,相反,增加動态的函數顯著增加了複雜性。
另一方面,fromVariable 這樣的能力完全可以通過 helm template/ kustomiz 等工具來做,由這些工具渲染出完整的 OAM spec,再進行使用。
是以這裡 variables 清單和相關的 fromVariable 均去掉,并不影響任何功能。
// 老版本,僅對比使用
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
name: my-app-deployment
spec:
variables:
- name: VAR_NAME
value: SUPPLIED_VALUE
components:
- componentName: my-web-app-component
instanceName: my-app-frontent
parameterValues:
- name: ANOTHER_PARAMETER
value: "[fromVariable(VAR_NAME)]"
traits:
- name: ingress
properties:
DATA: "[fromVariable(VAR_NAME)]"
主要改動 7 用 ContainerizedWorkload 替代原來的六種核心 Workload
因為現在已經統一用 WorkloadDefinition 定義 Workload,Component 變成了執行個體,是以原先的六種核心Workload 實際上都變成了同一個 WorkloadDefinition,字段描述完全一樣,唯一的不同是對 trait 限制和訴求不一樣。是以原先的六種核心 Workload 的 spec,統一修改為一種名為 ContainerizedWorkload 的 Workload 類型。
同時,這裡計劃要通過增加 policy 這樣的概念,來讓研發表達對運維政策的訴求,即 Component 中可以表達希望增加哪些 trait。
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: containerizedworkloads.core.oam.dev
spec:
definitionRef:
name: containerizedworkloads.core.oam.dev
一個使用 ContainerizedWorkload 示例:
apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
name: frontend
annotations:
version: v1.0.0
description: "A simple webserver"
spec:
workload:
apiVersion: core.oam.dev/v1alpha2
kind: ContainerizedWorkload
metadata:
name: sample-workload
spec:
osType: linux
containers:
- name: web
image: example/charybdis-single:latest@@sha256:verytrustworthyhash
resources:
cpu:
required: 1.0
memory:
required: 100MB
env:
- name: MESSAGE
value: default
parameters:
- name: message
description: The message to display in the web app.
required: true
type: string
fieldPaths:
- ".spec.containers[0].env[0].value"
下一步計劃
- 應用級别的元件間參數傳遞和依賴關系( workflow );
- Policy 方案 ,便于研發在 Component 對 trait 提出訴求;
- Component 增加版本的概念,同時給出 OAM 解決應用版本釋出相關方式 。
常見 FAQ
- 我們原有平台改造為 OAM 模型實作需要做什麼?
對于原先是 K8s 上的應用管理平台,接入改造為 OAM 實作可以分為兩個階段:
- 實作 OAM ApplicationConfiguration Controller(簡稱 AppConfig Controller),這個 Controller 同時包含 OAM 的 Component、WorkloadDefinition、TraitDefinition、ScopeDefinition 等 CRD。AppConfig Controller 根據 OAM AppConfig 中的描述,拉起原有平台的 CRD Operator;
- 逐漸将原先的 CRD Operator 根據關注點分離的思想,分為 Workload 和 Trait。同時接入和複用 OAM 社群中更多的 Workload、Trait,豐富更多場景下的功能。
- 現有的 CRD Operator 為接入 OAM 需要做什麼改變?
現有的 CRD Operator** 功能上可以平滑接入 OAM 體系,比如作為一個獨立擴充 Workload 接入。但是為了更好的讓終端使用者體會到 OAM 關注點分離的好處,我們強烈建議 CRD Operator 根據研發和運維不同的關注點分離為不同的 CRD,研發關注的 CRD 作為 Workload 接入 OAM,運維關注的 CRD 作為 Trait 接入 OAM。
目前,OAM 規範和模型實際已解決許多現有問題,但它的路程才剛剛開始。OAM 是一個中立的開源項目,我們歡迎更多的人參與其中,共同定義雲原生應用傳遞的未來。
參與方式:
- 釘釘掃碼進入 OAM 項目中文讨論群
作者簡介
孫健波(花名:天元)阿裡雲技術專家,是 OAM 規範的主要制定者之一,緻力于推動雲原生應用标準化。同時也在阿裡巴巴參與大規模雲原生應用傳遞與應用管理相關工作。團隊誠邀應用傳遞、Serverless、PaaS 領域的專家加入,歡迎聯系 jianbo.sjb AT alibaba-inc.com
招人啦!
雲原生應用平台誠邀 Kubernetes / Serverless / PaaS / 應用傳遞領域專家( P6-P8 )加盟:
- 工作年限:建議 P6-7 三年起,P8 五年起,具體看實際能力;
- 工作地點:國内(北京 / 杭州 / 深圳);海外(舊金山灣區 / 西雅圖);
- 崗位包含:架構師、技術專家、全棧工程師等。
履歷立刻回複,2~3 周出結果,履歷投遞:jianbo.sjb AT alibaba-inc.com。
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”