前言

在国内,使用 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
2
3
4
5
6
7
8
9
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."

validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}

参数文件

调用 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
lifecycle 配置影响了 Terraform 如何构建并遍历依赖图。作为结果,lifecycle 内赋值仅支持字面量,因为它的计算过程发生在 Terraform 计算的极早期。这就是说,例如 prevent_destroy、create_before_destroy 的值只能是 true 或者 false,ignore_changes、replace_triggered_by 的列表内只能是硬编码的属性名。

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
2
3
4
5
6
7
8
9
10
data "aws_ami" "example" {
most_recent = true
owners = ["self"]

filter {
name = "name"
values = ["my-image*"]
}
}

1
2
3
4
5
6
7
8
9
resource "aws_instance" "my_instance" {
ami = data.aws_ami.example.id
instance_type = "t2.micro"

tags = {
Name = "MyInstance"
}
}

算法和逻辑表达式

表达式

dynamic 块

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

字符串模版

字符串插值Interpolation

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

1
2
"Hello, ${var.name}!"

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

操作步骤

命令

主要四个关键操作

1
2
3
4
5
6
7
8
9
10
11
# Prepare your working directory for other commands
terraform init

# Show changes required by the current configuration
terraform plan

# Create or update infrastructure
terraform apply

# Destroy previously-created infrastructure
terraform destroy

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

  1. terraform init
  2. terraform apply

配置 AKSK

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

配置方式

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

然后source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Terraform
# aliyun
export ALICLOUD_ACCESS_KEY="********"
export ALICLOUD_SECRET_KEY="********"

# txyun
export TENCENTCLOUD_SECRET_ID="********"
export TENCENTCLOUD_SECRET_KEY="********"

# huaweiyun
export HW_ACCESS_KEY="********"
export HW_SECRET_KEY="********"

# ...etc...

文件

配置文件

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

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

状态文件

状态文件以tfstate为后缀

CRUD

生命周期

validate

主要用来验证 Terraform 配置文件的语法和配置是否有效,确保没有任何语法错误或者配置上的错误,而无需访问任何远程服务。

  • 语法检查:确保配置文件遵守 HCL 的语法
  • 配置验证:验证配置中的属性和值是否符合预期的格式,比如确保资源属性设置的类型正确,引用的变量或资源存在等。
  • 环境验证:检查配置的环境是否符合执行要求,例如所需的提供者是否已正确配置和初始化。

plan

Terraform 在为新部署生成执行计划时完成的步骤

  • 资源定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 代码语言其实是HCL,这里有bash替代,会高亮一些内容
    terraform {
    required_version = ">= 0.15"
    required_providers {
    local = {
    source = "hashicorp/local"
    version = "~> 2.0"
    }
    }
    }

    resource "local_file" "literature" {
    filename = "art_of_war.txt"
    content = <<-EOT
    Sun Tzu said: The art of war is of vital importance to the State.

    It is a matter of life and death, a road either to safety or to
    ruin. Hence it is a subject of inquiry which can on no account be
    neglected.
    EOT
    }
  • 执行代码

    1
    terraform plan

当为已经处在期望状态的现有部署生成执行计划时,Terraform 执行的步骤

force new 可变与不可变更新的区别

force new 特性:

如果修改它,整个资源就会被污染。为了得到新的期望状态,Terraform必须重建资源。这是不可变基础设施的典型示例,不过并不是Terraform管理的资源的所有特性都具有这种行为。事实上,大部分资源支持就地(即可变)更新
  • 对任何 force-new 特性的任何修改都将触发 force-new 更新,因为现在把 create_before_destroy 设置为 true,所以将在销毁旧资源前先创建替换资源。这仅适用于管理资源,而不适用于数据源。

Terraform 生成更新的执行计划时执行的步骤

  • 资源定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    terraform {
    required_version = ">= 0.15"
    required_providers {
    local = {
    source = "hashicorp/local"
    version = "~> 2.0"
    }
    }
    }

    resource "local_file" "literature" {
    filename = "art_of_war.txt"
    content = <<-EOT
    Sun Tzu said: The art of war is of vital importance to the State.

    It is a matter of life and death, a road either to safety or to
    ruin. Hence it is a subject of inquiry which can on no account be
    neglected.

    The art of war, then, is governed by five constant factors, to be
    taken into account in one's deliberations, when seeking to
    determine the conditions obtaining in the field.

    These are: (1) The Moral Law; (2) Heaven; (3) Earth; (4) The
    Commander; (5) Method and discipline.
    EOT
    }
  • 执行代码

    1
    terraform apply -auto-approve

Terraform 在生成删除资源执行计划时执行的步骤

  • 执行代码

    1
    terraform destroy

参考文献

  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)