news 2026/9/23 17:44:49

Crossplane OAM 工作流实战:基于 core.oam.dev 的组件化应用定义、参数注入与跨集群调度全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Crossplane OAM 工作流实战:基于 core.oam.dev 的组件化应用定义、参数注入与跨集群调度全流程解析
  • 云原生
  • 后端

【免费下载链接】crossplane

The Cloud Native Control Plane

项目地址:https://gitcode.com/gh_mirrors/cr/crossplane
点击查看免费下载

本篇技术指南以 Crossplane 仓库中标记为 Defunct(已废弃)的 OAM POC 设计文档为主体,完整还原一次基于 Open Application Model(OAM)的应用部署链路:从Component定义部署单元,到ApplicationConfiguration组装应用并注入参数,再到ContainerizedWorkload被控制器打包为KubernetesApplication并调度到目标 Kubernetes 集群。读者将掌握这套 POC 工作流中每个 CRD 的职责、控制器在每一环的调和行为、参数覆盖机制,以及其底层KubernetesApplication架构的调度/应用/资源三控制器模型,并了解该方案最终被废弃、演进的历史脉络。

一、文档定位与历史背景

本文所讲解的工作流出自仓库中的设计文档 one-pager-oam-workflow.md,其状态为Defunct(废弃),由 Dan Mangum(@hasheddan)撰写,对应 Crossplane 早期 PR #1276 中的 OAM 类型实现。文档开篇明确了两点关键事实:

  • 文档描述的是初始 POC(概念验证)阶段的控制器行为;
  • 这些控制器计划在后续迭代中移出 core Crossplane,不属于长期架构。

文档同时提到,OAM 的长期实现方案参考了独立的 "OAM Runtime Architecture" 设计文档。因此,阅读本文时应将其视为一份历史设计快照——它记录了 Crossplane 曾经如何通过 OAM 模型承载"应用定义—组装—调度"的完整链路,这对理解 Crossplane 应用建模思想的演进(尤其是后来的 Composition 体系)具有重要参考价值。

从当前仓库源码结构看,这一演进确实发生了:在internal目录下已搜索不到任何core.oam.dev相关的控制器实现,cluster/crds目录中的 CRD 清单(如 apiextensions.crossplane.io_compositeresourcedefinitions.yaml、pkg.crossplane.io_providers.yaml 等)也全部归属于apiextensions.crossplane.iopkg.crossplane.ioops.crossplane.ioprotection.crossplane.io等 API 组,core.oam.dev类型的 CRD 已不在安装清单中——这正是文档预告的"移出 core Crossplane"的最终结果。

二、安装:随 Crossplane 一起部署的 OAM 组件

2.1 随安装落地的 OAM CRD

按照文档描述,安装 Crossplane 时,以下 6 个 OAM CRD 会被一并安装:

CRD角色定位
ApplicationConfiguration组装多个Component并注入参数,生成工作负载实例的应用级资源
Component定义一个可被ApplicationConfiguration引用的部署单元
TraitDefinition定义可附加到工作负载上的运维特征(如扩缩容、监控)
ScopeDefinition定义工作负载的适用范围/边界
WorkloadDefinition将某种工作负载类型(apiVersion/kind)注册为 OAM 可用的 workload
ContainerizedWorkload核心的容器化工作负载类型,描述容器的镜像、资源、环境变量与端口

2.2 自动创建的 WorkloadDefinition 实例

除 CRD 之外,Crossplane 安装时还会自动创建一个WorkloadDefinition实例,用于将内置的ContainerizedWorkload类型注册为 OAM 可用的核心 workload:

apiVersion: core.oam.dev/v1alpha2 kind: WorkloadDefinition metadata: name: containerizedworkloads.core.oam.dev spec: definitionRef: name: containerizedworkloads.core.oam.dev

