五分钟入门阿里云Terraform OSS Backend
本文将介绍Terraform的Backend机制及如何使用Terraform OSS Backend。
Terraform State简介
Terraform State是用来存放基础设施资源及其属性和状态的机制。Terraform State从存储形态上分为两种:
local:本地存储
资源状态存放在本地的一个state文件中,默认为执行目录下的名为terraform.tfstate文件。此方式也是Terraform默认的存储形式。
remote:远端存储
资源状态存放在远端的一个服务中,例如阿里云的OSS、Terraform Cloud、HashiCorp Consul等。远端存储带来的好处是实现了与资源定义模板管理的解耦,可以让State脱离本地磁盘而存储,在提升State安全性的同时,团队协作可以不再受制于Terraform的执行环境、执行目录和多人执行时间的限制,提升了管理的灵活性。
Terraform State的存储是由一个称之为Backend的组件决定的,local state使用的是local backend。除了local backend,其他所有的backend在使用之前都需要在模板中显式定义并通过terraform init来实现加载和配置。
Terraform Backend简介
Backend是存储State的机制,它决定了State数据的加载逻辑和Terraform命令执行之后State的数据的更新过程。Terraform生命周期中与Backend的交互过程,如下图所示。
Terraform通过init命令完成Backend的加载和配置。在执行plan和apply命令,加载资源模板的同时,通过Backend加载State中的存量数据(如果有),命令结束后,将Provider或Provisioner响应中的数据通过Backend更新到State中,最终达到State数据与模板定义的一致。
从功能实现级别的角度,Backend可以分为两种:
Standard
State管理的标准化实现,覆盖标准的功能点State存储和State加锁。这种Backend目前只适用于在本地系统上运行所有操作的场景,尽管也实现了对State的远程存储,但Backend的执行逻辑仍发生在本地并通过直接调用API请求来完成。
目前这种Backend总共有13种,阿里云的OSS Backend也属于此类。
Enhanced
可以看作是Standard的加强版,即Backend的执行逻辑不仅可以发生在本地,还可以通过API或者CLI的方式发生在远端。目前这种Backend的种类有两种,一是local,另一个是只支持Terraform Cloud的remote 。
阿里云也提供了一个标准的Backend-oss ,在Terraform 0.12.2中予以支持,并在Terraform 0.12.6 和0.12.8版本中对OSS进行了升级,支持profile设置、ecs_role_name、assume_role等鉴权方式。
阿里云OSS Backend详解
OSS Backend是基于阿里云的表格存储服务(Tablestore)和对象存储服务(OSS)实现的Standard Backend,其中Tablestore用来存储运行过程中产生的“Locking”,保证State的正确性和完整性;OSS用来存储最终的State文件。接下来将详细介绍OSS Backend。
工作原理
OSS Backend的工作流程可以分为加锁、存储State、释放锁三步。下图是一个简单的工作流程图:

