Terraform ['terəfɔ:m] 是 Hashicorp 公司开源的一种多云资源编排工具。使用者通过一种特定的配置语言(HCL - Hashicorp Configuration Language)来描述基础设施,由 Terraform 工具统一解析,构建资源之间的关系,生成执行计划,并通过调用各家云厂商的具体实现来完成整个基础设施生命周期的管理。
Terraform - Infrastructure as Code
官网 https://www.terraform.io/
中文官网 https://www.gaia.net/sc/brands_detail/0/0/93/hashicorp-terraform
教程 https://developer.hashicorp.com/terraform/tutorials
基础架构即代码 (Infrastructure as Code, IaC) 是指通过代码(而不是手动流程/控制台点击)来管理和配置基础架构。
这里有两个关键词:
- Infrastructure
- Code
Infrastructure 是被管理对象,在这里主要是指公有云(还有私有云、混合云等)
Code 是管理方式,即像管理代码一样管理公有云资源。那么管理代码最重要的部分是版本管理。
使用 IaC,创建的配置文件包含了基础设施的 spec,这使得编辑和分发配置变得更加容易。IaC 还确保每次都提供相同的环境、相同的资源、相同的配置。通过编辑和记录配置的 spec,IaC 有助于避免未记录的、临时的配置更改(当然,前提是所有人都使用 IaC,而不是还会有人在控制台点击修改导致配置漂移)。
版本控制是 IaC 的重要组成部分,配置文件应该像任何其他软件源代码文件一样受到源代码控制。 另外,随着公有云的发展,公有云的标准化的 API 也使得将基础架构组件模块化 (Terraform 里叫做 modules) 成为可能,使用者可以像搭积木一样组合这些基础的组件。比如:在 AWS 上建个静态博客,就可以组合以下组件:
有两种实现 IaC 的方法:声明式和命令式。
声明式方法定义了系统的理想状态,包括需要的资源以及它们应该具有的任何属性,IaC 工具将自动配置它。
Terraform 就是基于 IaC 声明式的理念。在 Terraform 流行之前,另一个将声明式发扬光大的当然是:Kubernetes!
声明式方法还保留系统对象当前状态的列表,这使得拆除基础架构更易于管理。
相反,命令式方法定义了实现所需配置所需的特定命令,然后需要以正确的顺序执行这些命令。
典型的就是 Ansible.
IaC 工具通常能够在两种方法中运行。如 Terraform, 它更喜欢声明式的方法,但是它的 Provider、Modules、函数中仍然残留不少命令式的方法, 如:local-exec
IaC(特别是声明式的)是随着公有云而发展起来的。
置备基础设施历来是一个耗时且成本高昂的手动过程。现在基础设施管理已经从数据中心的物理硬件、虚拟化转移到容器和云计算。借助云计算,基础设施组件的数量不断增加,每天都有更多的应用程序发布到生产环境中,并且基础设施需要能够频繁地启动、扩展和关闭。如果没有适当的 IaC 实践,管理当今基础设施的规模会变得越来越困难。
IaC 可以帮助您的组织管理 IT 基础设施需求,同时提高一致性并减少错误和手动配置。
目前主流的有:
声明式:
Terraform
AWS CloudFormation
Pulumi
CrossPlane
命令式:
Puppet
Chef
Ansible
SaltStack
IaC 是实施 DevOps 实践和 CI/CD 的重要组成部分。DevOps 和 CI/CD 主要是应用和组件层面的, 而 IaC 消除了开发人员的大部分配置工作,开发人员可以执行脚本让他们的基础设施准备就绪。这样一来,应用程序部署就不会因等待基础架构而停止,系统管理员也不需要管理耗时的手动流程。那么整个开发部署迭代的流程就会越来越快, 越来越快。
IaC 帮助您协调开发和运维,因为两个团队可以在代码仓库中看到基础架构全貌。而且每个环境都可以使用相同的部署过程。环境一致性也有保障,开发上测试通过的,生产上出问题的概率也小。
DevOps 最佳实践也适用于 IaC 中的基础架构。基础设施可以通过与应用程序在软件开发期间相同的 CI/CD 管道,对基础设施代码应用相同的测试和版本控制。将 IaC 的 modules 代码也引入 DevOps 理念,快速测试,快速迭代。
毋庸置疑,Terraform 是 IaC 领域事实上的领导者。
Terraform 是一个安全、高效地部署、更改、版本化基础设施和应用程序的工具,可以用来管理多层次的资源。从上层的软件配置到底层的网络、系统配置都可以使用 Terraform 统一进行管理。
Terraform 用配置文件来描述一个应用。Terraform 会将配置文件与当前环境对比后,生成一个执行计划,这个计划会列出为了达到配置文件中定义的状态所需要执行的操作,然后执行计划以达到期望的状态。
Terraform 通过插件机制管理不同的资源提供者,以此来接入各种资源,如虚拟机,存储,网络和各种应用服务。
Terraform 是一款基于插件的工具,因此其有一个核心应用 Terraform,和上百个插件。核心应用提供了一个统一的层来管理 IaC 代码,并且它还会负责安装所需的插件,调用它们、管理状态等。另一方面,插件可以与基础设施平台和应用通信,如AWS、GCP、Grafana、Jenkins、GitLab等。其中一些插件由 Hashicorp 团队维护,另外的那些则是由第三方维护。每个人都可以编写并发布自己的插件,访问下方网站可以获取插件库:https://registry.terraform.io/
Terraform 插件由两大主要类型:Provider 和 Provisioner。
Terraform 中使用的核心概念/术语:
Terraform 的生命周期由init、plan、apply和 destroy,4个阶段构成。
Terraform 可以让用户通过配置文件定义和管理整个基础设施和版本控制。这主要由 Terraform 架构中的两个主要组件来实现这一目标:Core 和 Provider。
要完成工作,Terraform core 需要使用2个输入源。
Terraform 会获取这些输入信息并决定下一步应该如何进行。它采用用户指定的期望状态,并将其与当前状态进行对比,进而对架构进行配置以消除两种状态之间的差距。Terraform core 本质上是要计算出需要创建、更新或删除的内容,以便全面配置基础设施。
第二个让 Terraform 运行的关键组件是 provider。常见的是云厂商,如 AWS 或 Azure,但其他基础设施或平台服务工具也可以。例如,Kubernetes 也是 Terraform 用的一个 provider。
Terraform 有上百个不同技术的 Provider 可供用户访问。例如,如果你正在使用 AWS,Terraform 可以访问 EC2 实例和在这一技术栈内的其他资源。接着,用户可以在不同层级上创建基础设施,比如在 Azure 上构建 Kubernetes。
这就是 Terraform 的工作原理:使用 Core 和 Provider 功能来快速完成应用程序和基础设施的设置,并且仅仅使用代码即可。
Terraform 的工作流程由以下3个步骤组成:
Step 1:写
在 Terraform 工作流程的第一步中,用户需要使用 Haschicorp Configuration Language (HCL)来将基础设施资源声明为代码。
Step 2:审核
接下来,Terraform 会展示其计划,它会根据用户设置的期望状态和现有资源的当前状态进行比较,来增加或移除资源。
Step 3:应用
最终,接受计划的更改,来增加或删除任何基础设施资源。然后基础设施将在 Terraform 的帮助下进行全面部署。
与 Terraform 类似的 Infrastructure as Code 工具大概有下面几种:
下面将从几个方面来说明 Terraform 与其他工具对比的优势。
Chef、Puppet、Ansible、SaltStack 都可以称为配置管理工具,这些工具的主要目标是在已经存在的机器上安装和管理软件。而 Terraform 和 CloudFormation 可以称为编排工具,更注重于数据中心以及相关服务的高级抽象。他们的工作重点是创建资源并且引导进行初始化。
在现在的环境下,大家使用容器等服务,镜像已经包括了软件的安装与配置。一旦你有了镜像,你需要的是一些服务器去运行它。对于提供服务器这种需求,编排工具会比配置管理工具更适合做此类工作。
Chef 和 Ansible 希望你去一步步编写程序以达到最终所期望的状态。
Terraform、CloudFormation、SaltStack、Puppet 希望你去声明最终想要的资源与资源的状态,工具本身会自动分析达到想要的状态需要进行怎样的操作。
在使用编程式语言时,工具不会获取历史的状态,所以我们需要考虑的更多以达到与之前版本的兼容。并且使用编程式语言会导致代码库变得越来越庞大,不利于人们理解与代码的复用。不过声明式的语言的表达能力是较为欠缺的,例如我们需要基础设施的滚动升级时,声明式的语言是很难满足要求的。
为此 Terraform 提供了一些基础服务,例如输入变量,输出变量,在销毁之前创建等。
Chef、Puppet、SaltStack 在默认情况下都使用了C/S架构。客户端(可能是 Web UI 或 CLI 工具)是用来发出命令(例如「deploy X」)的东西。
这些命令到达一个服务器,它负责执行你的命令并存储系统的状态。要执行这些命令,服务器会与 agent 进行通信,agent 必须在要配置的每个服务器上运行,这有几个缺点:
CloudFormation 也是C/S架构,但 AWS 透明地处理所有的服务器细节,作为最终用户,您只需要考虑客户端代码。
Ansible 客户端则通过 SSH 直接连接到您的服务器。
Terraform 使用云提供商 API 来配置基础架构,因此除了您已经使用云提供商之外,没有新的身份验证机制,并且不需要直接访问您的服务器。
综合对比
分类 | Chef | Puppet | Ansible | SaltStack | CloudFormation | Terraform |
---|---|---|---|---|---|---|
是否开源 | 开源 | 开源 | 开源 | 开源 | 闭源 | 开源 |
支持的云 | All | All | All | All | AWS only | All |
工具类型 | 配置管理 | 配置管理 | 配置管理 | 配置管理 | 编排工具 | 编排工具 |
语言类型 | 编程式 | 编程式 | 编程式 | 声明式 | 声明式 | 声明式 |
架构 | C/S | C/S | 客户端 | C/S | 客户端 | 客户端 |
原著这里提到的客户端架构,笔者理解为不需要额外安装或运行客户端程序,例如jenkins的节点需要运行agent.jar,所以是C/S架构。而ansible直接可以通过ssh(大部分系统自带)进行通信,则为客户端架构。
某应用使用了 Loadbalancer 进行 Instance 间的流量均衡处理,以增大吞吐率、扩大并发数、缩短延迟。
某应用需要隔离的网络环境,需要将应用搭建在 VPC 网络当中,架构如下:
VPC网络是指私有网络。私有网络(Virtual Private Cloud,VPC)是一块在公有云上自定义的逻辑隔离网络空间,您可以为 云服务器、云数据库 等资源构建逻辑隔离的、用户自定义配置的网络空间,以提升用户云上资源的安全性,并满足不同的应用场景需求。
随着业务调整和业务量增大,需要增加更多的节点和集群以承载更多的请求,此时需要对已有的资源进行扩容:
随着应用的不断迭代,应用部署和发布的成本也在增加,如何实现应用的快速部署和发布:
上述场景的共性:
操作流程配置固定;
手工操作效率低、时间长;
手工操作可能导致错误;
手工操作过程没有历史记录;
手工操作过程不能审计;
手工操作随着业务的复杂而不断增大。
terraform-providers: 集成了当前几乎所有公有云的iac工具实现
terraform aws 工具集:封装了aws的iac工具实现,例如申请ec2,创建vpc等等
编写配置文件:内容为按照手工操作流程,逐个调用aws工具集的工具实现
参考链接:https://geekflare.com/terraform-best-practices/
当您使用 Terraform 处理大型生产基础设施项目时,需要遵循适当的目录结构来应对项目中可能出现的复杂性。建议为不同目的建立单独的目录。如果您在开发、暂存和生产环境中使用 Terraform,请为每个环境设置单独的目录。
Terraform 配置也应该是分开的,因为经过一段时间后,不断增长的基础设施的配置将变得更加复杂。
geekflare@geekflare:~$ tree terraform_project/
terraform_project/
├── dev
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── modules
│ ├── ec2
│ │ ├── ec2.tf
│ │ └── main.tf
│ └── vpc
│ ├── main.tf
│ └── vpc.tf
├── prod
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── stg
├── main.tf
├── outputs.tf
└── variables.tf
6 directories, 13 files
您可以在文件 main.tf 本身内编写所有的 Terraform 代码(模块、资源、变量、输出),同时为变量和输出设置单独的 Terraform 代码使其更具可读性和易于理解。
Terraform 中使用命名规则让资源易于理解、一目了然。
例如,假设您要为项目中的不同环境创建三个不同的工作区。与其将环境命名为env1 、env2 、env3
,不如尝试使用dev、stage、prod
来命名。从名称本身来看,很明显每个环境都代表着不同的工作空间。资源、变量、模块等的也遵循类似的规则。
Terraform 中的资源名称应以相关资源名称开头,后跟下划线和其他详细信息。例如,用于在 AWS 中为 route table 创建 Terraform 对象的资源名称为 aws_route_table
。
所以如果正确地遵循命名规则,即使是复杂的代码也会更容易理解。
强烈建议使用可用的官方 Terraform 模块。直接使用现有模块,能够有效节省时间和精力。Terraform registry 有很多现成可用的模块,企业可以根据需要对现有模块进行更改。
此外,每个模块应该只专注于基础设施的一个方面,比如创建 AWS EC2 实例、设置 MySQL 数据库等。举个例子,如果您想在 terraform 代码中使用 AWS VPC,可以直接使用 -simple VPC
。
module "vpc_example_simple-vpc" {
source
= "terraform-aws-modules/vpc/aws//examples/simple-vpc"
version = "2.48.0"
}
Terraform 开发社区非常活跃,新功能发布频繁。建议在发布新的主要版本时使用最新版本的 Terraform。您可以轻松升级到最新版本。如果跳过多个主要版本,升级将变得非常复杂。
运行terraform -v
命令以检查版本更新。
geekflare@geekflare:~$ terraform -v
Terraform v0.11.14
Your version of Terraform is out of date! The latest version
is 0.12.0. You can update by downloading from www.terraform.io/downloads.html
请始终备份 Terraform 的状态文件,这些文件跟踪基础设施的元数据和资源。默认情况下,这些名为terraform.tfstate
的文件本地存储在工作区目录中。
如果没有这些文件,Terraform 将无法确定在基础设施上部署了哪些资源。因此,必须备份状态文件。默认情况下,terraform.tfstate.backup
会被创建并用来保存状态文件的备份。
geekflare@geekflare:~$ tree terraform_demo/
terraform_demo/
├── awsec2.tf
├── terraform.tfstate
└── terraform.tfstate.backup
0 directories, 3 files
如果要将备份状态文件存储到其他位置,在 terraform 命令中使用-backup
标记并提供位置路径。大部分情况下,一个项目会有多个开发人员参与。因此为了让开发人员方便访问状态文件。应该使用terraform_remote_state
数据源将其存储在远程位置。
以下示例将备份到 S3。
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = “s3-terraform-bucket”
key = “vpc/terraform.tfstate"
region = “us-east-1”
}
}
可能有多个场景,其中不止一个开发人员试图同时运行 Terraform 配置。这可能导致 terraform 状态文件损坏甚至数据丢失。锁定机制能够有效防止此类情况发生,确保一次只有一个人在运行 terraform 配置,并且没有冲突。
下面是使用 DynamoDB 锁定位于远程位置的状态文件的示例。
resource “aws_dynamodb_table” “terraform_state_lock” {
name = “terraform-locking”
read_capacity = 3
write_capacity = 3
hash_key = “LockingID”
attribute {
name = “LockingID”
type = “S”
}
}
terraform {
backend “s3” {
bucket = “s3-terraform-bucket”
key = “vpc/terraform.tfstate”
region = “us-east-2”
dynamodb_table = “terraform-locking”
}
}
当多个用户尝试访问状态文件时,DynamoDB 数据库名称和主键将用于状态锁定并保持一致性。注意:并非所有后端都支持锁定。
self
变量是一种特殊的变量,当您在部署基础设施之前不知道变量的值时可以使用它。假设您要使用仅在 terraform apply
命令后部署的实例的 IP 地址,也就是在它启动并运行之前您是不知道 IP 地址的。
在这种情况下,您可以使用自变量self.ATTRIBUTE
。在这个例子中,您可以使用self.ipv4_address
作为自变量来获取实例的 IP 地址。这些变量只允许在 terraform 配置的连接和配置块上使用。
connection {
host = self.ipv4_address
type = "ssh"
user = var.users[2]
private_key = file(var.private_key_path)
}
爆炸半径(Blast Radius)是衡量如果流程没有按计划进行时可能发生的损害的量度。
例如,如果您在基础设施上部署了一些 Terraform 配置,但配置没有得到正确应用,那么对基础设施造成的损害会有多大。因此,为了尽量减少故障影响范围,建议每次配置的基础设施数量不宜过多。即便出现问题,对基础设施的损害也能控制在较小范围,并且可以迅速得到纠正。一次性配置大量基础设施是非常危险的。
在 terraform 中,您可以创建一个带有扩展名的文件.tfvars
,并使用-var-file
此文件传递给terraform apply
命令。这个方式可以帮助您传递那些您不想放入 terraform 配置代码中的变量。
始终建议通过-var-file
在本地传递密码、密钥等变量,而不是将其保存在 terraform 配置或远程位置版本控制系统中。
例如,如果您想使用 terraform 启动 ec2 实例,您可以使用-var-file
传递访问密钥和其他机密信息。创建文件 terraform.tfvars
并将密钥放入此文件中。
geekflare@geekflare:~$ gedit terraform.tfvars
access_key = "AKIATYWSDFYU5DUDJI5F"
secret_key = "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"
然后在 terraform 命令中使用这个 var 文件。
geekflare@geekflare:~$ terraform apply -var-file=/home/geekflare/terraform.tfvars
当您运行 CI/CD 流水线构建作业时,建议使用 docker 容器。Terraform 提供了可以直接使用的官方 Docker 容器。如果需要更改 CI/CD 服务器,您可以轻松地在容器内传递基础设施。
在生产环境上部署基础设施之前,您还可以在 docker 容器上测试基础设施,非常容易部署。通过结合 Terraform 和 Docker,您可以获得可移植、可重用、可重复的基础设施。