ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Kubernetes CSI 迁移核心库 csi-translation-lib 解析:In-Tree 卷插件与 CSI 驱动之间的 PV 翻译机制

2026/9/8 22:47:41 拓冰建站 浏览量
Kubernetes CSI 迁移核心库 csi-translation-lib 解析:In-Tree 卷插件与 CSI 驱动之间的 PV 翻译机制 Kubernetes CSI 迁移核心库 csi-translation-lib 解析In-Tree 卷插件与 CSI 驱动之间的 PV 翻译机制【免费下载链接】kubernetesProduction-Grade Container Scheduling and Management项目地址: https://gitcode.com/GitHub_Trending/kuber/kubernetes导读本文围绕 Kubernetes 仓库中的staging/src/k8s.io/csi-translation-lib组件展开它是 Kubernetes 与各云厂商 Out-of-Tree CSI 组件如 external provisioner共同消费的翻译层当集群启用 CSI migration卷插件迁移后把仍以kubernetes.io/gce-pd、kubernetes.io/aws-ebs等 In-Tree 形态存在的 PV / StorageClass / 内联卷描述无损地翻译成对应 CSI 驱动的 API 对象让存量数据卷在不改动用户 YAML 的前提下平滑迁往 CSI 生态。读完本文你将掌握该库的定位、CSITranslator全部公开 API 的调用语义、7 大云厂商插件的注册与翻译细节、拓扑与访问模式的兼容性处理以及它在 Kubernetes 控制器与调度器中的真实消费路径。组件定位谁在生产中消费这套翻译函数staging/src/k8s.io/csi-translation-lib/README.md对其定位有非常明确的表述这个仓库提供一系列函数供 Kubernetes 各组件以及Out-of-Tree CSI 组件如 external provisioner使用目标是把 Kubernetes In-Tree 插件代码的迁移逻辑从插件仓库中提取出来、供双方共享。README 特别指出它的典型消费方式——外部 CSI 组件可以直接调用TranslateToCSI与TranslateToInTree系列函数来翻译 PV source。由于该库属于 Kubernetes 官方 staged repository外部仓库暂存区 机制它既以独立模块发布也被主仓库k8s.io/kubernetes通过 vendor 方式内部引用。在本仓库内真实的调用方包括卷迁移门面层 pkg/volume/csimigration/plugin_manager.go它包装了GetInTreePluginNameFromSpec、GetCSINameFromInTreeName等映射方法配合 CSIMigration 特性门控判断某插件的迁移是否已经完成PV 控制器与 attach/detach 控制器如 pkg/controller/volume/persistentvolume/pv_controller_base.go、pkg/controller/volume/attachdetach/attach_detach_controller.goKubelet 卷管理器 pkg/kubelet/volumemanager/volume_manager.go调度器的卷绑定插件 pkg/scheduler/framework/plugins/volumebinding/binder.go 与节点卷上限插件 pkg/scheduler/framework/plugins/nodevolumelimits/csi.go。这些调用方共享的是同一个入口对象——CSITranslator其核心实现位于 staging/src/k8s.io/csi-translation-lib/translate.go。核心 APICSITranslator 的翻译方法族CSITranslator是一个空结构体通过New()工厂函数创建实例translate.go#L44-L50type CSITranslator struct{} func New() CSITranslator { return CSITranslator{} }所有翻译逻辑都维护在一个包级注册表inTreePlugins中translate.go#L29-L39var inTreePlugins map[string]plugins.InTreePlugin{ plugins.GCEPDDriverName: plugins.NewGCEPersistentDiskCSITranslator(), plugins.AWSEBSDriverName: plugins.NewAWSElasticBlockStoreCSITranslator(), plugins.CinderDriverName: plugins.NewOpenStackCinderCSITranslator(), plugins.AzureDiskDriverName: plugins.NewAzureDiskCSITranslator(), plugins.AzureFileDriverName: plugins.NewAzureFileCSITranslator(), plugins.VSphereDriverName: plugins.NewvSphereCSITranslator(), plugins.PortworxDriverName: plugins.NewPortworxCSITranslator(), }map 的 key 是CSI 驱动名因此查找In-Tree 名 → CSI 名实际是遍历比较而CSI 名 → In-Tree 名是 O(1) 的 map 直接命中。下面逐个介绍四个核心翻译方法。1. TranslateInTreePVToCSIIn-Tree PV 源 → CSI 源这是最常用、也是 README 中TranslateToCSI所指的翻译入口translate.go#L96-L107。它接收一个*v1.PersistentVolume先做DeepCopy()再遍历注册表调用CanSupport(pv)找到能够识别该 PV 的插件交给插件自身的TranslateInTreePVToCSI处理func (CSITranslator) TranslateInTreePVToCSI(logger klog.Logger, pv *v1.PersistentVolume) (*v1.PersistentVolume, error) { if pv nil { return nil, errors.New(persistent volume was nil) } copiedPV : pv.DeepCopy() for _, curPlugin : range inTreePlugins { if curPlugin.CanSupport(copiedPV) { return curPlugin.TranslateInTreePVToCSI(logger, copiedPV) } } return nil, fmt.Errorf(could not find in-tree plugin translation logic for %#v, copiedPV.Name) }注意两点实现语义输入对象不被修改方法先DeepCopy()再翻译注释明确 The input persistent volume will not be modified插件接口层与门面层的语义差异接口方法TranslateInTreePVToCSI的注释是 The input persistent volume can be modifiedplugins/in_tree_volume.go#L46——门面层负责拷贝保护插件实现直接改写传入对象。从插件实现看如 plugins/gce_pd.go#L262-L263翻译后会清空pv.Spec.PersistentVolumeSource.GCEPersistentDisk并把CSI源填进去。以 GCE PD 为例翻译会做三件事plugins/gce_pd.go#L212-L267依据 PV 上的 zone 标签Beta 版failure-domain.beta.kubernetes.io/zone或 GA 版topology.kubernetes.io/zone多个 zone 用__分隔构造标准化的 CSI volume handle把partition、fstype、readOnly等字段映射进CSIPersistentVolumeSource调用translateTopologyFromInTreeToCSI把 NodeAffinity/Labels 中的旧拓扑键改写为 CSI 拓扑键topology.gke.io/zone。2. TranslateCSIPVToInTreeCSI 源 → In-Tree 源回滚兼容与上一方法互为逆操作用于 CSI 迁移发生问题需要回退、或迁移尚未在目标组件上完成时把 CSI PV 还原成 In-Tree PVtranslate.go#L112-L123func (CSITranslator) TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) { if pv nil || pv.Spec.CSI nil { return nil, errors.New(CSI persistent volume was nil) } copiedPV : pv.DeepCopy() for driverName, curPlugin : range inTreePlugins { if copiedPV.Spec.CSI.Driver driverName { return curPlugin.TranslateCSIPVToInTree(copiedPV) } } return nil, fmt.Errorf(could not find in-tree plugin translation logic for %s, copiedPV.Spec.CSI.Driver) }它的分派依据是pv.Spec.CSI.Driver字段——哪个驱动名命中注册表就用哪个插件做回翻。以 GCE PD 为例回翻时会从 volume handle 中解析出 PDName、还原Partition整数并把 CSI 拓扑键再改写回 Kubernetes 拓扑plugins/gce_pd.go#L271-L304。测试 translate_test.go#L49-L102 中的TestTranslationStability专门验证了PV → CSI → 再回 In-Tree往返后reflect.DeepEqual与原对象完全一致从测试层面保证了双向翻译的稳定性。3. TranslateInTreeInlineVolumeToCSI内联卷 → CSI PVPod 内的内联卷例如直接在 Pod spec 里写gcePersistentDisk:而非引用 PVC无法在 CSI 时代原样存在因此该库把它们包装成一个临时 PVtranslate.go#L67-L90。分派依据是接口中的CanSupportInline(volume)for _, curPlugin : range inTreePlugins { if curPlugin.CanSupportInline(volume) { pv, err : curPlugin.TranslateInTreeInlineVolumeToCSI(logger, volume, podNamespace) ... if pv.Spec.VolumeMode nil { volumeMode : v1.PersistentVolumeFilesystem pv.Spec.VolumeMode volumeMode } return pv, nil } }实现中有两处容易忽略的关键行为卷模式兜底内联卷只支持 Filesystem不支持 Block。由于 PV 默认初始化逻辑不覆盖内联卷场景若插件未显式设置VolumeMode门面层统一把它置为PersistentVolumeFilesystemtranslate.go#L77-L85。podNamespace参数只为 azurefile 服务注释说明该参数仅 azurefile 翻译器需要用于定位 secret 所在 namespace其他插件传空即可plugins/in_tree_volume.go#L41。以 GCE PD 内联卷翻译为例plugins/gce_pd.go#L166-L208它会构造一个名字形如pd.csi.storage.gke.io-diskName的 PV该名字作为 stage 路径的唯一区分部分把partition、fstype、readOnly搬到 CSI 源并按只读与否推导出ReadOnlyMany或ReadWriteOnce访问模式。4. TranslateInTreeStorageClassToCSIStorageClass 参数翻译对于动态供给场景external provisioner 拿到的是 StorageClass。该函数translate.go#L54-L62先 DeepCopy再按 In-Tree 插件名匹配并翻译例如把 In-Tree 时代的fstype、zone、zones等参数转换为 CSI 驱动认识的形态。每个插件的参数转换规则各有差异参数场景In-Tree 写法CSI 翻译后依据实现文件系统类型fstype: ext4csi.storage.k8s.io/fstype: ext4前缀参数由 external provisioner 剥离plugins/gce_pd.go#L88-L90单 zone 约束zone: us-central1-a转成AllowedTopologies键为 CSI 拓扑键plugins/aws_ebs.go#L70-L71多 zone 约束zones: a,b拆分后转成AllowedTopologies的多个值plugins/gce_pd.go#L94-L95EBS 特有iopspergb保留原参数并追加allowautoiopspergbincrease: true以保持 In-Tree 行为plugins/aws_ebs.go#L74-L79约束检查如果AllowedTopologies与zone/zones参数同时出现翻译器直接报错cannot simultaneously set allowed topologies and zone/zones parameters避免语义冲突若只存在旧的AllowedTopologies则通过translateAllowedTopologiesplugins/in_tree_volume.go#L310-L336把其中failure-domain.beta.kubernetes.io/zone/topology.kubernetes.io/zone条目改写成 CSI 拓扑键其他拓扑原样透传。辅助判定 API迁移能力探测与名称双向映射除了四个翻译方法README 强调的判断翻译逻辑是否存在、建立 in-tree 与 CSI 的双向映射由下列方法完成均在 translate.go 中方法作用分派依据IsMigratableIntreePluginByName(name)给定 In-Tree 插件名判断是否有对应迁移逻辑遍历比较GetInTreePluginName()L127-L134IsMigratedCSIDriverByName(name)给定 CSI 驱动名判断它是否为已迁移的 In-Tree 插件的替代者直接查 map keyL138-L143GetInTreePluginNameFromSpec(pv, vol)从 PV 或内联卷 spec 反查 In-Tree 插件名CanSupport/CanSupportInlineL146-L164GetCSINameFromInTreeName(name)In-Tree 插件名 → CSI 驱动名遍历 mapL168-L175GetInTreeNameFromCSIName(name)CSI 驱动名 → In-Tree 插件名map 命中后返回GetInTreePluginName()L179-L184IsPVMigratable(pv)给定 PV 是否可迁移CanSupportL187-L194IsInlineMigratable(vol)给定内联卷是否可迁移CanSupportInlineL197-L204RepairVolumeHandle(driverName, volumeHandle, nodeID)依据节点 ID 修复缺失 project/zone 信息的 volume handle按 driverName 命中插件L207-L212这些映射方法被仓库内卷迁移门面复用。例如 pkg/volume/csimigration/plugin_manager.go#L29-L46 定义了PluginNameMapper接口仅要求GetInTreePluginNameFromSpec与GetCSINameFromInTreeNamePluginManager组合它并据此判断某插件的迁移是否完整完成——需要同时开启 CSIMigration 特性门控与对应的 InTreePluginUnregister 门控。插件注册表全景7 大 In-Tree → CSI 转换器每个云厂商插件都以独立的plugins/*.go文件存在并实现统一的InTreePlugin接口。当前注册的驱动与文件路径如下云平台In-Tree 插件名CSI 驱动名CSI 拓扑键源码位置GCE PDkubernetes.io/gce-pdpd.csi.storage.gke.iotopology.gke.io/zoneplugins/gce_pd.goAWS EBSkubernetes.io/aws-ebsebs.csi.aws.comtopology.ebs.csi.aws.com/zoneplugins/aws_ebs.goAzure Diskkubernetes.io/azure-diskdisk.csi.azure.comtopology.disk.csi.azure.com/zoneplugins/azure_disk.goAzure Filekubernetes.io/azure-filefile.csi.azure.com不涉及 zone 拓扑共享文件卷plugins/azure_file.goOpenStack Cinderkubernetes.io/cindercinder.csi.openstack.orgtopology.cinder.csi.openstack.org/zoneplugins/openstack_cinder.govSpherekubernetes.io/vsphere-volumecsi.vsphere.vmware.comtopology.csi.vmware.com/zone另有 region 键plugins/vsphere_volume.goPortworxkubernetes.io/portworx-volumepxd.portworx.com—plugins/portworx.go其中部分插件如 azurefile因为 PV 形态特殊还单独维护了 volume handle 编解码格式azurefile 用#分隔 shareName、secret 等字段见 plugins/azure_file.go#L39-L51。InTreePlugin 接口每个转换器必须实现的契约plugins/in_tree_volume.go#L31-L69 定义了每个插件翻译器都必须满足的接口type InTreePlugin interface { TranslateInTreeStorageClassToCSI(logger klog.Logger, sc *storage.StorageClass) (*storage.StorageClass, error) TranslateInTreeInlineVolumeToCSI(logger klog.Logger, volume *v1.Volume, podNamespace string) (*v1.PersistentVolume, error) TranslateInTreePVToCSI(logger klog.Logger, pv *v1.PersistentVolume) (*v1.PersistentVolume, error) TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) CanSupport(pv *v1.PersistentVolume) bool CanSupportInline(vol *v1.Volume) bool GetInTreePluginName() string GetCSIPluginName() string RepairVolumeHandle(volumeHandle, nodeID string) (string, error) }各翻译器结构体通常以编译期断言保证契约完整实现例如 GCE PD 与 AWS EBS 都写有var _ InTreePlugin gcePersistentDiskCSITranslator{}plugins/gce_pd.go#L57、var _ InTreePlugin awsElasticBlockStoreCSITranslator{}plugins/aws_ebs.go#L50。接口中的CanSupport/CanSupportInline决定了门面层的分派结果例如 GCE 实现仅判断pv.Spec.GCEPersistentDisk ! nilplugins/gce_pd.go#L309-L318。翻译中的兼容性细节访问模式、拓扑键与 region 推导从源码看这套翻译并不是简单的字段搬家其中埋了多处在 In-Tree 时代宽松、不校验而 CSI 驱动严格、会报错的兼容处理理解它们对排查迁移期问题至关重要。访问模式向后兼容GCE PD 的 In-Tree 实现从不校验ReadWriteMany——用户即使声明了它底层也只是按单节点读写挂载。但 CSI driver 会严格拒绝ReadWriteMany。因此backwardCompatibleAccessModesplugins/gce_pd.go#L130-L162把所有ReadWriteMany收敛为ReadWriteOnce并把[ReadWriteOnce, ReadOnlyMany]这种单盘不可同时满足的组合也降级为ReadWriteOnce确保旧卷迁移后仍可工作。拓扑标签 Beta → GA 升级translateTopologyFromInTreeToCSIplugins/in_tree_volume.go#L185-L214会把 PV 上存在的failure-domain.beta.kubernetes.io/zone等 Beta 标签一并改写为 GA 的topology.kubernetes.io/zone并优先消费 NodeAffinity 中的拓扑若 NodeAffinity 与 PV Labels 同时存在NodeAffinity 优先。getTopologyLabelin_tree_volume.go#L225-L241按NodeAffinity GA → NodeAffinity Beta → Labels GA → Labels Beta的顺序判定当前使用的拓扑键代际。CSI → In-Tree 回翻与 region 推导translateTopologyFromCSIToInTreeplugins/in_tree_volume.go#L275-L308把 CSI 拓扑改写回 Kubernetes zone 标签并通过可选的regionParserFn由每个云厂商自行实现zone → region推导例如 GCE 要求 zone 形如{locale}-{region}-{zone}三段式见 plugins/gce_pd.go#L383-L400。若某个插件存在多个拓扑键如 vSphere 同时有 zone 与 region 键接口注释明确指出其需要单独处理不能复用通用路径in_tree_volume.go#L262-L267。volume handle 修复RepairVolumeHandle 的实战价值CSI 迁移早期部分存量卷的 volume handle 缺失 project / region 等前缀信息因为 In-Tree 时代的卷 ID 更短。RepairVolumeHandle通过传入的 nodeID 补齐这些字段。以 GCE PD 为例plugins/gce_pd.go#L333-L371其 volume handle 期望格式为zonalprojects/{project}/zones/{zone}/disks/{disk}regionalprojects/{project}/regions/{region}/disks/{disk}实现会按/切分字符串并校验元素个数少于 6 段即报错当 project 段为UNSPECIFIED时从同样结构的 nodeID 中提取 project 与 zoneregional 卷则再把 zone 反推为 region最终重写出完整 handle。测试文件 plugins/gce_pd_test.go、plugins/aws_ebs_test.go 等对这类路径均有覆盖。如何在自己的组件中接入这套翻译逻辑对希望编写 Out-of-Tree CSI 控制器如 external provisioner、卷修复控制器的开发者接入方式非常直接——把k8s.io/csi-translation-lib作为依赖引入然后用csitranslation.New()创建翻译器实例拿到底层对象前先做能力探测例如IsPVMigratable(pv)、IsMigratableIntreePluginByName(kubernetes.io/gce-pd)避免对未知对象直接翻译返回 error需要正向迁移时调用TranslateInTreePVToCSI/TranslateInTreeStorageClassToCSI输出 CSI 形态对象需要回退时调用TranslateCSIPVToInTree遇到 volume handle 不完整时调用RepairVolumeHandle(driverName, volumeHandle, nodeID)补齐。仓库内控制器层的既有实现可作为接入范例例如 attach/detach 控制器与 kubelet 卷管理器在启用迁移后先通过翻译把 In-Tree spec 转成 CSI spec 再走 CSI 附加/挂载流程其反向翻译则在特性门控回退场景下兜底。翻译前后的幂等与稳定性由测试保障——除前文TestTranslationStability外plugins/gce_pd_test.go、plugins/aws_ebs_test.go 等还各自覆盖了 zonal/regional 卷 ID 解析、内联卷翻译、StorageClass 参数冲突等边界用例可以作为自定义插件翻译器行为的参照。社区、讨论、贡献与支持该组件由 Kubernetes SIG-Storage 维护。本仓库的 README 特别说明它是一个自动发布的 staged 仓库问题与 PR 都应提交到主仓库 kubernetes/kubernetes见 staging/README.md本目录只读用于导入不接受直接贡献。可以参与讨论的渠道包括Slack#sig-storagekubernetes.slack.com邮件列表kubernetes-sig-storagegoogle groups仓库内同样放置了 code-of-conduct.md 与 CONTRIBUTING.md参与者需遵守 Kubernetes 社区行为准则与贡献规范。小结csi-translation-lib是整个 Kubernetes In-Tree 卷插件向 CSI 迁移链条上的翻译枢纽对外它向 external provisioner 等 Out-of-Tree 组件开放一组稳定的TranslateToCSI/TranslateToInTree函数对内它与 pkg/volume/csimigration 及各类控制器、调度器深度耦合。理解其注册表结构7 大插件的双向映射、接口契约InTreePlugin十方法、门面层语义DeepCopy 保护、内联卷 Filesystem 兜底以及兼容性细节访问模式收敛、Beta/GA 拓扑改写、volume handle 修复就能在迁移排障、自定义 CSI 控制器接入或为新的 In-Tree 插件编写翻译器时准确判断谁在什么条件下把什么对象翻译成了什么这也是该库代码与测试最能帮助到你的地方。【免费下载链接】kubernetesProduction-Grade Container Scheduling and Management项目地址: https://gitcode.com/GitHub_Trending/kuber/kubernetes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考