这个实例的存在使得ContainerizedWorkload可以被 OAMComponent引用。文档特别强调了一种可扩展模式:要引入其他工作负载类型,只需两件事——为该 workload 创建对应的 CRD,再创建一个引用该 CRD 的WorkloadDefinition。也就是说,WorkloadDefinition是 OAM 工作负载类型的"注册表入口",新类型接入无需改动 Crossplane 核心代码。

2.3 随安装启动的 OAM 控制器

安装完成后,两个 OAM 相关的控制器会被启动,它们构成了工作流的执行引擎:

  • Application Configuration controller:监听ApplicationConfiguration资源,根据其内联参数和所引用的Component,创建对应的工作负载实例;
  • Containerized Workload controller:监听ContainerizedWorkload资源,将其打包为KubernetesApplication,以便调度到目标集群。

这两者的职责划分非常清晰:前者完成"OAM 模型 → 具体工作负载"的翻译,后者完成"工作负载 → 可调度应用单元"的打包。后续第 3~5 节将沿这两条链路逐一展开。

三、创建 Component:定义可复用的部署单元

Component是用户定义部署单元的入口:它描述一个应用组件需要什么样的 workload(在 POC 中即ContainerizedWorkload),并可通过parameters暴露可调参数供上层覆盖。

3.1 完整示例

apiVersion: core.oam.dev/v1alpha2 kind: Component metadata: name: myapp spec: workload: apiVersion: core.oam.dev/v1alpha2 kind: ContainerizedWorkload metadata: name: my-workload spec: osType: linux containers: - name: tbs11-app image: hasheddan/tbs11:latest resources: cpu: required: 1.0 memory: required: 100MB env: - name: DB_HOST valueFrom: secretKeyRef: name: mysqlconn key: endpoint - name: DB_USER valueFrom: secretKeyRef: name: mysqlconn key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: mysqlconn key: password ports: - containerPort: 8080 parameters: - name: imageName required: false fieldPaths: - "spec.containers[0].image"

3.2 关键设计点

  • workload 字段:内嵌一个完整的 workload 对象。这里是ContainerizedWorkload,其spec描述运行平台(osType: linux)、容器镜像、资源需求(CPU 1.0 核、内存 100MB)、环境变量与端口。环境变量使用了valueFrom.secretKeyRef,从名为mysqlconn的 Secret 中读取endpoint/username/password——这体现了 Crossplane 早期"托管资源连接信息以 Secret 注入应用"的消费模型。
  • parameters 机制spec.parameters声明组件对外暴露的可调参数。示例中的imageName参数通过fieldPaths: ["spec.containers[0].image"]将参数名映射到 workload 内部字段路径,required: false表示该参数可选。这是实现"同一组件在不同环境复用不同镜像"的关键机制。
  • 不触发任何调和:文档明确说明,创建Component本身不会触发任何 Crossplane 控制器的调和动作Component只是"图纸",真正触发工作负载创建的是引用它的ApplicationConfiguration

四、创建 ApplicationConfiguration:组装组件并注入参数

要让上述Component真正部署,用户必须创建一个引用它的ApplicationConfiguration

4.1 完整示例

apiVersion: core.oam.dev/v1alpha2 kind: ApplicationConfiguration metadata: name: myapp-dev spec: components: - componentName: myapp parameterValues: - name: imageName value: hello-world:latest

4.2 控制器的调和行为

创建ApplicationConfiguration后,Application Configuration controller 会入队一次调和(reconcile)。调和期间,控制器会为spec.components每一个引用的Component创建工作负载实例。就本示例而言,控制器将根据myapp组件的定义,实例化一个ContainerizedWorkload

apiVersion: core.oam.dev/v1alpha2 kind: ContainerizedWorkload metadata: name: myapp-workload spec: osType: linux containers: - name: tbs11-app image: hello-world:latest # 由 ApplicationConfiguration controller 根据其 spec 中的参数替换 resources: cpu: required: 1.0 memory: required: 100MB env: - name: DB_HOST valueFrom: secretKeyRef: name: mysqlconn key: endpoint - name: DB_USER valueFrom: secretKeyRef: name: mysqlconn key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: mysqlconn key: password ports: - containerPort: 8080