主要包含以下几个部分:
运行Terraform命令后,Backend首先会从Tablestore中获取LockID,如果已经存在,表明State被损坏或者有人正在操作,返回报错,否则,自动生成一个LockID并存储在Tablestore中。
如果是init命令,首次会生成一个新的state文件并存储在OSS的特定目录下,并释放LockID。
如果是plan、apply、destroy等涉及到修改State的命令,会在命令结束后将最新的数据同步更新到State文件中,并释放LockID。
如果是 state、show 等不涉及修改的操作,会直接读取State内容并返回。
模板定义
和Provider和Provisioner一样,Backend在使用时同样需要在模板中定义。Backend 通过关键字backend来声明
如下代码声明了一个oss backend,其state存储在名为terraform-oss-backend-1024的bucket中,对应的文件为prod/terraform.tfstate,并声明state文件为只读和加密;锁信息存储在一个名为terraform-oss-backend-1024的表格中,这个表格位于杭州的Tablestore实例tf-oss-backend中。
terraform { backend "oss" { profile = "terraform" bucket = "terraform-oss-backend-1024" key = "prod/terraform.tfstate" tablestore_endpoint = "https://tf-oss-backend.cn-hangzhou.Tablestore.aliyuncs.com" tablestore_table = "terraform-oss-backend-1024" acl = "private" encrypt = true ... } }对backend的定义包含如下几个部分:
Terraform为运行主体,定义了Backend的操作主体。Backend的逻辑实现是存放在Terraform仓库中的,服务于所有Provider和Provisioner,因此它的运行主体是Terraform ,而不是具体某个Provider。
oss 为Backend类型,用来标识一个特定的Backend。
大括号里面的内容为参数配置,用来定义Backend属性,例如鉴权信息,OSS Bucket的名称,存放路径,Tablestore配置信息等。更多参数和含义可参考官方文档:https://www.terraform.io/docs/backends/types/oss.html
权限配置
使用 OSS Backend 前,需要为执行 Terraform 的 RAM 用户或角色配置相应的权限。根据使用场景不同,所需权限也不同。
场景一:使用已有的 OSS Bucket 和 OTS 实例及表
如果您已有可用的 OSS Bucket 和 OTS 资源,只需配置 Backend 指向这些存量资源,此时所需权限较小,仅需 OSS 的读写权限和 OTS 的读写权限。
参考 RAM 自定义策略如下,OTS为自定义权限策略,详情参阅通过RAM Policy为RAM用户授权
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:GetObject", "oss:PutObject", "oss:DeleteObject", "oss:ListObjects", "oss:GetBucketInfo" ], "Resource": [ "acs:oss:*:*:your-bucket-name", "acs:oss:*:*:your-bucket-name/*" ] }, { "Effect": "Allow", "Action": [ "ots:GetRow", "ots:PutRow", "ots:UpdateRow", "ots:DeleteRow" ], "Resource": [ "acs:ots:*:*:instance/your-ots-instance-name/table/your-ots-table-name" ] } ] }场景二:需要新建 OSS Bucket 和 OTS 实例及表
如果您尚未创建用于存储 State 的 OSS Bucket 和 OTS 资源,则需要授予 OSS 和 OTS 的资源的创建与管理权限。
参考 RAM 自定义策略如下:
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:PutBucket", "oss:PutBucketAcl", "oss:PutBucketEncryption", "oss:GetObject", "oss:PutObject", "oss:DeleteObject", "oss:ListObjects", "oss:GetBucketInfo", "oss:DeleteBucket", "oss:ListBuckets" ], "Resource": [ "acs:oss:*:*:*" ] }, { "Effect": "Allow", "Action": "ots:*", "Resource": "*" } ] }重要资源创建完成后,请务必将权限收敛为场景一中的最小权限策略,仅保留对特定 Bucket 和 OTS 表的读写权限,以符合最小权限原则。
一键生成OSS Backend模板
为了更快捷的编写OSS Backend模板,阿里云提供了一个Terraform Module:remote-backend
说明当前示例代码支持一键运行,您可以直接运行代码。一键运行
terraform { required_providers { alicloud = { # 在使用 module 时,需要使用 aliyun 源 source = "aliyun/alicloud" } } } variable "region" { default = "cn-hangzhou" } provider "alicloud" { region = var.region } resource "random_integer" "default" { min = 10000 max = 99999 } module "remote_state" { source = "terraform-alicloud-modules/remote-backend/alicloud" #设置为true时,若已存在 OSS 存储桶,则使用该存储桶;否则系统将创建一个存储桶。 create_backend_bucket = true create_ots_lock_instance = true # 注意,为了避免OTS实例名称的冲突,此处需要指定自己的OTS Instance名称 # 如果指定的OTS Instance已经存在,那么需要设置 create_ots_lock_instance = false backend_ots_lock_instance = "ots-i-${random_integer.default.result}" create_ots_lock_table = true # 注意,如果想要自定义OTS Table或者使用已经存在的Table,可以通过参数backend_ots_lock_table来指定 # 如果指定的OTS Table已经存在,那么需要设置 create_ots_lock_table = false # backend_ots_lock_table = "your-ots-table-name" region = var.region state_name = "prod/terraform.tfstate" encrypt_state = true }运行完成后,会在当前目录下生成一个名为terraform.tf的配置文件,文件内容即为已经配置好的OSS Backend:
terraform { backend "oss" { bucket = "terraform-remote-backend-94a22ee-0714-e8ef-8573-21df8b021f86" prefix = "env:" key = "new/terraform.tfstate" acl = "private" region = "cn-hangzhou" encrypt = "true" tablestore_endpoint = "https://your-ots-instance-name.cn-hangzhou.Tablestore.aliyuncs.com" tablestore_table = "terraform_remote_backend_lock_table_38001042_0714_e8ef_8573_21df8b021f86" } }生成后的terraform.tf文件可以直接跟目标模板放在同一个目录下,以用于后续State的远端存储。
如果想把生成terraform.tf文件的
state也存放在上述的OSS Backend中,只需再次运行terraform init命令,本地目录下的local state将会自动同步到OSS Backend中。