- 文档/教程
【免费下载链接】90DaysOfDevOps
This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.
本文是 90DaysOfDevOps 挑战中“基础设施即代码(IaC)”章节的第 5 讲(对应仓库 2022/Days/day61.md)。前几讲我们已经用 Terraform 在 VirtualBox 中部署虚拟机(Day 59)、在本地 Docker 中部署容器(Day 60),本讲则把同一套“用代码定义期望状态”的思路延伸到 Kubernetes:通过 Terraform 的 Kubernetes Provider 在集群内创建命名空间、Deployment 和 Service,并讨论生产/预发/开发多环境编排的两种实现路径。读完本文,你将掌握用.tf配置管理集群内对象、用kubectl验证部署结果,以及在terraform workspaces与目录结构两种多环境方案之间做出取舍的完整能力。
从虚拟机到容器再到 Kubernetes:IaC 的演进脉络
在整个 IaC 章节中,核心前提始终没有变:在代码里定义目标基础设施的样子,然后交给 Terraform 去落地。Day 59 把这一前提用在 VirtualBox 虚拟机上(virtualbox.tf),Day 60 把它用在 Docker 容器上(docker.tf)。到了 Day 61,同样的前提被用于 Kubernetes 集群内部的对象。
仓库作者在文档中说明,他曾用 Terraform 在三大主流云厂商上部署 Kubernetes 集群用于演示;而本讲更进一步——不只部署集群,还要用 Terraform 直接管理集群内部的对象(命名空间、工作负载、服务等)。
两条进入 Kubernetes 的 Provider 路径
Terraform 与 Kubernetes 交互有两种官方途径:
- Kubernetes Provider(
hashicorp/kubernetes):管理集群内的原生对象,如kubernetes_namespace、kubernetes_deployment、kubernetes_service等资源类型; - Helm Provider(
hashicorp/helm):用于管理 Helm Chart 的部署与生命周期,适合以 Chart 形式交付的应用。
本文演示走的是第一条路径(Kubernetes Provider),用 HCL 直接声明集群内的三类资源。
为什么不用 kubectl?
之前章节我们一直用kubectl操作集群,但在 Kubernetes 环境中引入 Terraform 有两个明确收益:
- 统一工作流(Unified workflow):如果集群本身就是用 Terraform 部署的,那么集群内部的资源也使用同一套工具与流程,整个链路(从云资源到集群资源)只有一个编排入口;
- 生命周期管理(Lifecycle management):Terraform 不只是“创建工具”,它基于 state 文件持续跟踪资源状态,能够统一执行变更、更新与删除,而不是散落在一条条临时命令里。
实操:用 Terraform 在 Minikube 上部署 nginx
本讲沿用 Minikube 作为本地演示集群(与 Day 55 起的 Kubernetes 章节一致)。完整配置存放在仓库 2022/Days/IaC/Kubernetes/kubernetes.tf(文档中写作Kubernetes.tf,仓库实际以小写kubernetes.tf存储)。下面逐块解读这份配置,并给出每一步的验证命令。
完整配置文件
terraform { required_providers { kubernetes = { source = "hashicorp/kubernetes" version = ">= 2.0.0" } } } provider "kubernetes" { config_path = "~/.kube/config" } resource "kubernetes_namespace" "test" { metadata { name = "nginx" } } resource "kubernetes_deployment" "test" { metadata { name = "nginx" namespace = kubernetes_namespace.test.metadata.0.name } spec { replicas = 2 selector { match_labels = { app = "MyTestApp" } } template { metadata { labels = { app = "MyTestApp" } } spec { container { image = "nginx" name = "nginx-container" port { container_port = 80 } } } } } } resource "kubernetes_service" "test" { metadata { name = "nginx" namespace = kubernetes_namespace.test.metadata.0.name } spec { selector = { app = kubernetes_deployment.test.spec.0.template.0.metadata.0.labels.app } type = "NodePort" port { node_port = 30201 port = 80 target_port = 80 } } }配置要点逐块解读
Provider 声明与连接配置
terraform { required_providers { kubernetes = { source = "hashicorp/kubernetes" version = ">= 2.0.0" } } } provider "kubernetes" { config_path = "~/.kube/config" }source = "hashicorp/kubernetes":指定官方 Kubernetes Provider,version = ">= 2.0.0"表示允许安装不低于 2.0.0 的版本(首次terraform init实际解析到的版本会记录进.terraform.lock.hcl锁文件,保证后续安装一致);config_path = "~/.kube/config":指向本机 kubeconfig 文件。Minikube 生成的 kubeconfig 默认就位于该路径,Terraform 会复用其中记录的集群地址、凭据与当前上下文,因此前提是本机已存在一个可用集群(如 Minikube)且kubectl能正常访问。
命名空间资源
resource "kubernetes_namespace" "test" { metadata { name = "nginx" } }声明一个名为nginx的命名空间,作为后续资源的逻辑隔离边界。
Deployment 资源
resource "kubernetes_deployment" "test" { metadata { name = "nginx" namespace = kubernetes_namespace.test.metadata.0.name } spec { replicas = 2 ... } }namespace = kubernetes_namespace.test.metadata.0.name:这是 Terraform 的资源间引用(引用表达式),直接把上面命名空间资源的名称引用过来,Terraform 会自动建立依赖顺序——先建命名空间,再建 Deployment;replicas = 2:期望运行 2 个 nginx 副本;selector.match_labels与template.metadata.labels均使用app = "MyTestApp",保持标签一致才能让 ReplicaSet 正确选中 Pod;container.image = "nginx":使用默认 latest 标签的官方 nginx 镜像,容器名nginx-container,监听container_port = 80。
Service 资源
resource "kubernetes_service" "test" { metadata { name = "nginx" namespace = kubernetes_namespace.test.metadata.0.name } spec { selector = { app = kubernetes_deployment.test.spec.0.template.0.metadata.0.labels.app } type = "NodePort" port { node_port = 30201 port = 80 target_port = 80 } } }selector.app引用了 Deployment 模板里 Pod 的标签值(MyTestApp),Service 据此把流量路由到对应 Pod,这同样构成了资源间的隐式依赖;type = "NodePort"将服务暴露到节点端口30201,容器端口80作为port与target_port。之所以选择 NodePort 而非 Ingress,是因为 Minikube 的 Docker 网络对 Ingress 的支持有限(本讲文档也明确提到了这一限制)。
分步执行与验证
第 1 步:初始化项目
在存放kubernetes.tf的新项目目录中执行:
terraform init输出会显示 Terraform 已成功初始化,并自动下载、安装匹配>= 2.0.0的 Kubernetes Provider 插件,同时生成.terraform.lock.hcl锁文件。这一步会拉取 Provider,因此需要本机网络可达 Terraform Registry。
第 2 步:确认应用前的集群状态
执行kubectl get ns,在未 apply 之前,集群中只有default、kube-node-lease、kube-public、kube-system等系统预置命名空间,尚不存在nginx命名空间,以此证明后续资源确实由 Terraform 创建。
第 3 步:执行 apply
terraform apply计划输出显示将新增 3 个资源(命名空间、Deployment、Service),最终以Apply complete! Resources: 3 added, 0 changed, 0 destroyed.收尾,说明三个资源全部创建成功。这与仓库 kubernetes.tf 中声明的资源一一对应。
第 4 步:验证集群内的部署结果
kubectl get ns kubectl get all -n nginxkubectl get ns输出中新增了nginx命名空间;kubectl get all -n nginx则展示完整部署状态:
- Pod:2 个 nginx 实例均处于 Running,就绪 1/1;
- Service:类型为 NodePort,Cluster IP 为
10.104.210.141,端口映射80:30201/TCP,与配置中的node_port = 30201一致; - Deployment:期望 2 个副本、就绪 2 个;
- ReplicaSet:管理这 2 个副本,运行正常。
第 5 步:访问应用
由于 Minikube 的 Docker 网络对 Ingress 支持有限,直接访问需要做端口转发:
kubectl port-forward -n nginx svc/nginx 30201:80然后浏览器打开http://localhost:30201/,即可看到 nginx 的默认欢迎页(Welcome to nginx!),证明由 Terraform 声明的 Service 已正确把流量转发到 Deployment 管理的 Pod。
多环境部署:workspaces 与文件结构两条路线
演示代码跑通后,自然的下一步是:如果希望**生产(production)、预发(staging)、开发(development)**三套环境外观一致、复用同一份代码,Terraform 提供两种主流做法:
| 方案 | 本质 |
|---|---|
terraform workspaces | 在**同一个后端(backend)**内划分多个具名工作区 |
| 文件结构(目录布局) | 用目录隔离环境,用**模块(module)**实现复用 |
两种方案各有取舍,下面逐一分析。
terraform workspaces
优点
- 上手简单:一条
terraform workspace new dev即可创建新工作区,无需改动目录结构; - 表达式便捷:可直接使用
terraform.workspace表达式引用当前工作区名称,例如用它拼接命名空间或资源名,实现“一份代码、多环境复用”; - 代码重复最小化:所有环境共享同一份配置,不需要复制粘贴
.tf文件。
缺点
- 易受人为错误影响:所有环境共用一个后端,误切 workspace 后执行 apply,可能把变更应用到错误环境——这恰恰违背了使用 IaC 消除人为失误的初衷;
- 状态存于同一后端:多个环境的 state 文件存放在同一处,隔离性弱;
- 代码库无法无歧义地表达部署配置:单从代码仓库看不出某个工作区对应哪套环境,环境与配置的对应关系靠约定而非声明。
文件结构(目录布局)
优点
- 后端隔离:每个环境可以配置独立的 state 后端(如不同对象存储桶/路径),带来两层好处——安全性提升与人为失误概率下降,错误环境的 apply 不会触碰其他环境的 state;
- 代码库完整反映部署状态:从目录结构即可看出 dev/staging/prod 各自的配置与差异,环境对应关系一目了然。
缺点
- 需要多次 apply:每个环境都要单独执行一次
terraform apply(或通过 CI 分别触发),不能一次覆盖全部环境; - 代码重复更多:目录间会存在重复配置,但可以通过模块化大幅压缩——把通用资源抽成 module,各环境目录只保留差异化参数。
如何选择
结合本讲上下文可以得出实践倾向:workspaces 适合快速起步、环境差异小、团队规模小的场景;文件结构适合对环境隔离与安全要求高的生产级场景。第 59 讲已经介绍了模块(Module)作为资源组合与复用的载体,文件结构方案的“代码重复”痛点正好可以用模块化解,这也是 Day 62(测试、工具与替代方案)继续探讨的方向。
仓库配套:从单集群演示到工程化
围绕本讲主题,仓库 IaC 目录提供了可对照学习的配套素材:
- 2022/Days/IaC/Kubernetes/kubernetes.tf:本讲核心配置,可直接用于 Minikube 或任何
kubectl可达的集群; - 2022/Days/IaC/Docker/docker.tf 与 2022/Days/IaC/Docker-Wordpress/docker-wordpress.tf:Day 60 的容器编排示例,展示了相同工作流在 Docker 上的应用;
- 2022/Days/IaC/Virtualbox/virtualbox.tf:Day 59 的虚拟机示例;
- 2022/Days/IaC/Terratest:Go 语言编写的 IaC 自动化测试示例(
test/terraform_test.go及配套examples/目录),对应 Day 62 的测试章节; - 2022/Days/Kubernetes:Kubernetes 章节的 YAML 清单(如 nginx-stateless-demo.yaml、pacman-stateful-demo.yaml),可与本讲的
.tf声明式写法对照,理解同一目标在 YAML 与 HCL 两种语法下的差异。
值得注意的工程化细节:从配置看,Kubernetes Provider 的>= 2.0.0版本约束与terraform init生成的锁文件机制,保证不同机器、不同时间执行部署时 Provider 版本一致;而 Deployment/Service 通过引用表达式串联依赖关系,让 Terraform 的定向图(DAG)自动保证创建顺序,这也是“Terraform 不只是创建工具,还能统一管理变更与删除”的实现基础。
延伸阅读
- Day 59:用 Terraform 创建虚拟机与变量:理解 IaC 章节起点、变量注入方式(
TF_VAR_、terraform.tfvars、-var)与敏感信息处理; - Day 60:Docker 容器、Provisioners 与模块:本讲直接的前置实践,包含 Docker Provider、local-exec/remote-exec provisioner 与模块概念;
- Day 62:测试、工具与替代方案:本讲的后续延伸,覆盖
terraform fmt/validate/plan内置校验、Terratest 自动化测试及 Pulumi 等替代方案; - 2022/Days/IaC:本讲所属 IaC 章节的全部示例代码目录。
- 文档/教程
【免费下载链接】90DaysOfDevOps
This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.
相关推荐
90DaysOfDevOps 第 61 天实战:用 Terraform 管理 Kubernetes 资源与多环境部署
90DaysOfDevOps 第 61 天实战:用 Terraform 管理 Kubernetes 资源与多环境部署 导读 本篇文章是 90DaysOfDevO
文档/教程90DaysOfDevOps 实战:用 Terraform 管理 Kubernetes 资源与多环境部署(Day 61)
90DaysOfDevOps 实战:用 Terraform 管理 Kubernetes 资源与多环境部署(Day 61) 导读 本文是 90DaysOfDevO
文档/教程90DaysOfDevOps Day 61:用 Terraform 与 Kubernetes Provider 管理集群资源,并落地多环境部署策略
90DaysOfDevOps Day 61:用 Terraform 与 Kubernetes Provider 管理集群资源,并落地多环境部署策略 本文对应 9
文档/教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考