前言

在国内,使用 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、alicloudtencentcloud、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 CloudTerraform 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,如3195.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

text
1

text
1

算法和逻辑表达式

表达式

dynamic 块

动态的赋值某些支持资源类型的值

字符串模版

字符串插值Interpolation

一个  ${...}  序列被称为插值,插值计算花括号之间的表达式的值,有必要的话将之转换为字符串,然后插入字符串模版,形成最终的字符串:

text
1

上面的例子里,输入变量  var.name  的值被访问后插入了字符串模版,产生了最终的结果,比如:"Hello, Juan!"

操作步骤

命令

主要四个关键操作

1

以及在 plan 之前可以使用terraform vadiate 来验证配置是否正确,最精简的两个步骤:

  1. terraform init
  2. terraform apply

配置 AKSK

目前的 AKSK 是明文放在各自的环境变量中

配置方式

~/.zshrc 或者~/.bashrc 配置

然后source

1

文件

配置文件

配置文件均是以.tf 后缀的文件

一般设置为main.tf, terraform.tf

状态文件

状态文件以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

参考文献

  1. https://registry.terraform.io/providers/hashicorp/random/latest/docs
  2. https://github.com/LarryDpk/terraform-101
  3. terraform 官网
  4. Terraform 实战》[美] 斯科特·温克勒
  5. 《Terraform:多云、混合云环境下实现基础设施即代码(第 2 版)》(美) 叶夫根尼·布里克曼 (Yevgeniy Brikman)