Terraform vs Helm
云资源层 vs 集群内应用层。
Terraform vs Helm
这一课你将能做到
- 说清楚 Terraform 和 Helm 各自管理的对象、抽象层级和变更模型
- 在 Terraform 中通过 helm_release 引导安装 chart,并划清后续升级归谁
- 面对一套真实架构,能给出「哪些东西给 Terraform、哪些给 Helm/GitOps」的分工方案
为什么要学
这是云原生岗位面试和实际项目设计中反复出现的问题。两个工具都能往 Kubernetes 集群里「创建东西」,初学者很容易困惑:既然 Terraform 有 Helm Provider,是不是所有 chart 都 Terraform 一把梭?或者既然 Helm 能装一切,还要 Terraform 干什么?答错边界,轻则发布流程别扭、升级靠人肉,重则两套系统同时操作同一批对象,状态互相打架。这一课把职责边界讲死,并给出混合架构的标准接线。
人话:Terraform 是管「地基和大楼结构」的,一次改动要稳、要审批、频率低;Helm 是管「楼里的家具和租户配置」的,应用每周发版都用它。让地基施工队每天帮你挪沙发,双方都难受。
先建立图像
对比表:
| 维度 | Terraform | Helm |
|---|
| 管理对象 | 云资源、集群、集群级对象 | K8s 应用的一组 YAML(chart 包) |
| 抽象 | 资源 + 依赖图 + state | 模板 + values 参数 + release 版本 |
| 变更频率 | 低(基础设施周/月级) | 高(应用日/周级发版) |
| 回滚 | 反向 plan(destroy/recreate) | helm rollback 秒级切版本 |
| 状态存放 | state(本地/S3) | Secret/ConfigMap(集群内 release 记录) |
| 最擅长 | 「存在性」与生命周期 | 「安装、升级、回滚」应用包 |
推荐的三层分工:
Terraform 云网络 + EKS 集群 + 节点组 + 集群插件底座
Helm(经 Terraform 引导或独立) 平台组件:ingress-nginx、cert-manager、metrics-server
Helm / GitOps 业务应用:Spring Boot 各服务(高频升级走这里)
跟着做
步骤 1:声明两个 provider
versions.tf:
terraform {
required_version = ">= 1.7.0"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.25"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.12"
}
}
}
providers.tf:
provider "kubernetes" {
config_path = "${pathexpand("~/.kube/config")}"
}
provider "helm" {
kubernetes {
config_path = "${pathexpand("~/.kube/config")}"
}
}
执行 terraform init。
步骤 2:用 helm_release 安装平台组件
main.tf:
resource "kubernetes_namespace" "ingress" {
metadata {
name = "ingress-nginx"
}
}
resource "helm_release" "ingress_nginx" {
name = "ingress-nginx"
repository = "https://kubernetes.github.io/ingress-nginx"
chart = "ingress-nginx"
version = "4.10.0"
namespace = kubernetes_namespace.ingress.metadata[0].name
values = [
<<-EOT
controller:
service:
type: ClusterIP
replicaCount: 2
EOT
]
}
要点:version 钉死 chart 版本;values 内嵌覆盖项,也可以 values_file 引用文件。apply 后等价于 helm install,集群内产生一个名为 ingress-nginx 的 release。
步骤 3:验证与日常操作
terraform plan
helm list -A
helm status ingress-nginx
如果之后升级 chart 版本,改 version 重新 apply,等价于 helm upgrade;但要注意:一旦你用 Terraform 接管了某个 release,就别再手工 helm upgrade 它——下次 apply Terraform 会把它改回去。一个 release 只能有一个权威管理者。
步骤 4:本地 chart 和私有仓库
resource "helm_release" "app" {
name = "springboot-app"
chart = "${path.module}/charts/springboot-app"
namespace = "springboot"
create_namespace = true
}
resource "helm_release" "private_app" {
name = "private-app"
repository = "https://charts.example.com"
chart = "springboot-app"
version = "1.2.0"
repository_username = "ci"
repository_password = "走环境变量,勿写死"
}
步骤 5:更主流的形态——Terraform 只引导,应用交给 GitOps
生产上业务应用高频发版,每次都改 Terraform 仓库再 apply 太慢。通行做法:
Terraform:装 ArgoCD(一个 helm_release)+ 创建 Application 的引导
ArgoCD :监听应用 Git 仓库,values 一变自动 helm upgrade(GitOps)
应用团队 :只改自己仓库的 values/image tag,不碰 Terraform
Terraform 管的对象收敛为「低频、强生命周期绑定」的东西;高频变更全部移出。这条边界一旦划清,两个团队的心智负担都小。
步骤 6:销毁的注意点
terraform destroy -target=helm_release.ingress_nginx
删除 helm_release 等价于 helm uninstall,其管理的对象随之删除;namespace 如果也是 Terraform 创建的,destroy 会一并删除。确认 values 里没有 helm.sh/resource-policy: keep 的对象需要保留。
讲清楚要点
- 按「变更频率」和「生命周期」划边界,而不是按技术上能不能做:两者能力重叠区很大,但工程边界让协作清晰。
- chart 是打包格式,release 是安装实例:Terraform 的 helm_release 管的是实例,chart 版本和 values 是它的参数。
- 不要双重管理:Terraform 接管的 release,禁止手工 helm 操作;反之团队约定独立管理的 release 就不要写进 Terraform。
- values 也是代码,也分环境:dev/prod 的 values 文件和 tfvars 理念相同;密钥走 --set 环境变量或 External Secrets,不写进文件。
- Helm 不管理云资源:装 chart 不会自动给你建 EKS、不会配 VPC——那是 Terraform 的地盘(少数 chart 会调云 API 创 LB 等,那是 K8s 控制器的行为,不是 Helm 本身)。
你可能会懵的地方
-
直接用 helm 命令不就行了,为什么要套一层 Terraform?
对纯应用 release,直接用 helm(或 ArgoCD 驱动 helm)确实更合适。套 Terraform 的价值在于平台 bootstrap 组件与集群同生命周期、统一审批和 state;不是所有 chart 都需要。
-
Terraform apply helm_release 卡住超时?
chart 里的资源没就绪(CRD 缺失、镜像拉不动、云 LB 慢)。用 helm status、kubectl get events 查;可设置 timeout 和 wait = false,但要接受异步未完成的事实。
-
helm rollback 后 Terraform plan 为什么报差异?
集群内版本回退了,但 Terraform 配置里还是新版本——它认为又该升回去。再次印证单一权威原则:回滚也要么走 Terraform,要么这个 release 不归 Terraform 管。
-
Helm 模板和 Terraform 模块是一回事吗?
不是。Helm 模板渲染出 K8s YAML 提交给集群;Terraform 模块是资源树的封装,可跨任意 provider。形似而神不同。
-
CRD 应该谁装?
常见做法:提供 CRD 的官方 chart 由 Terraform 引导(crd-install 或独立 chart),业务 CR 实例归 Helm/GitOps;注意 CRD 生命周期常比实例长,别随应用卸载误删。
错法对照
| 错法 | 问题 | 正解 |
|---|
| Terraform 管 release 又手工 helm upgrade | 状态互踩 | 二选一当权威 |
| 所有应用发版都改 Terraform 仓库 | 发版慢、审批错位 | 应用走 Helm/GitOps |
| chart 版本不钉、values 写死环境差异 | 不可重复 | version 固定 + 环境 values |
| chart 仓库密码写进 .tf | 凭据泄露 | 环境变量注入 |
| 指望 Helm 建集群/网络 | 职责错位 | Terraform 先建好底座 |
卡点急救
could not find the kube config:helm provider 的 kubernetes 块没配对。和 kubernetes provider 使用同样的 config/exec 配置。
- release 已存在但不在 state:之前手工装过。先
helm uninstall 清理,或 terraform import helm_release.x <release名> 纳管后再改。
- CRD 安装顺序失败:启用 chart 的 crds 选项或先 apply 独立 CRD release;重新 plan/apply。
面试常问
-
Terraform 和 Helm 如何分工?
Terraform 管云基础设施、集群和低频引导组件(可用 helm_release 安装 ingress/ArgoCD 等平台 chart);Helm 负责应用包的安装、升级、回滚,高频业务发版经 Helm 或 GitOps 完成。两者通过「单一 release 单一权威」原则避免冲突。
-
在 Terraform 里管理 Helm release 有什么风险?
与手工/其他系统操作互相覆盖;回滚路径分裂;应用发版被基础设施审批节奏拖慢。缓解方式:只把低频平台组件放 Terraform,业务 release 交给 GitOps,并禁止双重操作。
-
Helm 能替代 Terraform 吗?
不能。Helm 只处理集群内应用包,不提供跨云资源生命周期管理和统一 state;两者互补。
自测三问
- 划分 Terraform 与 Helm 的边界,你依据哪两个核心维度?
- Terraform apply 过的 helm release,为什么不能再 helm rollback?真发生了怎么办?
- 让你从零设计一个 EKS 平台,从 VPC 到业务应用,各层分别用什么工具交付?
小结一句话
Terraform 管低频的基础设施与集群引导,Helm 管高频的应用包升级回滚;helm_release 是两者唯一握手点,握手之后一个 release 只认一个管理者。
延伸