存储版本 (Storage Versions)

Kubernetes API 服务器存储对象时,依赖于 etcd 兼容的后端存储(通常后端存储本身就是 etcd)。每个对象都使用该 API 类型的特定版本进行序列化;例如,ConfigMap 的 v1 表示形式。Kubernetes 使用“存储版本”(storage version)一词来描述对象在集群中的存储方式。

Kubernetes API 还依赖于自动转换;例如,如果你有一个 HorizontalPodAutoscaler,那么你可以使用 HorizontalPodAutoscaler API 的 v1 和 v2 版本的任意组合来与该对象进行交互。Kubernetes 负责转换每个 API 调用,以便客户端不会感知到实际序列化的是哪个版本。

对于集群管理员来说,理解对象存储版本是一个重要的概念,因为它将对象的 API 表示形式与存储后端中的实际编码关联起来。当对象的底层二进制编码变得重要时(例如在静态加密或 API 弃用方面),这一点尤为关键。

同一个 API 可以拥有多个 API 服务器随后可转换为对象模式(Schema)的存储版本。属于该资源的一个特定对象在任何时候都必须仅有一个存储版本。这意味着 API 服务器了解对象的二进制编码,并能够在所有已存储版本与对象的 API 表示形式之间进行动态转换。

对象的版本与存储版本完全不同。例如,同一资源在不同版本(如 v1alpha1v1beta1)的 API 对象,只要在两个对象之间未更新存储版本,它们在存储中的编码方式就相同。

存储版本到资源的映射

每个资源在任何时间点都会有一个活跃的存储版本,这意味着对对象的任何写入操作都将以该存储版本进行存储。然而,存储版本是可以更新的,这使得对象可以以不同的版本进行存储。但在任何给定时间,一个对象只会以一个存储版本存储。

从 API 服务器读取时,会将存储的数据转换为对象的 API 表示形式。这意味着只要不对对象进行更新,旧的存储版本就可以无限期存在。另一方面,在更新时,写入操作会将存储的对象转换为新的表示形式。

自定义资源的存储版本

自定义资源是动态定义的,因此其存储版本与内置的 Kubernetes 类型有所不同。内置对象通常拥有与 API 类型分开定义的存储编码,其中存储的对象充当中心,而资源的特定版本除了作为对象模式中的一个字段外,无关紧要。

然而,对于自定义资源,必须设置某个特定版本的资源作为存储版本。由该特定版本的自定义资源定义的模式将被用作存储层中该资源的编码。有关 API 设置和版本控制的更多详细信息,请参阅 CRD 高级功能集

例如,请参阅此 crontabs 的 CustomResourceDefinition:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
  - name: v1beta1
    # Each version can be enabled/disabled by Served flag.
    served: true
    # One and only one version must be marked as the storage version.
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
          time:
            type: string
  conversion:
    strategy: None
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

v1beta1 API 定义被用作存储版本,这意味着任何 crontabs 的更新或创建都将使用 v1beta1 API 的对象模式进行存储。在这种情况下,实际上意味着 v1 API 对象将永远无法存储 time 字段,因为它不属于存储定义。此模式在存储层中用作对象本身的二进制编码。尝试同时将两个版本设置为存储版本被视为无效,因为这意味着两个数据方案将被同时视为存储对象的有效方式。

在修改用于存储的版本后,该版本的 API 将用于存储任何新的或已更新的 CR。监视(Watch)或获取(Get)对象时,对象仍会被正常使用,只是会从旧的存储版本进行转换,而不会影响对象本身。只有更新或创建操作才会产生影响并使用新定义的存储版本。

存储版本与静态加密的关系

有一些工具可以用于对集群的静态存储进行加密,尤其是针对集群密钥(Secrets)。这为数据泄露提供了额外的保护层,因为集群中实际存储的数据是加密的。这意味着 API 服务器在从存储中检索数据时会对其进行解密。APIServer 必须拥有该存储版本的密钥,才能正确解码该对象。

在这种情况下,存储版本不仅仅是对象的二进制编码。只要存储的内容可以以某种方式转换为 API 对象,它就可以用作存储版本。

迁移到不同的存储版本

单个资源的多个存储版本可能会给集群管理员带来问题。集群管理员可能无法移除 CRD 的旧 API 版本(这些版本可能已不受支持),直到他们确定所有对象都不再使用与之关联的存储版本。面对大量的对象,且难以分辨哪些是新对象、哪些仍由旧存储版本支持,这使得很难判断何时可以安全移除一个版本。如果过早移除某个版本,可能意味着完全无法读取该对象。

另一个重要问题是上述章节中定义的加密密钥的使用。由于资源必须处于活跃使用状态才能更新存储版本,因此当执行密钥轮换时,旧的加密密钥和新的加密密钥必须保持使用状态,直到管理员确定所有对象都已至少被写入过一次。这既带来了安全风险,也带来了可用性问题,因为在此之前密钥无法完全停止使用。

请参阅存储版本迁移,了解如何运行迁移以确保所有对象在无需人工干预的情况下使用较新的存储版本的示例。


最后修改于 2026 年 4 月 5 日下午 2:45 PST: 修正文档中的拼写错误:limtations, storege, Althought (89a9a2d607)