- 云原生
- 后端
【免费下载链接】crossplane
The Cloud Native Control Plane
本篇技术指南以 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.io、pkg.crossplane.io、ops.crossplane.io、protection.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:latest4.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定义可以看到两处显著变化:
- 镜像被替换:
spec.containers[0].image从hasheddan/tbs11:latest变成了hello-world:latest。这正是parameterValues中imageName: hello-world:latest通过fieldPaths路径写入的结果——参数注入发生在控制器创建 workload 实例的时刻,而不是Component定义时刻。 - 实例命名变化:实例名为
myapp-workload(对应Component中metadata.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 模板化:
resourceTemplates将ContainerizedWorkload的容器配置翻译成一个apps/v1的Deployment模板(命名空间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.secrets(corev1.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
本工作流中KubernetesApplication的targetSelector之所以留作 TODO,与同时期的集群消费模型演进直接相关。仓库中的 one-pager-consuming-k8s-clusters.md(同样为 Defunct 状态)记录了后续方案:
- 引入命名空间级的
KubernetesTarget资源,用于"发布"Kubernetes 集群:它可以通过clusterRef引用集群作用域的 Kubernetes 集群托管资源(如GKECluster),或通过connectionSecretRef直接引用命名空间内的本地 kubeconfigSecret——后者天然支持"Bring Your Own Cluster"(自带集群)场景; KubernetesApplication的调度从"按标签选择KubernetesCluster声明"改为"按标签选择KubernetesTarget",对应 API 字段由clusterSelector更名为targetSelector、clusterRef更名为targetRef,apiVersion由v1alpha1升至v1alpha2;- 新增一个自动创建控制器:当
KubernetesCluster声明变为 Bound 时,自动在命名空间内创建对应KubernetesTarget(带 ownerReference,随声明删除而清理),从而在无需用户具备创建KubernetesTarget权限的前提下保持自助服务。
这解释了本工作流示例中targetSelector的 TODO 状态:它处于"调度目标概念正在从集群声明向发布型资源迁移"的中间节点。
八、为什么止步于 POC:局限性与后续演进
OAM 工作流最终被标记为 Defunct 并整体移出 core Crossplane,除了"控制器逻辑不应留在核心"的组织原因外,其承载层KubernetesApplication的代理模型本身也面临体验问题。仓库中的 design-doc-agent.md 对此有直白的记录:
- 失去准入校验:
KubernetesApplicationResource是模板,编辑后需等控制器重新下发才能生效,错误不再被 admission 检查在写入时拦截,而是事后反映在状态里; - late initialization 问题:远端资源(如
Pod的spec.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
相关推荐
定义云原生应用:基于 OAM 开放应用模型的组件、特征与作用域实战指南
定义云原生应用:基于 OAM 开放应用模型的组件、特征与作用域实战指南 本篇技术指南以 define cloud native app.md https://l
教程云原生容器编排XTuner 多轮对话 SFT 微调实战:基于 multi_turn_1 示例的自定义数据集与 map_fn 全流程解析
XTuner 多轮对话 SFT 微调实战:基于 multi_turn_1 示例的自定义数据集与 map_fn 全流程解析 本文以 XTuner 官方示例 exa
大模型模型微调MiniCPM3 Function Calling 实战:基于 vLLM 与自定义 Tool Parser 的工具调用全流程指南
MiniCPM3 Function Calling 实战:基于 vLLM 与自定义 Tool Parser 的工具调用全流程指南 本文以 MiniCPM3 4B
人工智能大模型基础模型本地部署微调模型量化openBMB
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考