1. 项目概述与背景如果你和我一样在Kubernetes生态里摸爬滚打了几年那你一定对Helm这个“包管理器”又爱又恨。爱的是它用声明式的Chart把复杂的应用部署变得像helm install一样简单恨的是版本升级带来的“阵痛”尤其是从Helm 2到Helm 3的跨越。这不仅仅是客户端版本号的跳动更是一次底层架构的“心脏移植手术”——Helm 3彻底移除了服务端的Tiller组件将发布状态直接存储在了Kubernetes集群内默认是Secrets这意味着所有在Helm 2时代管理的成百上千个Release其元数据和历史记录都面临一次“大迁徙”。helm-2to3插件就是在这个背景下诞生的官方迁移工具。它的核心任务非常明确安全、准确地将Helm 2的配置、插件、仓库以及最重要的——所有已部署的Release——原封不动地“搬运”到Helm 3的管理体系下。想象一下你有一个运行了两年、承载了数十个微服务的生产集群每个服务都有多个版本和回滚历史。手动迁移那无异于一场灾难。2to3插件就是那个帮你自动化完成这场数据迁移的“摆渡人”。虽然项目现已归档并不再维护因为Helm 2早在2020年11月就已停止支持但理解它的工作原理和最佳实践对于任何仍在处理遗留系统迁移或希望深入理解Helm内部数据结构的运维和开发人员来说依然是一笔宝贵的财富。这篇文章我将结合自己多次执行大规模迁移的经验为你彻底拆解helm-2to3不仅告诉你每个命令怎么用更会深入解释它背后的逻辑、可能遇到的坑以及如何设计一个万无一失的迁移方案。2. Helm 2与Helm 3的核心架构差异解析在动手迁移之前我们必须先搞清楚我们在迁移什么以及为什么要迁移。这不仅仅是版本号的改变而是整个设计哲学的演进。2.1 Helm 2的“客户端-服务器”架构Helm 2采用经典的C/S架构客户端 (helm): 用户直接交互的命令行工具负责解析Chart、生成Kubernetes清单。服务器端 (Tiller): 部署在Kubernetes集群kube-system命名空间默认中的一个Pod。它接收来自客户端的指令并负责与Kubernetes API Server交互执行实际的资源创建、更新、删除操作。存储Release的元数据和历史版本。默认情况下这些数据以ConfigMap的形式存储在Tiller所在的命名空间中。每个Release的每次操作安装、升级、回滚都会生成一个新的ConfigMap命名格式类似release_name.version_number。这种架构带来了几个显著问题安全模型复杂Tiller在集群内拥有很高的权限通常是cluster-admin才能部署应用到任何命名空间。这导致授权变得困难需要精细的RBAC配置。状态存储耦合Release状态与一个特定的PodTiller强关联。Tiller的故障或数据丢失会直接影响Helm对已部署应用的管理能力。多租户隔离差所有用户共享同一个Tiller实例通过Tiller的权限来隔离这在实践中很难管理。2.2 Helm 3的“无服务器”架构Helm 3做出了一个大胆而优雅的简化彻底移除了Tiller。客户端 (helm): 现在helm命令行工具直接与Kubernetes API Server通信利用用户自身的kubeconfig凭证和RBAC权限来执行操作。这完美契合了Kubernetes的安全模型。状态存储Release的元数据和历史版本现在直接作为Kubernetes资源对象存储在与Release相同的命名空间里。默认存储后端是Secrets资源类型为helm.sh/release.v1这使得Release状态成为了集群原生的一部分具有了高可用和易备份的特性。这种转变带来了根本性的优势简化的安全模型权限控制完全依赖Kubernetes RBAC与kubectl无异。更强的隔离性Release状态按命名空间存储天然支持多租户。更少的运维负担无需再部署、维护、升级和故障排查Tiller服务。2.3 迁移的本质数据格式转换与存储位置转移理解了架构差异迁移的本质就清晰了。helm-2to3插件需要完成两件核心工作数据格式转换将Helm 2 Tiller存储的Release数据特定格式的ConfigMap/Secret解析出来并重新打包成Helm 3能够识别的、存储在Secrets中的新格式。存储位置转移将转换后的Release数据从Tiller所在的命名空间如kube-system写入到该Release实际部署的应用命名空间中。这个过程必须保证信息的无损包括Release名称、命名空间、Chart信息、Chart值values、版本号、状态deployed, failed等、描述、注释以及完整的版本历史。2to3插件正是为精确完成这个复杂过程而设计的。3. 迁移前的关键准备与风险评估迁移绝不是简单地运行几条命令。在按下回车键之前周密的准备是成功的一半也是避免生产事故的底线。3.1 环境与版本检查清单首先确保你的环境处于一个已知的、稳定的状态备份备份还是备份这是铁律。Helm v2 Home目录通常位于~/.helm或由$HELM_HOME指定。完整备份这个目录。集群内的Release数据执行kubectl get configmaps -n tiller-namespace -l OWNERTILLER -o yaml helm2-releases-backup.yaml如果使用Secret存储则将configmaps替换为secrets。这备份了所有Release的原始数据。集群资源快照对关键业务应用所在的命名空间使用kubectl get all -n namespace -o yaml进行备份。升级到最终版本将Helm 2客户端升级到2.17.0最后一个2.x版本。这个版本与Helm 3的兼容性和稳定性最好。安装一个较新且稳定的Helm 3版本例如v3.12.0或更高。避免使用早期有已知问题的v3.0.x版本。检查Kubernetes API兼容性Helm 2中使用的某些Kubernetes资源API版本可能在新的集群中已被废弃或移除。使用helm mapkubeapis插件一个社区插件可以自动检测并修复这些问题。在迁移前运行它能避免迁移后应用无法部署的尴尬。验证集群访问权限确保用于执行迁移的kubeconfig上下文Context对Tiller命名空间如kube-system和目标应用命名空间拥有足够的读写权限。插件需要能读取ConfigMaps/Secrets并创建新的Secrets。3.2 设计迁移策略分批、灰度与回滚方案对于拥有大量Release的生产环境全量一次性迁移风险极高。我推荐采用分批次、灰度的策略按业务重要性分级将Release分为三批P0低风险/测试环境首先迁移开发、测试环境的Release。这些环境可以承受故障用于验证迁移流程和脚本。P1非核心生产服务其次迁移那些对业务连续性影响较小的生产服务例如内部的监控Agent、日志收集器。P2核心生产服务最后在积累了足够信心后在业务低峰期迁移核心数据库、前端应用等。制定清晰的回滚流程回滚到Helm 2如果迁移后发现问题立即停止向Helm 3操作。由于2to3默认不会删除Helm 2的数据除非使用--delete-v2-releases你可以直接使用Helm 2客户端进行回滚或升级操作。这是最重要的安全网。在Helm 3中回滚迁移成功后在Helm 3中测试helm rollback命令确保版本历史可用。沟通与时间窗口通知相关团队开发、测试、SRE迁移计划明确时间窗口和预期影响通常是无感知的。3.3 安装与验证2to3插件安装过程很简单但要注意细节# 使用Helm 3的插件管理器安装 helm plugin install https://github.com/helm/helm-2to3.git安装后验证插件是否可用helm 2to3 --help你应该能看到move configconvertcleanup等子命令的帮助信息。重要提示插件会从GitHub Releases下载二进制文件。如果你的机器无法直接访问GitHub需要预先下载对应版本的tar包然后通过helm plugin install /path/to/local/helm-2to3.tar.gz方式安装。4. 分步迁移实战详解准备工作就绪现在我们开始实战。请严格按照以下顺序操作。4.1 第一步迁移Helm v2配置move config这个步骤迁移的是客户端本地的配置不涉及集群。helm 2to3 move config这条命令会做以下几件事复制仓库配置将Helm 2的~/.helm/repository/repositories.yaml复制到Helm 3的~/.config/helm/repositories.yaml。这样你在Helm 2中添加的稳定仓库stable、比特纳米bitnami等远程仓库地址就会出现在Helm 3中。复制插件将Helm 2的~/.helm/plugins目录内容复制到Helm 3的~/.local/share/helm/plugins。注意不是所有Helm 2插件都能在Helm 3上工作因为插件API可能发生了变化。复制Starters如果你用过helm create --starter相关的starter模板也会被复制。执行后的关键检查与操作运行helm repo list确认仓库列表已迁移。你会发现所有本地仓库如local虽然出现在列表里但指向的仍然是Helm 2的本地路径如http://127.0.0.1:8879而这个服务通常由helm serve命令启动在Helm 3中可能未运行。# 移除无效的本地仓库引用 helm repo remove local # 如果需要用Helm 3的方式重新添加本地仓库如果需要的话但Helm 3更推荐使用OCI仓库或直接指定chart路径 # helm repo add my-local http://localhost:8879 前提是你要启动一个本地chart服务器更新仓库索引执行helm repo update。这个命令会连接所有远程仓库下载最新的index.yaml文件到Helm 3的缓存目录替换掉从Helm 2带来的可能过时的缓存。测试插件逐个测试迁移过来的插件是否工作。例如如果你有helm-diff插件运行helm diff upgrade ...看看是否报错。如果插件失效需要手动卸载并重新安装其Helm 3兼容版本。helm plugin uninstall plugin-name # 查找该插件支持Helm 3的安装方式重新安装 helm plugin install new-plugin-url4.2 第二步迁移Helm v2 Releaseconvert这是迁移的核心和最具风险的部分。强烈建议对每一个Release先使用--dry-run进行模拟。4.2.1 单Release迁移基本命令格式如下helm 2to3 convert RELEASE_NAME --tiller-ns kube-system --release-storage configmapsRELEASE_NAME: Helm 2中部署的Release名称。--tiller-ns: Tiller所在的命名空间默认为kube-system。如果你的环境不同必须指定。--release-storage: Helm 2存储Release数据的资源类型。这是最容易出错的地方Helm 2默认使用ConfigMaps但可以通过--override spec.tiller.storagesecret或在helm init时指定--tiller-storagesecret来改为Secrets。你必须知道你的环境用的是哪一种。可以通过以下命令检查# 检查kube-system命名空间下是否有OWNERTILLER标签的ConfigMaps kubectl get configmaps -n kube-system -l OWNERTILLER # 检查kube-system命名空间下是否有OWNERTILLER标签的Secrets kubectl get secrets -n kube-system -l OWNERTILLER哪个命令有输出就说明用的是哪种存储。一个完整的、带有安全检查的实操流程# 1. 首先列出所有待迁移的Release通过Helm 2 helm list --all --tiller-namespace kube-system # 2. 选择一个非关键的Release进行试迁移使用--dry-run预览 helm 2to3 convert my-test-app --tiller-ns kube-system --release-storage configmaps --dry-run # 仔细阅读输出确认它计划读取哪个ConfigMap并转换到哪个命名空间的哪个Secret。 # 3. 实际执行迁移 helm 2to3 convert my-test-app --tiller-ns kube-system --release-storage configmaps # 4. 迁移后立即用Helm 3验证 helm list -n namespace-of-my-test-app # 确认Release出现 helm history my-test-app -n namespace-of-my-test-app # 确认版本历史完整 helm get values my-test-app -n namespace-of-my-test-app # 确认配置值正确4.2.2 批量迁移与高级参数对于成百上千的Release手动一个个转换是不现实的。我们可以结合kubectl和xargs进行批量操作但务必谨慎。示例批量迁移所有使用ConfigMap存储的Release# 获取所有唯一的Release名称然后逐个转换 kubectl get configmap -n kube-system -l OWNERTILLER \ | awk NR1 {print $1} | cut -d . -f1 | sort -u \ | xargs -I {} -n1 -P1 helm 2to3 convert {} --tiller-ns kube-system --release-storage configmaps命令拆解与风险控制kubectl get ...: 获取所有Tiller的ConfigMap。awk NR1 {print $1}: 跳过表头打印第一列ConfigMap名称。cut -d . -f1: 按.分割取第一部分即Release名称因为CM名格式是release_name.version。sort -u: 排序并去重得到唯一的Release名称列表。xargs -I {} -n1 -P1 helm 2to3 convert {} ...:-I {}: 用{}代替传入的参数。-n1: 每次只传递一个Release名给helm 2to3命令。-P1:至关重要设置进程数为1即串行执行。迁移是I/O和网络密集型操作并行执行可能导致集群API Server过载或意外错误。务必串行。在命令末尾可以加上--dry-run先进行整体预览确认无误后再移除。关键参数解析--release-versions-max: 默认只迁移每个Release最新的10个版本。如果你的Release有超过10个历史版本且你需要更早的历史记录可以增大这个值。设为0表示迁移所有版本。迁移大量历史版本会显著增加时间和资源消耗。--delete-v2-releases:危险参数如果加上迁移成功后会自动删除Helm 2对应的ConfigMap/Secret。在最终确认所有迁移完全成功并稳定运行前绝对不要使用这个参数保留Helm 2的数据是你的终极回滚保障。--ignore-already-migrated: 如果一个Release已经部分迁移过比如之前运行过convert再次运行时会报错。此参数可忽略已迁移的版本继续迁移新版本。4.3 第三步清理Helm v2数据cleanup警告这是不可逆的操作只有在所有集群、所有Tiller实例下的Release都已成功迁移到Helm 3并且经过充分验证至少一个完整的发布周期后才能执行清理。清理分为三个部分可以单独执行--config-cleanup: 删除本地的Helm 2配置目录~/.helm。--release-cleanup: 删除集群中Tiller存储的Release数据ConfigMaps/Secrets。--tiller-cleanup: 删除Tiller的Deployment、Service Account等集群资源。最安全的清理方式是分步、带确认地进行# 1. 首先清理集群中的Tiller部署这不会影响已迁移的Release因为Helm 3不依赖它 helm 2to3 cleanup --tiller-cleanup --tiller-ns kube-system # 命令会交互式询问你是否确认仔细阅读提示。 # 2. 然后清理集群中的Helm 2 Release数据。执行前请再次用Helm 3核对所有Release。 helm list --all-namespaces # Helm 3视图 helm list --all --tiller-namespace kube-system # Helm 2视图应该为空或只剩你确定要废弃的Release # 确认无误后清理 helm 2to3 cleanup --release-cleanup --tiller-ns kube-system --release-storage configmaps # 3. 最后清理本地配置。这步风险最低可以在任何时候做。 helm 2to3 cleanup --config-cleanup关于--name参数它用于清理单个指定Release的v2数据。例如你迁移了app-A和app-B但只想清理app-A在Helm 2中的数据可以helm 2to3 cleanup --name app-A --tiller-ns kube-system --release-storage configmaps这个操作是独立的不能与其他--*-cleanup标志同时使用。5. 疑难杂症与深度排错指南即使准备再充分在生产环境中也难免遇到问题。下面是我在多次迁移中总结的常见“坑”及其解决方案。5.1 错误“release not found” 或 “configmaps “xxx” not found”问题现象运行convert时提示找不到Release或对应的ConfigMap/Secret。排查思路确认Tiller命名空间你是否使用了正确的--tiller-ns有些环境可能将Tiller部署在tiller-system或其他自定义命名空间。用kubectl get pods -A | grep tiller查找。确认存储类型再次用kubectl get configmaps/secrets -n tiller-namespace -l OWNERTILLER确认存储类型。确保--release-storage参数与之匹配。检查RBAC权限执行迁移的用户是否对Tiller命名空间有get和listConfigMaps/Secrets的权限以及对目标命名空间有createSecrets的权限可以临时绑定一个cluster-admin角色测试或参考前文提供的ClusterRole片段。Release名称是否包含特殊字符或版本号helm list显示的名称才是真正的Release名。确保在convert命令中使用的是纯名称不包含版本后缀。5.2 错误“failed to convert release: existing release conflict”问题现象迁移时提示在Helm 3中已存在同名的Release。原因分析这通常是因为目标命名空间中已经存在一个由Helm 3直接创建的、同名的Secret存储了Helm 3的Release。或者你已经对这个Release执行过部分迁移。解决方案检查冲突kubectl get secrets -n target-namespace -l “ownerhelm”查看是否已存在。决策如果这个Helm 3的Release是旧的、可丢弃的可以先helm uninstall它然后再执行迁移。如果这个Helm 3的Release就是由之前失败的迁移产生的半成品并且你需要保留Helm 2的数据可以手动删除这个冲突的Secretkubectl delete secret -n namespace sh.helm.release.v1.release_name.vversion。操作前务必确认该Secret的内容使用--ignore-already-migrated标志跳过已迁移的版本如果错误信息是关于版本冲突。5.3 迁移后Helm 3命令失败如helm upgrade,helm repo update问题现象配置迁移后执行helm repo update或helm upgrade时出现连接错误或缓存错误。根因分析这几乎总是因为本地仓库缓存冲突。move config复制了Helm 2的repositories.yaml其中可能包含指向Helm 2本地缓存文件如~/.helm/cache下的索引引用而这些路径对Helm 3无效。标准解决流程# 1. 查看当前仓库列表找出所有‘local’或路径看起来异常的仓库 helm repo list # 2. 移除所有本地仓库或无效仓库 helm repo remove local # 移除其他可能出问题的仓库 # 3. 重新添加必要的远程仓库使用Helm 3的格式 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add stable https://charts.helm.sh/stable # 注意稳定仓库已归档可能需要添加其他替代源 # 4. 强制更新仓库索引重建缓存 helm repo update完成以上步骤后Chart依赖更新和升级命令应该可以正常工作。5.4 Tillerless Helm 2环境的迁移有些环境出于安全考虑使用“Tillerless”模式运行Helm 2即通过helm tiller run或类似插件在本地运行Tiller进程而非部署在集群中。这种情况下Release数据可能存储在本地文件或通过不同方式存在于集群。关键点对于Tillerless环境2to3插件可能无法直接定位到Release数据。你需要明确你的Tillerless方案是如何存储Release状态的是本地文件还是通过特定服务账户写入集群的Secrets。如果数据在集群中尝试使用--tiller-out-cluster标志并结合--release-storage指定存储类型。你可能需要为插件配置一个能访问这些数据的Kubernetes服务账户。如果数据在本地文件迁移可能更复杂需要手动解析文件并尝试通过Helm 3的API重新创建Release。这种情况建议查阅特定Tillerless插件的文档或寻求社区帮助。6. 迁移后的验证与最佳实践迁移完成并清理旧数据后工作并未结束。你需要建立对新体系的信心。6.1 系统性验证清单清单完整性检查在Helm 3中运行helm list --all-namespaces对比迁移前的Helm 2列表确保数量、名称、命名空间一致。随机抽样多个关键应用使用helm get manifest release -n namespace与Helm 2时代helm get manifest release --tiller-namespace kube-system的输出进行对比如果你有备份确保生成的Kubernetes资源模板一致。版本历史与回滚测试对抽样应用执行helm history release -n namespace确认所有历史版本包括FAILED状态的都已迁移。执行一次真实的回滚测试选择一个有多个历史版本的非核心应用记录当前版本号执行helm rollback release previous_version -n namespace观察应用是否按预期回滚。成功后再将其升级回原版本。应用功能冒烟测试访问应用的端点Endpoint执行核心业务流程确保服务功能完全正常。检查相关的监控图表、日志流确认没有因迁移引入的异常错误或性能抖动。Helm 3新功能验证测试helm diff upgrade需安装插件查看升级差异。测试helm uninstall --keep-history和helm rollback到已卸载的版本。验证依赖库如Bitnami的Chart是否能在Helm 3下正常安装升级。6.2 长期维护最佳实践统一客户端版本在所有CI/CD流水线、开发者机器上强制使用Helm 3并淘汰Helm 2二进制文件。更新文档与脚本将内部Wiki、部署手册、Shell脚本、Dockerfile中所有helm init、--tiller-namespace等Helm 2特有的命令或参数更新为Helm 3的等效形式。利用Helm 3新特性依赖管理Helm 3使用Chart.lock文件锁定依赖版本使部署更一致。Library Charts创建可重用的通用组件Chart。OCI注册中心支持考虑将Chart推送至OCI兼容的注册中心如Harbor、ACR、ECR实现更标准化的分发。制定新的备份策略Helm 3的Release状态存储在Secrets中。确保你的集群备份方案如Velero覆盖了所有业务命名空间以便在灾难恢复时能连同Release历史一起还原。迁移到Helm 3不是一个简单的工具升级而是一次基础设施管理的现代化。虽然helm-2to3插件已经完成了它的历史使命但通过这次迁移所获得的关于Helm内部状态管理的深刻理解会让你在未来的云原生旅程中更加从容。记住谨慎的计划、充分的备份和循序渐进的执行是应对任何复杂系统变更的不二法门。