对照第 3 节的Component定义可以看到两处显著变化:

  1. 镜像被替换spec.containers[0].imagehasheddan/tbs11:latest变成了hello-world:latest。这正是parameterValuesimageName: hello-world:latest通过fieldPaths路径写入的结果——参数注入发生在控制器创建 workload 实例的时刻,而不是Component定义时刻。
  2. 实例命名变化:实例名为myapp-workload(对应Componentmetadata.name: my-workload与上层命名规则的组合),而不是Component的名字myapp

其余字段(资源配额、环境变量、端口)被原样继承,说明ApplicationConfiguration只负责"组装 + 覆盖",未声明覆盖的部分保持Component定义不变。

五、从 ContainerizedWorkload 到 KubernetesApplication:打包与调度

ContainerizedWorkload实例的创建会再次触发其控制器的调和。此时轮到第二个控制器——Containerized Workload controller——登场:它将ContainerizedWorkload打包进一个KubernetesApplication,从而进入 Crossplane 既有的调度体系。

5.1 打包生成的 KubernetesApplication

apiVersion: workload.crossplane.io/v1alpha1 kind: KubernetesApplication metadata: name: myapp-workload-0003 spec: resourceSelector: matchLabels: app: 2a23de82-58e4-11ea-8e2d-0242ac130003 # 该 ContainerizedWorkload 的 UID targetSelector: matchLabels: # TODO: 是否透传调度标签? resourceTemplates: - metadata: name: myappworkload-deployment labels: app: 2a23de82-58e4-11ea-8e2d-0242ac130003 spec: template: apiVersion: apps/v1 kind: Deployment metadata: namespace: default name: myapp-workload labels: app: 2a23de82-58e4-11ea-8e2d-0242ac130003 spec: selector: matchLabels: app: 2a23de82-58e4-11ea-8e2d-0242ac130003 template: metadata: labels: app: 2a23de82-58e4-11ea-8e2d-0242ac130003 spec: - name: tbs11-app image: hello-world:latest resources: cpu: required: 1.0 memory: required: 100MB env: - name: DB_HOST valueFrom: secretKeyRef: name: mysqlconn key: endpoint - name: DB_USER valueFrom: secretKeyRef: name: mysqlconn key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: mysqlconn key: password ports: - containerPort: 8080

注:上述resourceTemplates[0].spec.template中的 Deployment 定义在 POC 文档中省略了containers键(原文直接在spec.template.spec下列出容器条目)。按 Kubernetes Deployment 对象模型,实际生效的模板应在spec.template.spec.containers下声明容器;此处保留原文结构,便于对照原始设计文档。

5.2 打包逻辑的关键语义

  • resourceSelector 与 UID 标签resourceSelector.matchLabels使用app: <ContainerizedWorkload 的 UID>作为关联键。控制器用 UID 而非名称做标签,是为了在跨集群场景下保持唯一性,避免不同命名空间/集群中的同名资源互相干扰。
  • targetSelector 与调度标签targetSelector.matchLabels留空并带TODO注释("是否透传调度标签?"),说明在 POC 阶段,调度目标的选择尚未完整设计——这是文档自带的未决问题标记。
  • resourceTemplates 模板化resourceTemplatesContainerizedWorkload的容器配置翻译成一个apps/v1Deployment模板(命名空间default、Deployment 名myapp-workload),容器镜像、资源配额、环境变量、端口全部继承自 workload 定义。

文档最后指出:从此之后,KubernetesApplication将按照其既有控制器的行为被调度。换言之,OAM 工作流到这一步就完成了与既有 Crossplane 工作负载体系的对接——后续的调度、远程资源创建与状态回写不再属于 OAM 专属逻辑。

六、底层架构佐证:KubernetesApplication 的三控制器模型

