Terraform入门操作指南
Terraform是什么?
Terraform
是一个可快速部署、方便管理IT基础架构配置的工具,它的理念是Infrastructure as Code,一切资源都是代码。如虚拟机、网络、DNS等,这些都通过代码来管理部署,而不是人工手动的去创建、删除等。它能大大减少人为操作的风险,能快速部署多套环境,适应多种硬件资源,特别适合云环境:AWS、GCP、Azure、阿里云等。
它通过丰富的Providers来管理多种类型的资源,就像是插件一样,如GCP、Docker、Kubernetes等。
Terraform
提供了对资源和提供者的灵活抽象。该模型允许表示从物理硬件、虚拟机和容器到电子邮件和 DNS 提供者的所有内容。由于这种灵活性,Terraform 可以用来解决许多不同的问题。这意味着有许多现有的工具与Terraform 的功能重叠。但是需要注意的是,Terraform 与其他系统并不相互排斥。它可以用于管理小到单个应用程序或达到整个数据中心的不同对象。
Terraform
使用配置文件描述管理的组件(小到单个应用程序,达到整个数据中心)。Terraform 生成一个执行计划,描述它将做什么来达到所需的状态,然后执行它来构建所描述的基础结构。随着配置的变化,Terraform 能够确定发生了什么变化,并创建可应用的增量执行计划。
Terraform 核心功能
- 基础架构即代码(Infrastructure as Code)
- 执行计划(Execution Plans)
- 资源图(Resource Graph)
- 自动化变更(Change Automation)
- 基础架构即代码(Infrastructure as Code)
- 使用高级配置语法来描述基础架构,这样就可以对数据中心的蓝图进行版本控制,就像对待其他代码一样对待它。
核心概念
- Variables:也被称为 input-variables(输入变量),它是 Terraform 模块使用的键值对,可以自定义。
- Provider:一种插件类型,与 API 服务进行交互并访问相关资源。
- Module:它是一个包含 Terraform 模板的文件夹,所有的配置都可以在这里定义。
- State:它由 Terraform 管理的基础设施和相关配置的缓存信息组成。
- Resources:它指一个或多个基础设施对象(计算实例、虚拟网络等)的块(block),这些对象用于配置和管理基础设施。
- Data Source:它是由 provider 实现的,以返回外部对象的信息到 Terraform。
- Output Values:这是 Terraform 模块的返回值,可以被其他配置使用。
- Plan:这是指其中一个阶段,在这一阶段中会决定需要创建、更新或销毁什么,以便从基础设施的 real/current 状态转移到期望状态。
- Apply:这一阶段会应用基础设施的更改 real/current 状态,以推动到期望状态
执行计划(Execution Plans)
Terraform 有一个 plan 步骤,它生成一个执行计划。执行计划显示了当执行 apply 命令时 Terraform 将做什么。通过 plan 进行提前检查,可以使 Terraform 操作真正的基础结构时避免意外。
资源图(Resource Graph)
Terraform 构建的所有资源的图表,它能够并行地创建和修改任何没有相互依赖的资源。因此,Terraform 可以高效地构建基础设施,操作人员也可以通过图表深入地解其基础设施中的依赖关系。
自动化变更(Change Automation)
把复杂的变更集应用到基础设施中,而无需人工交互。通过前面提到的执行计划和资源图,我们可以确切地知道 Terraform 将会改变什么,以什么顺序改变,从而避免许多可能的人为错误。
架构原理
生命周期
图中立方体所示分别为:
Terraform 核心进程
:负责资源定义文件,构建有向无环图,管理状态存储;
Provider 进程
:即提供资源编排能力的进程,包括由云厂商实现的能力(比如 UCloud),和应用程序提供的能力(比如 TLS)等;
Provisioner 进程
:即提供资源编排后处理操作的进程,比如执行 Shell 命令,上传文件等;
以中央的有向无环图为分界线,左侧的部分是 Terraform 本身提供的能力,右侧是由云厂商提供的能力。
当执行 Terraform 命令首次编排云上资源时:
-
Terraform 首先唤醒核心进程,初始化 Backend(即状态管理组件);
-
解析用户编写的资源定义文件,同步最新的资源状态,并与当前的资源定义作对比;
-
初次构建 DAG 时,资源尚未被初始化,所以资源状态为空,用户的资源实例都将作为 DAG 中新增的节点被创建。
在并行构建资源时:
-
并行遍历 DAG;
-
当遇到 Provider 节点时,Terraform 核心进程唤醒 UCloud Provider 进程;
-
将所有的编排动作依次发给 UCloud Provider;
-
Provider 调用 UCloud OpenAPI 管理云上资源;
-
返回的结果由 Terraform 核心进程写回状态存储。
进程管理
Terraform
中每一个云厂商的实现(Provider)都是一个独立的进程,进程间使用 RPC 通信的方式下发指令和交换数据,这样设计有什么好处呢?
-
安全性:多云环境下,进程隔离云厂商的实现,防止共享内存带来的安全性问题。
-
扩展性:插件式的设计使得特性的增加更加容易,而官方插件仓库使得特性的质量更有保障。
-
稳定性:核心与插件分离,保证了核心简单可靠,测试充分。单一插件的 Bug 不会扩散到全局。
而从使用者的角度来看,Terraform 多进程模型的重中之重,是多云环境下厂商隔离带来的安全性问题。安全性是多云编排的基石,如果无法保证云厂商之间的隔离性和安全性,多云编排则无从谈起。
Terraform 使用插件(Plugin = Provider + Provisioner)来抽象出各个云厂商之间的差异,并相互隔离。
在一次编排任务的生命周期中,Provider 将会基于 Terraform 提供的能力,完成静态检查(Validate)、资源状态同步(Read/Refresh)、生成执行计划(Plan)、执行编排(Apply)等操作。
依赖管理
软件工程的实践表明,高层次的抽象,可以简化问题,让复杂的问题变得可以测试。而对于依赖关系的抽象,业界最通行的做法即使用有向无环图(DAG,Directed Acyclic Graph)来描述事务间的依赖关系。有向无环图上的点即事物本身,边则是事物与事物之间的联系。
业界对于 DAG 的使用极为广泛,比较典型的是各种大数据工作流引擎,比如 Oozie,Airflow 等。在这些引擎中,批处理任务作为 DAG 上的节点,而任务间的依赖作为 DAG 上的边。
状态管理
Terraform
引入了面向资源的设计,将资源的状态描述为一个状态的集合,并支持若干种不同类型的状态存储。
默认情况下,在 Terraform 的执行目录下,会存储一个本地的资源状态文件,并在每次编排开始时,从远程同步状态到本地,比较该状态与用户定义的资源之间的差异,从而生成编排计划。
在这一抽象中,Terraform 官方给出了几个基本的定义:
从上文中的定义可以看出,执行计划(Plan)本质上就是 Diff 格式化输出的结果,而执行编排就是应用这个 Diff 的过程
Backend Terraform 将对资源状态的管理抽象出了一个统一的状态管理层(Backend),使得基于 Terraform 的资源编排系统可以保持基础设施的一致性。
Terraform Backend 通过对状态加锁来解决资源的竞态问题。A 在操作资源的时候状态会被锁定,此时 B 执行的任何变更行为都将被拒绝。
其中,consul、etcd 和 http 是比较推荐的扩展: consul、etcd 提供了锁机制,且基于 Raft 协议保证了数据的强一致性;
Terraform 对 Backend 的抽象增强了状态存储的可扩展性,同时提供了可选的锁机制扩展,基于此云厂商可以定义自己的远程状态存储,用于托管用户的资源状态,并为用户提供可靠的并发安全保障。
时效性
Terraform 如何保证已经失效的执行计划不再被执行?Terraform 使用多版本快照(Multi-Version Snapshot)的方式来实现。可以类比于常规的 MVCC(多版本并发控制)来理解,下图是一个最小化的 MVCC 实现:
进程 P1 和 P2 依次读到了序号为 1 的数据,并且都想进行写操作,P1 先修改数据,自增序号为 2 并写入成功,此时 P2 进行写操作时,由于修改后的序号同样为 2,此时应抛出写失败,P2 需要主动重新读取最新的数据再次修改,才能成功写入。
由于 Terraform 可以执行一个已导出的执行计划,一个事务的时间被极大延长了,所以版本冲突的可能被无限放大。
基于此,Terraform 同样选择该方式,通过一个序号来标识状态的版本,当执行计划的状态序号小于当前状态的序号时,直接丢弃过时的执行计划:
基于这样的原理,Terraform 保证了导出的执行计划是有时效性的。例如一个用户导出了一份执行计划,将云主机从 1 台水平扩容到 3 台,但在该执行计划审查通过之前,另一个用户已经扩容到 5 台云主机,此时这份执行计划执行时会 Abort,而不会从 5 台降为 3 台,从而保证关键基础设施的变更是安全的。
下载安装
根据系统平台,选择不同的系统版本,进行下载,本文选用windows系统版本,下载解压后,加入系统环境变量即可。
查看帮助命令
|
|
Terraform 关键概念
Configuration:基础设施的定义和描述
“基础设施即代码(Infrastructure as Code)”,这里的Code就是对基础设施资源的代码定义和描述,也就是通过代码表达我们想要管理的资源
|
|
对所有资源的代码描述都需要定义在一个以 tf 结尾的文件用于Terraform加载和解析,这个文件我们称之为“Terraform模板”或者“Configuration”
Provider:基础设施管理组件
Terraform 通常用于对云上基础设施,如虚拟机,网络资源,容器资源,存储资源等的创建,更新,查看,删除等管理动作,也可以实现对物理机的管理,如安装软件,部署应用等。
【Provider】 是一个与Open API直接交互的后端驱动,Terraform 就是通过Provider来完成对基础设施资源的管理的。不同的基础设施提供商都需要提供一个Provider来实现对自家基础设施的统一管理。目前Terraform目前支持超过160多种的providers,大多数云平台的Provider插件均已经实现了,阿里云对应的Provider为 alicloud 。
Terraform目前支持超过160多种的providers,可以说只要人听过的云都能支持,主流的如AWS、GCP、OpenStack等,国内的阿里云、腾讯云、Ucloud以及OpenStack系的华为云、京东云等。
除了公有云,私有云如Oracle、Vmware的支持也都完全没有问题。同时也支持目前主流的PaaS平台,如Kubernetes、Helm、Rancher2等,基本不需要再造轮子直接用就完了
Provider在Terraform中以插件的形式加载,在init阶段Terraform会自动下载所需要的所有Provider插件。
在操作环境中,Terraform和Provider是两个独立存在的package,当运行Terraform时,Terraform会根据用户模板中指定的provider或者resource/datasource的标志自动的下载模板所用到的所有provider,并将其放在执行目录下的一个隐藏目录 .terraform 下。
|
|
模板中显示指定了一个阿里云的Provider,并显示设置了provider的版本为 1.56.0+ (默认下载最新的版本),指定了需要管理资源的region,指定了当前这个模板的标识。 通常Provider都包含两个主要元素 resource 和 data source。
如上运行teraform init时会自动下载Provider aws插件。
Resource:基础设施资源和服务的管理
Resource是Terraform的主角,开发者大多数工作都是和Resource打交道,云平台中所有的资源都可以抽象为Terraform中的一个Resource实例。
定义一个Resource的语法非常简单,通过 resource 关键字声明,如下:
|
|
其中 alicloud_instance 为资源类型(Resource Type),定义这个资源的类型,告诉Terraform这个Resource是阿里云的ECS实例还是阿里云的VPC。 default 为资源名称(Resource Name),资源名称在同一个模块中必须唯一,主要用于供其他资源引用该资源。 大括号里面的block块为配置参数(Configuration Arguments),定义资源的属性,比如ECS 实例的规格、镜像、名称等。
显然这个Terraform模板的功能为在阿里云上创建一个ECS实例,镜像ID为 ubuntu_16_04_64_20G_alibase_20190620.vhd ,规格为 ecs.sn1ne.large ,自定义了实例名称和系统盘的类型。
除此之外,在Terraform中,一个资源与另一个资源的关系也定义为一个资源,如一块云盘与一台ECS实例的挂载,一个弹性IP(EIP)与一台ECS或者SLB实例的绑定关系。这样定义的好处是,一方面资源架构非常清晰,另一方面,当模板中有若干个EIP需要与若干台ECS实例绑定时,只需要通过Terraform的 count 功能就可以在无需编写大量重复代码的前提下实现绑定功能。
|
|
显然这个Terraform模板的功能为在阿里云上创建5个ECS实例和5个弹性IP,并将它们一一绑定。
Data Source:基础设施资源和服务的查询
对资源的查询是运维人员或者系统最常使用的操作,比如,查看某个region下有哪些可用区,某个可用区下有哪些实例规格,每个region下有哪些镜像,当前账号下有多少机器等,通过对资源及其资源属性的查询可以帮助和引导开发者进行下一步的操作。
除此之外,在编写Terraform模板时,Resource使用的参数有些是固定的静态变量,但有些情况下可能参数变量不确定或者参数可能随时变化。比如我们创建ECS 实例时,通常需要指定我们自己的镜像ID和实例规格,但我们的模板可能随时更新,如果在代码中指定ImageID和Instance,则一旦我们更新镜像模板就需要重新修改代码。 在Terraform 中,Data Source 提供的就是一个查询资源的功能,每个data source实现对一个资源的动态查询,Data Souce的结果可以认为是动态变量,只有在运行时才能知道变量的值。
Data Sources通过 data 关键字声明,如下:
|
|
如上例子中的ECS Instance 没有指定镜像ImageID和实例规格,而是通过 data引用,Terraform运行时将首先根据镜像名称前缀选择系统镜像,如果同时有多个镜像满足条件,则选择最新的镜像。实例规格也是类似,在某个可用区下选择2核4G的实例规格进行返回。
State:保存资源关系及其属性文件的数据库
Terraform创建和管理的所有资源都会保存到自己的数据库上,这个数据库不是通常意义上的数据库(MySQL,Redis等),而是一个文件名为 terraform.tfstate 的文件,在Terraform 中称之为 state ,默认存放在执行Terraform命令的本地目录下。这个 state 文件非常重要,如果该文件损坏,Terraform 将认为已创建的资源被破坏或者需要重建(实际的云资源通常不会受到影响),因为在执行Terraform命令是,Terraform将会利用该文件与当前目录下的模板做Diff比较,如果出现不一致,Terraform将按照模板中的定义重新创建或者修改已有资源,直到没有Diff,因此可以认为Terraform是一个有状态服务。
当涉及多人协作时不仅需要拷贝模板,还需要拷贝 state 文件,这无形中增加了维护成本。幸运的是,目前Terraform支持把 state 文件放到远端的存储服务 OSS 上或者 consul 上,来实现 state 文件和模板代码的分离。具体细节可参考官方文档Remote State或者关注后续文章的详细介绍。
Backend:存放 State 文件的载体
正如上节提到,Terraform 在创建完资源后,会将资源的属性存放在一个 state 文件中,这个文件可以存放在本地也可以存放在远端。存放 state 文件的载体就是 Backend 。 Backend 分为本地(local)和远端(remote)两类,默认为本地。远端的类型也非常多,目前官方网站提供的有13种,并且阿里云的OSS就位列其中。
使用远端的Backend,既可以降低多人协作时对state的维护成本,而且可以将一些敏感的数据存放在远端,保证了数据的安全性。
Provisioner:在机器上执行操作的组件
Provisioner 通常用来在本地机器或者登陆远程主机执行相关的操作,如 local-exec provisioner 用来执行本地的命令, chef provisioner 用来在远程机器安装,配置和执行chef client, remote-exec provisioner 用来登录远程主机并在其上执行命令。
Provisioner 通常跟 Provider一起配合使用,provider用来创建和管理资源,provisioner在创建好的机器上执行各种操作。
变量
Terraform 运行时会读取工作目录中所有的 *.tf, *.tfvars 文件,所以我们不必把所有的东西都写在单个文件中去,应按职责分列在不同的文件中,例如:
provider.tf – provider 配置
terraform.tfvars – 配置 provider 要用到的变量
varable.tf – 通用变量
resource.tf – 资源定义
data.tf – 包文件定义
output.tf – 输出
Terraform引用了一些环境变量来控制部分功能,这些环境变量都不是必需的,但是可以改变一些Terraform的默认行为,帮助用户适配更多应用场景
|
|
Terraform中输入变量
variable
是Terraform
重要的配置文件类型之一,通过对变量的集中管理,用户可以在资源文件中直接引用变量名进行赋值。首先需要先定义(声明)变量,放到一个.tf文件中,如:
创建variable.tf
文件,配置参数的默认值
|
|
上面定义了变量。前两个变量是空的,第三个给了一个默认值(默认参数)。此时运行terraform plan,Terraform会提示输入这些尚未定义的变量。
引用变量,使用${var.xxx}的形式。这样在资源配置文件中,参数可以直接调用var.f5user
|
|
变量赋值
前面我们声明了变量,但是还没有给变量赋值,无法真正使用。给变量赋值,有以下几种方法,下面几种方法按照变量赋值的优先顺序排序。
使用terraform的各种命令时,使用-var选项,可以在后面直接跟变量的定义,如:
# terraform apply \ -var 'access_key=foo' -var 'secret_key=bar' # ...
以这种方式赋值变量是一次性的,并不会保存它们的值,也就是说下一次重新执行命令时,需要重新赋值。
在terraform apply中,直接设置变量值会覆盖掉variable.tf中设置的默认值
为永久性存储一个变量的值,可以将其放在文件中保存。Terraform会自动加载当前目录下扩展名为.tfvars和.auto.tfvars的文件来填充定义的变量。如果以其他格式存放,可以使用-var-file选项来手动指定需要加载的变量值文件。这些文件使用Terraform格式或JSON格式。
使用文件也方便版本控制,但是用户名、密码这种东西就不要用版本控制管理的。因此可以将用户名和密码这类信息单独放在一个文件中,使用-var-file来手动指定。其他的,可以自动填充,方便使用版本控制管理的,可以直接放在.tfvars文件中,Terraform会自动加载。
Terraform会读取TF_VAR_name这种格式的环境变量,用来填充定义好的变量。比如,环境变量中有一个TF_VAR_access_key的变量,Terraform就会读取到,并用于填充access_key变量。
如果某个变量没有采用以上任何一种方法来进行赋值,那么如果在变量的定义中有个default属性,那么Terraform就会使用default的值来对变量进行赋值。
没有使用任何方法来对变量赋值,在输入命令时使得Terraform不知道如何处理,此时就会出现交互界面,让用户手动输入变量值,来给变量赋值。
也可以利用TF_VAR_name把变量设置在环境变量中
export TF_VAR_f5user=“admin” 配置TF_INPUT,可以关闭对未指定值的变量的提示。将刚才的variable.tf中设置的参数删除
|
|
执行Terraform指令,会要求写入参数值, 设置TF_INPUT为false或0,再次执行指令,系统报错:未指定变量的值
|
|
CLI Config File
用户可以通过CLI的配置文件对CLI进行一些设置,适用于所有Terraform的工作目录,与资源配置文件是区分开的。
配置文件中支持的参数有:
-
是否开启更新与安全检查:disable_checkpoint
-
允许更新与安全检查,但禁止使用匿名id删除警告消息:disable_checkpoint_signature
-
启用插件缓存,以字符串的形式指定插件缓存目录的位置:plugin_cache_dir
-
Terraform企业版凭证:credentials
可以在环境变量中配置CLI Config File的位置
|
|
资源管理常用命令
terraform plan:资源的预览
在初始化目录下执行 terraform plan 查看部署计划;参数前面的+代表新添加的资源,当销毁资源时,参数前面对应的符号会变为-;更改一些参数需要重新部署资源时,该资源前面的符号为-/+;在旧参数和新参数内容之间有→符号标识。
plan命令用于对模板中所定义资源的预览,主要用于以下几个场景:
预览当前模板中定义的资源是否符合管理预期,和Markdown的预览功能类似。 如果当前模板已经存在对应的state文件,那么plan命令将会展示模板定义与state文件内容的diff结果,如果有变更,会将结果在下方显示出来。 对DataSource而言,执行plan命令,即可直接获取并输出所要查询的资源及其属性。
terraform apply:资源的新建和变更
apply命令用于实际资源的新建和变更操作,为了安全起见,在命令运行过程中增加了人工交互的过程,即需要手动确认是否继续,当然也可以通过–auto-approve参数来跳过人工确认的过程。
apply命令适用于以下几种场景:
创建新的资源。 通过修改模板参数来修改资源的属性。 如果从当前模板中删除某个资源的定义,apply命令会将该资源彻底删除。可以理解为“资源的移除也是一种变更”。
terraform show:资源的展示
show命令用于展示当前state中所有被管理的资源及其所有属性值。
terraform destroy:资源的释放
destroy命令用于对资源的释放操作,为了安全起见,在命令执行过程中,也增加了人工交互的过程,如果想要跳过手动确认操作,可以通过–force参数来跳过。
terraform destroy默认会释放当前模板中定义的所有资源,如果只想释放其中某个特定的资源,可以通过参数-target=<资源类型>.<资源名称>来指定。
terraform import:资源的导入
import命令用于将存量的云资源导入到terraform state中,进而加入到Terraform的管理体系中
terraform taint:标记资源为被污染
taint命令用于把某个资源标记为被污染状态,当再次执行apply命令时,这个被污染的资源将会被先释放,然后再创建一个新的,相当于对这个特定资源做了先删除后新建的操作。
命令的详细格式为: terraform taint <资源类型>.<资源名称> ,如:
terraform taint bigip_ltm_pool_attachment.attach_nodeqat01Resource instance bigip_ltm_pool_attachment.attach_nodeqat01 has been marked as tainted.
terraform untaint:取消被污染标记
untaint命令是taint的逆向操作,用于取消被污染标记,使其恢复到正常的状态。命令的详细格式和taint类似为:terraform untaint <资源类型>.<资源名称> ,如:
terraform untaint untaint bigip_ltm_pool_attachment.attach_nodeqat01Resource instance bigip_ltm_pool_attachment.attach_nodeqat01 has been successfully untainted. terraform output:打印出参及其值
如果在模板中显示定义了output参数,那么这个output的值将在apply命令之后展示,但plan命令并不会展示,如果想随时随地快速查看output的值,可以直接运行命令 terraform output
Terraform对资源状态的管理,实际上是对State文件中数据的管理。State文件保存了当前Terraform管理的所有资源及其属性,内容都是由Terraform自动存储的,为了保证数据的完整性,不建议手动修改State内容。对State数据的操作可以通过terraform state命令来完成。
terraform state list:列出当前state中的所有资源
state list命令会按照 <资源类型>.<资源名称> 的格式列出当前state中存在的所有资源(包括datasource),例如:
|
|
目录结构说明
- main.tf 属于主配置文件,定义整个编排流程
- modules 属于模块,可引入单个模块
- output.tf 属于定义输出
- variable.tf 属于定义变量属性
例如modules/dns/main.tf
|
|
先取出domain相关数据,再提取部分信息,最后创建对应的资源