Terraform 101
前言
在国内,使用 Terraform 的公司屈指可数。通常,在市场上取得巨大成功的公司会选择自建 IDC,如一线和二线的大厂,这些公司拥有数十万甚至上百万台物理服务器。使用混合云并达到一定规模的公司更是少之又少,加上国内 ToB 市场的挑战,导致 IaC 在国内的发展并不理想。然而,在国际市场上,传统行业选择公有云时,要求必须支持 Terraform,Terraform 已然成为事实上的 IaC 标准。
关于 Terraform 的中文讨论非常少,以下是我学习和整理 Terraform 内容的分享。
Terraform 简介
Terraform 是由 HashiCorp 公司研发的开源 IaC(基础设施即代码)工具。它由 Go 语言编写,可以在各个平台上运行,包括 Linux、Mac 和 Windows。Terraform 简单易用,即使没有太多代码经验的人也能读懂 Terraform 的配置代码 HCL(HashiCorp Configuration Language)。
HCL,即 HashiCorp Configuration Language,是 HashiCorp 公司开发的一种专用配置语言,扩展名为
.tf
主要特性:
- 基础设施即代码(Infrastructure as Code,简称 IaC)
- 使用配置语言 HCL 描述基础设施,使代码更容易共享和重用。
- 变更计划(Plan)
- 在实际变更前,Terraform 可以根据代码和当前状态生成变更计划,明确告诉你哪些资源将会被创建、修改或销毁。这可以在执行变更前进行最后的检查,为基础设施提供额外的保护层。
- 资源视图(Resource Graph)
- 通过依赖关系生成资源视图,直观地展示整个基础设施的关系图,帮助理解资源之间的依赖和相互作用。
- 自动化(Automation)
- 自动执行基础设施变更,无需人工干预,确保一致性和高效性。
- 跨平台支持
- Terraform 支持多种云服务提供商和私有云环境,包括 AWS、Azure、Google Cloud、alicloud、tencentcloud、huaweicloud 等,使得多云环境的管理更加简便,基本上常用的云都有其实现的 provider
- 模块化(Modularity)
- 支持模块化,用户可以创建、共享和复用模块,提高配置的可维护性和可扩展性。
Terraform 是一个功能强大且灵活的工具,使得管理和部署基础设施变得更为简便和高效。
通过对比
Terraform 与其他 IaC 工具的对比
名称 | 置备工具 | 易于使用 | 免费、开源 | 声明式 | 云无关 | 表达能力强、可扩展 |
---|---|---|---|---|---|---|
Ansible | √ | × | × | √ | × | × |
Chef | √ | √ | × | × | × | × |
Puppet | √ | √ | × | × | × | × |
SaltStack | √ | × | × | × | × | × |
Terraform | × | x | x | x | x | x |
Pulumi | × | √ | × | √ | × | × |
AWS CloudFormation | × | × | √ | x | √ | √ |
GCP Deployment Manager | × | × | √ | x | √ | √ |
Azure Resource Manager | × | √ | √ | √ | √ | √ |
从技术上讲,Pulumi 最接近 Terraform,唯一的区别在于它不是声明式的。Pulumi 团队认为这是 Pulumi 相较于 Terraform 的优势,但 Terraform 也有一个云开发工具包(Cloud Development Kit,CDK),允许实现相同的功能。
Terraform 的设计受到了 AWS CloudFormation 的启发,并且与 GCPDeployment Manager 和 Azure Resource Manager 有很相近的地方。
Ansible、Chef、Puppet 和 SaltStack 都是配置管理工具,而不是基础设施置备工具。它们解决的问题类别与 Terraform 有些区别,不过也存在重叠的地方
置备工具
- Terraform 是一个声明式 IaC置备工具(provisioning tool),可以把资源部署到任何公有云或私有云。置备工具不是配置管理工具,置备工具部署和管理基础设施,配置管理工具(如 Ansible、Puppet、SaltStack 和 Chef)将软件部署到现有服务器上。
易于使用
- Terraform 之所以如此易用,主要原因在于其代码是用一种称作 HashiCorp Configuration Language(HCL)的领域特定的配置语言编写的。HashiCorp 开发了这种语言,用来替代更加冗长的 JSON 和 XML 等配置语言。HCL 试图在人类可读性和机器可读性之间达到一种平衡,并受到了这个领域中一些早期尝试(如 libucl 和 Nginx 配置)的影响。HCL 与 JSON 完全兼容,这意味着 HCL 能够完全转换为 JSON,反之亦然。
免费且开源的软件
Terraform 的引擎称作 Terraform core,这是一款免费且开源的软件,通过 Mozilla Public License v2.0 提供
- 注:自 2023.8.21 以来 HashiCorp宣布,未来发布的所有产品和若干库将从 Mozilla Public License v2.0 (MPL 2.0) 过渡到 Business Source License (BSL, 或 BUSL) v1.1。HashiCorp API、SDK、Terraform 提供者和几乎所有其他库都将保留 MPL 2.0。
Terraform 没有提供高级版本,但提供了商业解决方案和企业解决方案(Terraform Cloud和Terraform Enterprise),可成规模运行 Terraform。
声明式编程
- 声明式编程指的是表达计算逻辑(做什么),但不描述控制流(怎么做)。你不必编写一步步执行的指令,只要描述自己想要的结果即可。Java 的 Stream 流式编程
云无关
- 云无关指的是能够使用一组相同的工具和工作流,无缝运行在任意云平台上。Terraform 是云无关的,使用 Terraform 把基础设施部署到 AWS 与部署到 GCP、Azure 甚至私有数据中心一样简单(参见图 1.2)。云无关很重要,因为这意味着你不会被局限于特定的云供应商,也不需要在每次改变云供应商时学习一种全新的技术。做一个 broker,屏蔽不同云商之间的差异
我的理解:大多数云服务商为用户提供了控制台 UI 界面,以及基于大客户提供的 SDK 和 CLI 等工具,但对于资源管理和编排没有统一的标准。而 Terraform 通过其声明式配置语言和状态管理机制,提供了一个统一的、面向状态的基础设施管理标准。
关于Pulumi
- 支持多语言,与开发语言相关,比如 JS、TS、python、java、go、.NET
- 面向开发者:不使用配置语言了,使用编程语言,门槛略高些
- 架构和模型:
- 与应用代码集成:Pulumi 可以更容易地与应用代码集成,因为它使用相同的编程语言。这使得基础设施和应用代码可以更加紧密地结合在一起。
- Terraform:声明式
- 资源定义
- 编程模型:Pulumi 使用编程模型来定义资源,这意味着可以利用编程语言的全部功能,如循环、条件语句和模块化。
- Terraform:支持模块化
- 状态管理
- 托管状态:Pulumi 提供托管的状态存储服务,但也支持将状态存储在本地文件系统或其他后端(如 AWS S3、GCP Storage)
- Terraform:默认使用本地状态文件,用户可以配置将状态存储在各种远程后端(如 AWS S3、GCP Storage 等),并支持状态锁定机制
关于 SDK
使用各大云商的 SDK,之前做过的一种云管方式,一个或者多个项目来引入各种云商的 SDK,然后定义好统一模型以及设计模式,通过策略模式来路由到相关云商资源的 CRUD。
- 优点:
- 接入和维护简单:使用各大云商的 SDK,可以直接调用云服务的 API,链路短,易于定位和解决问题。
- 贴合公司业务:可以根据公司的具体需求自定义和优化,确保与业务需求高度契合。
- 缺点:
- 接入云商较慢:需要自己阅读和理解云商的文档,开发和集成时间较长。
- 测试云商的 bug:需要处理云商 SDK 的各种问题和 bug,增加了维护成本。
HCL 配置语言(HashiCorp Configuration Language )
易于使用,HCL 是机器可读与人类可读性达到一种平衡,和 JSON 之间可以完全转换
从语言角度
基本类型:
- 字符串 string,如
"pkslow.com"
- 数字 number,如
319
或5.11
- 布尔值 bool,如
true
组合类型:
- 列表 list(),如
["dev", "uat", "prod"]
- 集合 set(),如
set(...)
- 映射 map(),如
{name="Larry", age="18"}
- 对象 object
({name1=T1, name2=T2})
- 元组 tuple
([T1,T2,T3...])
从功能角度
输入变量、输出变量与模块化作用范围
对变量的引用
类型 | 引用方式 |
---|---|
资源 Resources | <Resource Type>.<Name> |
输入变量 Input Variables | var.<NAME> |
本地变量 Local Values | local.<NAME> |
子模块的输出 | module.<Module Name>.<output Name> |
数据源 Data Sources | data.<Data Type>.<Name> |
路径和 Terraform 相关 | path.module :模块所在路径path.root :根模块的路径path.cwd :一般与根模块相同,其它高级用法除外terraform.workspace :工作区名字 |
块中的本地变量 | count.index :count 循环的下标;each.key /each.value :for each 循环的键值;self :在 provisioner 的引用; |
输入变量 - 关键字
有一组关键字不可以被用作输入变量的名字:
source
version
providers
count
for_each
lifecycle
depends_on
locals
输入变量只能在声明该变量的目录下的代码中使用。
输入变量块中可以定义一些属性。
断言validation
1 |
参数文件
调用 terraform 命令时,通过 -var-file
参数指定要用的参数文件
输出值
outputs.tf 文件
局部值
locals{}
元参数声明
resource 块支持几种元参数声明,这些元参数可以被声明在所有类型的 resource 块内,它们将改变资源的行为:
depends_on
:显式声明依赖关系count
:创建多个资源实例for_each
:迭代集合,为集合中每一个元素创建一个对应的资源实例provider
:指定非默认 Provider 实例lifecycle
:自定义资源的生命周期行为provisioner
和connection
:在资源创建后执行一些额外的操作
count
terraform 关键字,resource
块最外层,0
表示不创建资源,大于 1
是多个
一个 resource 块定义了一个对应的实际基础设施资源对象。
Terraform 提供了两种方法实现这个目标:count
与 for_each
。
for_each
从 variables.tf 数组中遍历,赋值资源
lifecycle
三个元参数:
create_before_destroy
prevent_destroy
在 resource 块内声明了
prevent_destroy = true
会导致无法执行terraform destroy
,所以对它的使用要节制。需要注意的是,该措施无法防止我们删除 resource 块后 Terraform 删除相关资源,因为对应的prevent_destroy = true
声明也被一并删除了。ignore_changes
1 |
provisioner 资源置备器
名称 | 描述 | 示例 |
---|---|---|
file | 从远程或本地文件复制文件到新创建的资源目录 | provisioner "file" { source = "conf/myapp.conf" destination = "etc/myapp.conf" } |
local-exec | 调用在 Terraform 的例程(而不是资源上)的任意命令 | provisioner "local-exec" { command = "echo hello" } |
remote-exec | 调用在远程资源上任意命令。调取已安装在远程资源上的任意终端工具,启动脚本等 | provisioner "remote-exec" { inline = ["puppet apply",] } |
操作超时设置
timeouts
数据源 datasource
ex: 获取最新的 AMI ID
1 |
1 |
算法和逻辑表达式
表达式
dynamic 块
动态的赋值某些支持资源类型的值
字符串模版
字符串插值Interpolation
一个 ${...}
序列被称为插值,插值计算花括号之间的表达式的值,有必要的话将之转换为字符串,然后插入字符串模版,形成最终的字符串:
1 |
上面的例子里,输入变量 var.name
的值被访问后插入了字符串模版,产生了最终的结果,比如:"Hello, Juan!"
操作步骤
命令
主要四个关键操作
1 |
以及在 plan 之前可以使用terraform vadiate
来验证配置是否正确,最精简的两个步骤:
terraform init
terraform apply
配置 AKSK
目前的 AKSK 是明文放在各自的环境变量中
配置方式
在~/.zshrc
或者~/.bashrc
配置
然后source
1 |
文件
配置文件
状态文件
状态文件以tfstate
为后缀
CRUD
生命周期
validate
主要用来验证 Terraform 配置文件的语法和配置是否有效,确保没有任何语法错误或者配置上的错误,而无需访问任何远程服务。
- 语法检查:确保配置文件遵守 HCL 的语法
- 配置验证:验证配置中的属性和值是否符合预期的格式,比如确保资源属性设置的类型正确,引用的变量或资源存在等。
- 环境验证:检查配置的环境是否符合执行要求,例如所需的提供者是否已正确配置和初始化。
plan
Terraform 在为新部署生成执行计划时完成的步骤
资源定义
1
执行代码
1
当为已经处在期望状态的现有部署生成执行计划时,Terraform 执行的步骤
force new 可变与不可变更新的区别
force new 特性:
如果修改它,整个资源就会被污染。为了得到新的期望状态,Terraform必须重建资源。这是不可变基础设施的典型示例,不过并不是Terraform管理的资源的所有特性都具有这种行为。事实上,大部分资源支持就地(即可变)更新
- 对任何 force-new 特性的任何修改都将触发 force-new 更新,因为现在把 create_before_destroy 设置为 true,所以将在销毁旧资源前先创建替换资源。这仅适用于管理资源,而不适用于数据源。
Terraform 生成更新的执行计划时执行的步骤
资源定义
1
执行代码
1
Terraform 在生成删除资源执行计划时执行的步骤
执行代码
1
参考文献
- https://registry.terraform.io/providers/hashicorp/random/latest/docs
- https://github.com/LarryDpk/terraform-101
- terraform 官网
- 《Terraform 实战》[美] 斯科特·温克勒
- 《Terraform:多云、混合云环境下实现基础设施即代码(第 2 版)》(美) 叶夫根尼·布里克曼 (Yevgeniy Brikman)