本工作流中作为调度终点的KubernetesApplication,其完整架构在仓库的姊妹设计文档 design-doc-complex-workloads.md 中有系统性论述(该文档同样标记为 Defunct)。理解这套模型,才能看懂第 5 节中"按既有控制器的行为被调度"的具体含义。该文档提出将当时compute.crossplane.io/v1alpha1组的Workload替换为workload.crossplane.io/v1alpha1组的KubernetesApplication,并将控制器拆分为三个各司其职的角色:

控制器职责
scheduler controller监听KubernetesApplication,将其调度(分配)到某个KubernetesCluster
application controller监听已调度的KubernetesApplication,按resourceTemplates创建/更新/删除KubernetesApplicationResource,设置 controller reference,维护.status.desiredResources.status.submittedResources统计
resource controller监听已调度的KubernetesApplicationResource,向目标集群传播依赖 Secret、创建/更新模板化资源(Deployment、Service、Job、ConfigMap 等),并把远端资源的.status回写到自身.status.remote

其中与本工作流直接相关的设计决策包括:

  • 原子调度单元KubernetesApplication是不可跨集群拆分的调度原子,其下每个KubernetesApplicationResource恰好模板化一个任意 Kubernetes 资源。工作流中的 Deployment 模板正是被KubernetesApplicationResource包裹后才下发到目标集群。
  • Secret 传播与命名KubernetesApplicationResource通过.spec.secretscorev1.LocalObjectReference列表)声明依赖的托管资源连接 Secret,控制器将其传播到模板化资源所在命名空间。为避免多个模板引用同一 Secret 时的命名冲突,传播后的 Secret 名由资源模板名与 Secret 名拼接而来——例如名为wordpress-deployment的资源模板引用名为mysql的 Secret,传播到目标集群后将成为wordpress-deployment-mysql
  • 命名空间的"无立场"约定:文档明确反对在应用级别统一指定目标命名空间,理由是模板可指向任意资源类型(包括Namespace、CRD 等集群作用域资源),统一指定会带来令人意外的行为。因此命名空间由每个资源模板的对象元数据自行决定,未指定时按 Kubernetes 惯例落入default命名空间——与本工作流示例中namespace: default的行为一致。
  • 所有权与防冲突:application controller 通过 controller reference 认领自己模板化的KubernetesApplicationResource,避免多个应用争夺同名资源;远程资源则由 resource controller 打上kubernetesapplicationresource.workload.crossplane.io/uid注解来标识归属,防止两个模板竞争创建同一个远端 Deployment。
  • 状态透明:用户无需连接目标集群,即可通过KubernetesApplicationResource.status.remote查看远端资源的原始状态。

七、调度目标的演进:从 clusterSelector 到 KubernetesTarget

本工作流中KubernetesApplicationtargetSelector之所以留作 TODO,与同时期的集群消费模型演进直接相关。仓库中的 one-pager-consuming-k8s-clusters.md(同样为 Defunct 状态)记录了后续方案:

  • 引入命名空间级的KubernetesTarget资源,用于"发布"Kubernetes 集群:它可以通过clusterRef引用集群作用域的 Kubernetes 集群托管资源(如GKECluster),或通过connectionSecretRef直接引用命名空间内的本地 kubeconfigSecret——后者天然支持"Bring Your Own Cluster"(自带集群)场景;
  • KubernetesApplication的调度从"按标签选择KubernetesCluster声明"改为"按标签选择KubernetesTarget",对应 API 字段由clusterSelector更名为targetSelectorclusterRef更名为targetRefapiVersionv1alpha1升至v1alpha2
  • 新增一个自动创建控制器:当KubernetesCluster声明变为 Bound 时,自动在命名空间内创建对应KubernetesTarget(带 ownerReference,随声明删除而清理),从而在无需用户具备创建KubernetesTarget权限的前提下保持自助服务。

这解释了本工作流示例中targetSelector的 TODO 状态:它处于"调度目标概念正在从集群声明向发布型资源迁移"的中间节点。

八、为什么止步于 POC:局限性与后续演进

