- 文档/教程
【免费下载链接】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 挑战中 Day 59 的完整实战记录:以 Terraform 的「期望状态配置」为核心,通过社区terra-farm/virtualboxProvider 在本地 VirtualBox 上批量创建并管理多台 Ubuntu 虚拟机,并深入讲解变量(Variables)与输出(Outputs)的多种定义方式、优先级规则和敏感信息处理。读完本文,你将掌握一套不依赖云端账号、在笔记本上即可复现的 Terraform 全流程(init → plan → apply → destroy),并能将其无缝迁移到 AWS、Azure 等公有云环境。
为什么用 Terraform 管理本地 VirtualBox
通常 Terraform 被用于公有云(AWS、Google Cloud、Microsoft Azure)以及 VMware、Hyper-V、Nutanix AHV 等虚拟化平台的资源编排。VirtualBox 属于工作站级虚拟化软件,正常情况下并不是 Terraform 的典型使用场景——正如原文档作者所言,本次演示发生在三万六千英尺的高空航班上,相比在云端开通资源,在笔记本电脑本地创建虚拟机要快得多。
但这并不妨碍我们用它来理解核心思想:编写一份描述目标状态的配置代码(HCL),交给 Provider 去执行,让基础设施按声明的方式被创建、修改与销毁。这个「声明式」过程与以往用 Vagrant 的体验不同——在本章节开头已经对比过 Vagrant 与 Terraform 的差异,简单来说:
- Terraform以「期望状态」为驱动力,配置变更时对比状态文件,只做必要的增删改;
- Vagrant更偏向开发环境的可重复供给(provisioning),面向单机工作流。
Terraform 的架构由两部分组成:代码(configuration)与状态(state),二者合称 Terraform Core;而真正与目标平台通信的是Provider(如 AWS Provider、Azure Provider,以及本次使用的 VirtualBox 社区 Provider),它们通过平台 API 执行创建、更新、删除操作。Terraform Registry 上有数百个 Provider,既能引用官方发布的(如hashicorp/aws),也能自行编写并本地使用或发布到 Registry。
前置准备
动手前,需要在本机准备好两样东西:
- VirtualBox:Provider 实际调用的虚拟化引擎,负责承载并运行虚拟机;
- Terraform CLI:解析 HCL 配置并驱动 Provider 的命令行工具。
接着创建项目目录:
mkdir virtualbox cd virtualbox在目录内新建virtualbox.tf,所有资源定义都写在这个文件中。仓库中对应的完整源码位于 2022/Days/IaC/Virtualbox/virtualbox.tf,下文将逐段拆解。
编写 virtualbox.tf:Provider、资源与输出
virtualbox.tf由三个层次组成:声明 Provider 的terraform块、描述资源的resource块、以及展示结果的output块。
1. 声明 Provider
terraform { required_providers { virtualbox = { source = "terra-farm/virtualbox" version = "0.2.2-alpha.1" } } } # There are currently no configuration options for the provider itself.source:Provider 在 Registry 上的发布地址,terra-farm/virtualbox是社区维护的 VirtualBox Provider;version:锁定0.2.2-alpha.1版本。这是一个alpha 版本,说明 VirtualBox Provider 仍处于活跃迭代期,实际生产环境建议优先使用官方云平台 Provider;- 该 Provider 目前没有可供配置的 provider 级选项,所以无需写
provider "virtualbox" {}块。
2. 定义资源:批量创建两台 VM
resource "virtualbox_vm" "node" { count = 2 name = format("node-%02d", count.index + 1) image = "https://app.vagrantup.com/ubuntu/boxes/bionic64/versions/20180903.0.0/providers/virtualbox.box" cpus = 2 memory = "512 mib" network_adapter { type = "hostonly" host_interface = "vboxnet1" } }关键参数说明:
| 参数 | 值 | 含义 |
|---|---|---|
count | 2 | 元参数(meta-argument),将同一资源块实例化 2 份,生成node[0]、node[1]两个实例 |
name | format("node-%02d", count.index + 1) | 利用format函数格式化名称:count.index从 0 开始,%02d保证两位补零,最终得到node-01、node-02 |
image | Ubuntu bionic64 的 VirtualBox box 下载地址 | 虚拟机模板镜像,Provider 会据此创建系统盘 |
cpus | 2 | 每台虚拟机分配 2 个 vCPU |
memory | "512 mib" | 每台分配 512 MiB 内存(注意是带单位的字符串) |
network_adapter.type | "hostonly" | 使用 VirtualBox 仅主机(host-only)网络 |
network_adapter.host_interface | "vboxnet1" | 绑定到宿主机已创建的vboxnet1虚拟网卡 |
3. 定义输出:读取每台 VM 的 IP
output "IPAddr" { value = element(virtualbox_vm.node.*.network_adapter.0.ipv4_address, 1) } output "IPAddr_2" { value = element(virtualbox_vm.node.*.network_adapter.0.ipv4_address, 2) }virtualbox_vm.node.*.network_adapter.0.ipv4_address是splat 表达式:把所有node实例的第一个网卡(下标 0)的 IPv4 地址收集成列表;element(list, index)按索引取值,分别取出第 1 个与第 2 个元素的地址输出到终端。原文档有意从1而非0开始取,仅作演示,读者可根据实际实例数调整索引。
apply完成后,Terraform 会在终端打印这两个输出值,方便你直接 SSH 登录对应虚拟机。
三步创建:init → plan → apply
代码就绪后,按顺序执行三个核心命令。整个 CLI 流程与 Day 58(HCL 基础) 中 hello-world 示例一脉相承,仓库中的 2022/Days/IaC/Hello-world/main.tf 就是那个最小示例。
terraform init:初始化项目并下载 Provider
terraform initinit是任何 Terraform 项目的第一步:解析required_providers,从 Registry 下载terra-farm/virtualbox插件到本地,并在项目目录生成.terraform目录与.terraform.lock.hcl锁文件(记录 Provider 版本哈希,保证后续执行的一致性)。
terraform plan:预览执行计划
terraform planplan会读取配置与现有状态,输出一份「将要创建 / 变更 / 销毁什么资源」的执行计划,相当于真正的apply之前的安全预览,不会对基础设施产生任何实际改动。
terraform apply:落地创建
terraform applyapply内置安全确认机制:先展示与plan相同的执行计划,并要求你输入yes才真正创建资源。下图展示了 2 台名为node[0]、node[1]的虚拟机创建完成、以及输出的内网 IP 地址:
完成后打开 VirtualBox 管理器,即可看到 2 台正在运行的虚拟机node-01、node-02。
横向扩容:修改 count 增加节点
Terraform 最核心的价值在于通过修改期望状态驱动基础设施变更。现在想把集群扩到 3 台,只需把count从2改成3:
resource "virtualbox_vm" "node" { count = 3 # ...其余参数保持不变 }再次执行terraform apply,Terraform 会对比状态文件:已有的 2 台保持不变,只新增第 3 台(node-03)。这种「增量式、幂等化」的变更能力,正是声明式 IaC 相比手工操作的根本优势——同样的配置反复执行,得到的结果始终一致,全程无需人工干预。
terraform destroy:一键清理
实验结束后,用一条命令移除全部资源:
terraform destroy同样带安全确认:预览即将销毁的资源清单(含虚拟机网卡等配置),输入yes后执行删除:
如果你希望在自动化脚本中跳过确认,可以在apply/destroy末尾追加--auto-approve。但建议仅在学习和测试环境使用——资源往往销毁得比创建时更快,生产环境请务必保留人工确认环节。
Day 58 把本次用到的 CLI 命令总结为四件套,这里一并列出:
| 命令 | 作用 |
|---|---|
terraform init | 初始化项目目录、下载并锁定 Provider |
terraform plan | 预览将要创建、变更的资源 |
terraform apply | 按代码部署资源 |
terraform destroy | 销毁项目创建的资源 |
对应的两个核心配置概念是:providers(Terraform 如何通过 API 与目标平台通信)与resources(我们想用代码部署什么)。
变量与输出:六种定义方式与优先级
Day 59 的另一个重头戏是变量系统。上一节的 hello-world 示例已经展示过output的用法,本节深入变量定义。
六种变量来源
Terraform 允许通过以下方式为变量赋值:
- 在
terraform plan/terraform apply命令中手动交互输入; - 在
.tf文件内的variable块中定义(通常配合default); - 使用系统环境变量,格式为
TF_VAR_变量名; - 在项目目录放置
terraform.tfvars文件(作者的个人偏好,也是团队协作最常见的做法); - 使用
*.auto.tfvars文件(文件名以.auto.tfvars结尾,Terraform 启动时自动加载,无需显式指定); - 执行命令时通过
-var或-var-file参数显式传入。
优先级规则
上述来源的优先级从高到低(即-var/-var-file最高):
- 命令行
-var与-var-file *.auto.tfvars文件terraform.tfvars文件TF_VAR_环境变量.tf文件内的default默认值- 手动交互输入(仅当变量未被任何来源赋值时才触发)
也就是说,高优先级的来源会覆盖低优先级来源对同一变量的赋值,而default仅在没有任何来源提供值时生效。原文档中「从列表底部往上数,就是变量生效的顺序」描述的正是指向这一规则。
仓库中的真实变量示例
仓库 2022/Days/IaC/Terratest/examples/vars.tf 展示了生产风格的定义方式:
variable "AWS_ACCESS_KEY" { } variable "AWS_SECRET_KEY" { } variable "AWS_REGION" { default = "ap-south-1" } variable "AMIS" { type = map(string) default = { ap-south-1 = "ami-0860c9429baba6ad2" } }这里可以看到:没有default的变量(如AWS_ACCESS_KEY)必须在 apply 前由外部来源提供值,否则 Terraform 会交互式询问;而带default的变量(如AWS_REGION)在未显式赋值时自动回退到默认值;AMIS则演示了map(string)这类复合类型的默认值写法。配套的 terraform.tfvars 文件即用于集中存放这类敏感或区域相关的赋值。
sensitive 变量:保护状态文件中的敏感信息
前面 Day 58 已提醒过:terraform.tfstate状态文件是 JSON 格式的「Terraform 眼中的世界」,其中会明文记录敏感数据。最佳实践是:
- 将
.tfstate文件加入.gitignore,避免提交到代码仓库; - 生产环境把状态存到远端共享位置(如 S3 桶、Terraform Cloud),以换取加密、协作与自动化能力,代价是增加一定的复杂度。
针对敏感数据,可以为变量开启sensitive标记(注意 HCL 赋值语法是=,原文档示例中的type:应写作type =):
variable "some_resource" { description = "something important" type = string sensitive = true }标记后,该变量在plan/apply输出中会被脱敏隐藏,避免密码、密钥等直接暴露在终端日志中。
延伸:从本地 VirtualBox 到云端与容器
本次演示虽然跑在 VirtualBox 上,但掌握的原理完全可迁移:
- 把
resource块换成aws_instance(参考 Day 58 的 AWS 示例),配合hashicorp/awsProvider,即可在 AWS 上创建 EC2 并注入user_data启动脚本; - 仓库的 2022/Days/IaC/Docker/docker.tf 与 2022/Days/IaC/Kubernetes/kubernetes.tf 还提供了用 Terraform 管理 Docker 容器与 Kubernetes 资源的示例,可作为后续深入方向;
- 若要为 Terraform 代码编写自动化测试,可参考仓库中的 Terratest 示例(测试用例),把「配置 → 应用 → 验证 → 清理」固化为可重复的测试流程。
小结
通过 Day 59 的完整演练,你掌握了:
- 用
virtualbox_vm资源 +count元参数批量创建虚拟机,并用format、splat 表达式、element组织名称与输出; - Terraform 的标准工作流:
init下载 Provider、plan预览变更、apply落地资源、destroy一键清理; - 六种变量赋值方式及其优先级,以及用
sensitive = true保护敏感信息; - 状态文件的安全风险与远程存储方案。
这套「代码定义期望状态 → Provider 执行 → 状态文件追踪」的心智模型,是后续在公有云、容器与 Kubernetes 场景下使用 Terraform 的通用基础。下一节(Day 60)将继续深入 Terraform 的进阶用法。
注:原文档末尾附有若干 Terraform 学习视频与资源清单,此处不再逐一罗列外部链接;仓库内 2022/Days/day59.md 保留了完整原文,可直接查阅。
- 文档/教程
【免费下载链接】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 实战:用 Terraform 与变量在 VirtualBox 中创建虚拟机(Day 59)
90DaysOfDevOps 实战:用 Terraform 与变量在 VirtualBox 中创建虚拟机(Day 59) 在 90DaysOfDevOps 的
文档/教程90DaysOfDevOps:使用 Terraform 与变量在 VirtualBox 中创建虚拟机(Day 59 实战指南)
90DaysOfDevOps:使用 Terraform 与变量在 VirtualBox 中创建虚拟机(Day 59 实战指南) 本文是 90DaysOfDevO
文档/教程90DaysOfDevOps 第 59 天:用 Terraform 与变量在 VirtualBox 中创建虚拟机
90DaysOfDevOps 第 59 天:用 Terraform 与变量在 VirtualBox 中创建虚拟机 导读 本文是《90DaysOfDevOps》学
文档/教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考