Open Component Model (OCM) 详解:云原生应用标准化交付的组件模型

张开发
2026/5/6 6:22:54 15 分钟阅读

分享文章

Open Component Model (OCM) 详解:云原生应用标准化交付的组件模型
1. 项目概述一个面向云原生应用管理的开源利器最近在梳理团队内部的云原生应用部署流程时发现了一个挺有意思的开源项目叫dtzp555-max/ocm。乍一看这个仓库名可能有点摸不着头脑但它的核心其实非常明确OCM也就是Open Component Model的缩写。简单来说这是一个用于定义、打包、传输和部署云原生应用及其所有依赖的组件模型规范与工具集。你可以把它想象成云原生世界的“乐高积木”标准说明书和“物流配送系统”。在微服务和容器化大行其道的今天一个应用往往由几十甚至上百个微服务、配置、策略、证书等组件构成。传统的做法是写一堆YAML清单用Helm Chart打包再用CI/CD流水线推送到各个环境。这个过程繁琐、容易出错而且组件之间的版本依赖、安全扫描、合规检查常常是割裂的。OCM的出现就是为了解决这个“最后一公里”的难题。它试图建立一个统一的、标准化的组件描述和交付框架让应用的“打包”和“运输”过程像收发快递一样清晰、可追溯、可验证。dtzp555-max/ocm这个仓库可以看作是OCM规范的一个具体实现或相关工具集。对于开发者、平台工程师和SRE来说掌握OCM意味着你能更优雅地管理复杂的多云、多集群应用分发实现真正意义上的“一次打包随处部署”。接下来我就结合自己的实践经验深入拆解一下OCM的核心思想、实操要点以及那些容易踩坑的地方。2. OCM核心思想与架构拆解2.1 什么是“组件”重新定义云原生交付物在OCM的世界观里一切可交付的软件实体都被抽象为“组件Component”。一个组件不仅仅是一个容器镜像它是一个自描述的、版本化的、包含所有必要元数据的集合体。一个典型的OCM组件可能包含资源Resources这是组件的实体内容比如一个Docker镜像的tar包、一个Helm Chart的tgz文件、一个JSON配置文件甚至是一个Terraform模块。源Sources描述这些资源是如何构建出来的例如指向Git仓库的提交哈希、构建流水线的ID。这提供了可追溯性。引用References声明此组件所依赖的其他组件。这形成了组件之间的依赖关系图。签名Signatures基于组件的描述符一个JSON文件生成的数字签名用于验证组件的完整性和来源。所有这些信息都被封装在一个叫做组件描述符Component Descriptor的JSON文件中。这个文件是OCM的核心它就像是这个“软件包裹”的运单上面详细列出了包裹里有什么资源、从哪里来源、依赖哪些其他包裹引用以及由谁签发签名。注意组件描述符本身不包含大的二进制文件如镜像层它只包含对这些资源的引用如镜像仓库的Digest。实际的二进制内容存储在OCIOpen Container Initiative兼容的仓库中比如Docker Registry、Harbor或者专门的OCI Artifact仓库。OCM复用OCI标准作为其存储和传输层这是它设计上的一个巧妙之处避免了重复造轮子。2.2 OCM的价值主张为什么需要它你可能会问我们有Helm、Kustomize、Carvel为什么还要OCM它们解决的问题层面不同。Helm/Kustomize更侧重于在Kubernetes集群内部署应用的配置渲染和生命周期管理。它们回答的是“如何把YAML应用到集群”的问题。OCM更侧重于在进入集群之前对应用进行打包、验证、传输和存储。它回答的是“如何把一堆分散的、有关联的制品作为一个逻辑整体安全可靠地从A点搬到B点”的问题。具体价值体现在标准化交付物无论你的应用技术栈如何JavaSpring Cloud, Go微服务Python ML模型最终都可以被打包成一个标准的OCM组件。这为跨团队、跨项目的制品管理提供了统一语言。完整的可追溯性从源代码提交到构建产物再到测试和部署整个链条的信息都记录在组件描述符中。出现安全漏洞时你可以快速定位受影响的组件版本及其构建来源。安全的供应链通过数字签名和访问控制确保组件在传输过程中不被篡改并且只能被授权的消费者使用。高效的跨环境流转组件一旦打包完成可以像镜像一样被推送到仓库然后被拉取到开发、测试、预发、生产等不同环境无需为每个环境重新打包或处理复杂的依赖解析。2.3dtzp555-max/ocm仓库的角色通常以ocm命名的仓库可能是OCM规范参考实现的一部分或者是一个命令行工具CLI用于创建、操作、验证OCM组件。其核心功能可能包括ocm component create基于一个蓝图目录生成组件描述符。ocm component transfer将组件及其所有资源从一个仓库推送到另一个仓库。ocm component verify验证组件的签名和完整性。ocm resource get获取组件中的特定资源。它充当了开发者与OCM生态系统之间的桥梁让你能够以命令行的方式实践OCM的整个工作流。3. 从零开始构建你的第一个OCM组件理论说了这么多我们来点实际的。假设我们有一个简单的Web应用它由一个Go语言编写的后端服务打包成Docker镜像和一个Nginx前端也是一个Docker镜像组成外加一个环境特定的配置文件。3.1 环境准备与工具安装首先你需要安装OCM CLI工具。由于dtzp555-max/ocm的具体构建方式可能在其README中这里我以常见的安装方式为例。通常你可以从GitHub Releases页面下载预编译的二进制文件。# 假设从GitHub release下载请替换为实际版本和URL VERSIONv0.1.0 wget https://github.com/dtzp555-max/ocm/releases/download/${VERSION}/ocm-linux-amd64 chmod x ocm-linux-amd64 sudo mv ocm-linux-amd64 /usr/local/bin/ocm # 验证安装 ocm version同时你需要一个可以存储OCI Artifact的仓库。除了专业的Harbor支持OCI Artifact像Google Container Registry (GCR)、Amazon ECR、Azure Container Registry (ACR) 以及开源的DistributionDocker Registry的较新版本v2.7也支持。为了方便演示我们可以在本地启动一个测试用的OCI仓库比如使用registry:2镜像。docker run -d -p 5000:5000 --name my-oci-registry registry:2这样我们就有了一个本地的OCI仓库地址是localhost:5000。3.2 创建组件蓝图BlueprintOCM组件不是凭空产生的它需要一个“配方”这个配方就是组件蓝图。蓝图通常是一个目录里面包含一个component-descriptor.yaml文件或类似名称用来定义组件的元数据、资源和依赖。让我们创建一个简单的蓝图目录结构my-webapp-component/ ├── component-descriptor.yaml └── resources/ ├── backend-image.yaml ├── frontend-image.yaml └── config.yamlcomponent-descriptor.yaml是核心# component-descriptor.yaml apiVersion: ocm.software/v3alpha1 # OCM描述符的API版本 kind: ComponentDescriptor metadata: name: github.com/myorg/my-webapp # 组件的全局唯一标识通常用域名倒写 version: 1.0.0 # 组件版本 provider: name: my-team repositoryContexts: # 此描述符发布到的目标仓库 - type: ociRegistry baseUrl: localhost:5000 componentNameMapping: urlPath # 组件名在仓库中的映射方式 spec: resources: # 组件包含的资源列表 - name: backend-service type: ociImage version: 1.2.3 relation: local access: # 如何访问这个资源 type: ociArtifact imageReference: my-dockerhub/backend:1.2.3 # 实际的镜像地址 - name: frontend-nginx type: ociImage version: 1.0.0 relation: local access: type: ociArtifact imageReference: nginx:alpine - name: application-config type: plainText version: 1.0.0 relation: local access: type: localBlob filename: resources/config.yaml # 指向蓝图目录内的文件 sources: # 可选构建来源 - name: app-source type: git version: main-a1b2c3d access: type: gitHub repoUrl: https://github.com/myorg/my-webapp commit: a1b2c3d4e5f...resources/config.yaml是一个示例配置文件# config.yaml database: host: ${DB_HOST} port: 5432 logging: level: INFObackend-image.yaml和frontend-image.yaml在这里不是必须的因为我们在主描述符中已经通过imageReference直接引用了外部镜像。但如果你的镜像是本次构建新产生的你可能需要在一个单独的步骤中构建并推送它们到仓库然后在描述符中引用。3.3 打包与推送组件有了蓝图我们就可以使用ocmCLI 来创建并推送组件了。这个过程通常分为两步创建组件归档或直接推送CLI会读取蓝图解析所有资源对于localBlob类型的资源会将其文件内容打包生成完整的组件描述符并可以选择将其打包成一个tar归档或者直接推送到远程仓库。# 切换到蓝图目录 cd my-webapp-component # 方式一创建本地归档便于离线传输或检查 ocm component archive create . --file my-webapp-component.tar # 方式二直接推送到OCI仓库 # 首先登录你的OCI仓库如果需要 # docker login localhost:5000 # 然后推送 ocm component transfer . --uploader oci --target localhost:5000当你执行推送命令时ocm工具会做以下几件事验证component-descriptor.yaml的格式。收集所有type: localBlob的资源文件计算它们的哈希值。将组件描述符包含所有资源的元数据和访问信息作为一个OCI Artifact推送到目标仓库。如果资源本身如镜像不在目标仓库它不会自动推送镜像本身因为访问方式ociArtifact只是引用。你需要确保引用的镜像在目标仓库可访问或者使用ocm的--copy-resources选项如果支持来一并复制。实操心得在实际生产环境中镜像和组件描述符的存储最好规划在一起。例如使用Harbor同时存储Docker镜像和OCM组件。这样组件的传输复制操作可以确保所有依赖的资源也被一并复制到目标环境真正做到自包含交付。直接引用公共镜像如nginx:alpine则问题不大。3.4 验证与拉取组件组件推送到仓库后你可以像拉取Docker镜像一样拉取它不过拉取的是其描述符。# 拉取组件描述符到本地 ocm component get localhost:5000/github.com/myorg/my-webapp:1.0.0 -o yaml downloaded-descriptor.yaml # 验证组件的签名如果组件被签名了 ocm component verify localhost:5000/github.com/myorg/my-webapp:1.0.0 --public-key ./public-key.pem拉取下来的描述符文件可以被下游的部署工具如FluxCD、ArgoCD的OCM插件或者自定义控制器消费。这些工具会解析描述符获取其中定义的资源镜像地址、配置文件然后执行实际的Kubernetes资源部署。4. 深入核心组件描述符的细节与高级用法4.1 资源访问器Access Methods详解资源如何被访问是OCM的关键。上面例子中我们看到了ociArtifact和localBlob。OCM支持多种访问器ociArtifact最常用用于引用存储在OCI仓库中的任何Artifact镜像、Helm Chart、SBOM文件等。imageReference字段是核心。localBlob用于将一些小文件如配置文件、脚本直接内联或与描述符一起存储在组件仓库中。它通常有一个filename指向蓝图内的文件在打包时文件内容会被处理。gitHub/gitLab用于引用源代码仓库中的特定文件或目录。http/s3/gcs用于引用通过通用协议可访问的资源。选择哪种访问器取决于资源的特点和你的管理策略。大文件、二进制制品适合用ociArtifact小的、与组件版本强绑定的配置文件适合用localBlob。4.2 源Sources与软件物料清单SBOM“源”的部分对于安全审计和合规至关重要。除了基本的Git信息你还可以关联构建流水线信息比如Jenkins Build ID、GitHub Actions Run ID。漏洞扫描报告可以将Trivy、Grype等工具生成的SBOM软件物料清单或漏洞报告作为另一个资源或源附加到组件上。许可证信息声明组件及其依赖的许可证。一个包含SBOM的组件其描述符可能如下所示spec: resources: - name: my-app-image type: ociImage ... sources: - name: app-source type: git ... resources: - name: sbom-spdx type: spdxJson relation: external access: type: ociArtifact imageReference: localhost:5000/sbom/my-app:1.0.0这样当你审查这个组件时不仅能拿到可执行件还能立刻拿到它的“成分表”和“体检报告”。4.3 组件依赖与版本解析复杂的应用由多个组件构成。OCM允许你在组件描述符中声明对其它组件的引用。spec: references: - name: redis-cache componentName: github.com/myorg/redis-cluster version: 6.2.10 - name: postgres-db componentName: github.com/myorg/postgres-ha version: 14.5这声明了当前应用组件依赖于特定版本的Redis和PostgreSQL组件。一个高级的OCM工具或平台可以解析这些依赖确保在部署应用时这些依赖组件也以正确的版本被部署或已经就绪。这有点像编程语言中的包管理如Go mod, npm但作用于基础设施和应用层面。5. 集成到CI/CD流水线实战工作流OCM不是用来替代现有CI/CD工具的而是嵌入其中增强制品管理和交付阶段。一个典型的工作流如下代码提交触发构建开发者推送代码到Git。CI流水线构建与测试编译代码运行单元测试。构建Docker镜像并推送到镜像仓库registry.mycom.com/myapp:${COMMIT_SHA}。生成SBOM扫描镜像漏洞将报告推送到OCI仓库。使用OCM CLI打包组件创建一个组件蓝图其中资源指向刚构建的镜像和SBOM报告源信息指向本次Git提交。执行ocm component transfer将组件推送到公司的OCM组件仓库ocm-registry.mycom.com。CD流水线交付与部署触发条件当有新的组件版本被推送到OCM仓库时可通过Webhook。CD工具如ArgoCD with OCM插件拉取新的组件描述符。解析描述符获取需要部署的镜像地址、配置文件等资源。根据部署模板如Kustomize overlay将具体的镜像Tag和配置值渲染成最终的Kubernetes YAML。将渲染后的YAML同步到目标Kubernetes集群。这个流程的关键在于应用的版本号在OCM组件层面被定义和管理如1.0.0commit-a1b2c3d而不是散落在各个镜像Tag或配置文件中。部署工具只需关注“部署哪个版本的组件”组件内部的具体内容由OCM描述符保证一致性。6. 常见问题、排查技巧与选型思考6.1 常见问题速查表问题现象可能原因排查步骤与解决方案ocm component transfer失败报错“unsupported media type”或“404”。目标OCI仓库不支持存储自定义的OCI Artifact类型如application/vnd.ocm.component.v1targzip。1. 确认仓库是否支持OCI Artifact。Harbor 2.0 默认支持。对于Docker Distribution需确认版本2.7且配置了REGISTRY_STORAGE_OCI_MEDIATYPESapplication/vnd.oci.*,application/vnd.cncf.*。2. 尝试推送一个普通的Docker镜像到该仓库确认网络和权限正常。拉取组件后部署工具无法获取资源如镜像拉取失败。组件描述符中资源的access.imageReference指向了一个下游环境无法访问的镜像仓库如内网地址。1. 在打包组件时应使用在所有目标环境包括生产都能访问的通用镜像仓库地址如公司统一的内网镜像中心。2. 或者在CD阶段实现一个“镜像重写image rewrite”策略将组件内的镜像引用替换为当前环境可用的地址。组件签名验证失败。用于验证的公钥不匹配或者组件在传输后被篡改。1. 确认使用的公钥与签名时使用的私钥对应。2. 重新从可信源拉取组件描述符。3. 检查签名流程是否在安全的CI环境中进行私钥是否妥善保管。组件依赖解析复杂循环依赖或版本冲突。组件间依赖关系设计不合理。1. 在组件设计阶段遵循单一职责原则避免大而全的组件。2. 使用OCM仓库的查询API或CLI工具在打包前分析依赖图。3. 考虑引入类似语义化版本的范围依赖并在CD阶段进行冲突解决。6.2 选型思考什么时候该用OCMOCM引入了一定的复杂度并非所有项目都需要。考虑引入OCM如果你的团队或企业面临以下情况应用结构复杂微服务数量多且有复杂的中间件、数据库等依赖。交付环境复杂需要交付到多个独立的Kubernetes集群、边缘站点或不同的云厂商。合规与安全要求高需要对交付物的来源、构建过程、包含的软件成分有严格的审计和追溯要求。正在构建内部平台希望为内部开发者提供一套标准的、自助式的应用打包和交付能力。对于简单的单服务应用或者所有部署都在单一集群内完成的情况直接使用Helm Chart或Kustomize可能更轻量、更直接。6.3 性能与优化技巧组件粒度不要将所有东西都塞进一个巨型组件。按功能或服务边界划分组件。例如将前端、后端、数据库初始化脚本分别作为独立组件然后由一个顶层“应用”组件引用它们。这样有利于复用和独立升级。资源缓存在跨地域传输组件时其引用的镜像可能很大。可以利用OCI仓库的镜像缓存代理如Harbor的Replication功能或P2P分发工具如Dragonfly来加速资源拉取。描述符精简只包含必要的元数据。避免在component-descriptor.yaml中存储过大的文本块如完整的日志文件应将其作为localBlob资源或存储到外部系统如S3并通过链接引用。我个人在实践OCM的过程中最大的体会是它强迫团队去思考和应用“声明式”的交付。一旦将交付物标准化很多手工的、易出错的步骤就可以被自动化工具链替代从长远看这对于提升软件交付的效率和可靠性是有巨大价值的。刚开始可能会觉得YAML文件变多了流程变复杂了但当你需要排查一个生产环境的问题能通过组件版本瞬间定位到对应的代码提交、构建日志和安全扫描报告时你会觉得这一切都是值得的。

更多文章