OAM 工作流最终被标记为 Defunct 并整体移出 core Crossplane,除了"控制器逻辑不应留在核心"的组织原因外,其承载层KubernetesApplication的代理模型本身也面临体验问题。仓库中的 design-doc-agent.md 对此有直白的记录:

  • 失去准入校验KubernetesApplicationResource是模板,编辑后需等控制器重新下发才能生效,错误不再被 admission 检查在写入时拦截,而是事后反映在状态里;
  • late initialization 问题:远端资源(如Podspec.node)被控制器延迟补全的字段,由于模板非强类型且只回写 status,用户难以区分"自己声明的期望"与"控制器补全的值";对数组元素执行 PATCH 时可能整体替换,破坏被补全的簿记字段;
  • 存量应用迁移成本高:要把一个 Helm chart 部署到与 Crossplane 不同集群,需要把每个资源逐一改造成KubernetesApplicationResource模板,改造量随 chart 规模线性放大。

这些痛点指向了一个方向:与其在中心集群"推送"代理资源到远端,不如让应用运行在目标集群、以"拉取"方式消费中心集群的托管资源。这一认识深刻影响了 Crossplane 后续的 Composition 与 Provider 体系(参见仓库 design/design-doc-composition.md 等设计文档),也是本文所讲 POC 工作流最重要的历史价值——它完整演练了"应用模型(OAM)—工作负载抽象(ContainerizedWorkload)—调度单元(KubernetesApplication)"的三层抽象链条,为后来者理解 Crossplane 应用建模的取舍提供了不可多得的对照样本。

结语

Component的静态定义,到ApplicationConfiguration的参数注入,再到ContainerizedWorkload被打包为KubernetesApplication进入调度体系,这份 OAM POC 工作流展示了 Crossplane 早期在应用建模层的一次完整实践。它虽然已经废弃,但其暴露的控制器拆分、参数覆盖、UID 标签关联、Secret 传播约定等设计细节,仍然是以史为鉴理解 Crossplane 架构演进的关键材料。希望本文档的完整还原能帮助读者在研究 one-pager-oam-workflow.md 及相关设计文档时,快速建立从"应用定义"到"集群调度"的全局视图。

  • 云原生
  • 后端

【免费下载链接】crossplane

The Cloud Native Control Plane

项目地址:https://gitcode.com/gh_mirrors/cr/crossplane
点击查看免费下载

相关推荐

上一篇:规避法律风险:SillyTavern用户必须了解的知识产权合规指南
下一篇:终极指南:Cherry Studio端到端加密如何保障您的数据安全

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 17:44:47

《君子之交》深度书评:人物、阅读顺序与txt合集整理指南

从来没有哪本小说&#xff0c;让我在读完txt全集之后&#xff0c;把手机扣在桌上发了十分钟呆。《君子之交》做到了。它连着一个续篇&#xff0c;还带一组番外&#xff0c;合在一起像一坛埋了很多年的酒&#xff0c;入口不烈&#xff0c;后劲却大得离谱。我后来又把文件里的“正…

作者头像 李华
网站建设 2026/9/23 17:44:11

政府电子签章服务商怎么选:立约笔河北CA四川CA场景对比

政务电子签章核心概念区分当前政务数字化转型进程中&#xff0c;大量用户检索政府电子签章系统哪家靠谱、怎么选、哪些符合合规要求。本次说明不排名不打分&#xff0c;统一采用客群适配、部署方式、合规底座、接入场景四个维度评估&#xff0c;不比价格&#xff0c;所有事实均…

作者头像 李华
网站建设 2026/9/23 17:41:19

仓库托盘检测为何必须用YOLO+VOC双格式数据集

简介&#xff1a;本资源是面向计算机视觉初学者与工业检测开发者的目标检测专用数据集&#xff0c;聚焦仓库场景下的托盘识别任务&#xff0c;可直接用于YOLO、Faster R-CNN等主流模型的训练与评估。压缩包共2000个文件&#xff0c;含1182张高清JPG图像、1182份VOC格式XML标注&…

作者头像 李华