作者 | 何贵民 阿里云开放平台高级开发工程师 主要负责开源生态工具(Terraform,Ansible,Spinnaker)与阿里云的集成,专注于借助主流开源工具帮助企业上阿里云和云上运维提效。
摘要
开源生态工具Terraform通过简单的客户端命令即可实现对阿里云资源的创建,更改和删除等操作,但对于很多以非Terraform创建的云资源,是否有办法统一管理呢?本文将向你揭秘如何使用Terraform Import命令来实现对存量云资源的导入和统一管理。
前两篇文章《一分钟部署阿里云ECS集群》和《五分钟入门阿里云Terraform OSS Backend》分别向大家详细介绍了Terraform的一些基本概念,用法和State的工作原理。通过这两篇的介绍,相信大家一定可以感受到Terraform 在资源编排,多云管理,团队协作,简单易用等方面的优势,在持续提升企业上云效率,降低运维成本等方面的强大能力。本文将继续向大家介绍Terraform的一个高级功能-Terraform Import。
在开始阅读本文之前,先介绍四种运维场景:
场景一:我是Terraform的新人,从来没有使用Terraform管控过任何资源,当前所有的存量云资源都是通过控制台,阿里云CLI,ROS或者直接调用API创建和管理的,现在想要切换为Terraform,如何实现对这些存量资源的管理?
场景二:我们团队所有的云资源都是通过Terraform来管理的,某一天团队的新人通过控制台对其中某个云资源做了属性变更,导致原有的资源状态State不一致,该如何处理?
场景三:我的所有资源都定义在一个模板中,随着资源数的不断增多,模板和state的管理复杂度也在持续增大,现在想要对原有模板进行重构,将其进行拆分,如何在不影响资源正常使用的前提下实现?
场景四:阿里云的Provider进行了兼容性升级,新版Provider对原有模板中所定义的资源支持了更多的参数,如何把新加入的参数值同步进来?
面对以上四种甚至更多类似的场景,在阅读完本文后,都可以找到对应的解决方案。
Terraform基于资源模板定义不仅可以实现对新资源的创建,变更,删除等操作,还可以通过简单的命令将那些游离在Terraform管理体系之外的云资源进行导入和纳管,进而实现对所有云资源的统一管理。
1 Terraform 导入存量资源
Terraform对资源的导入可以分为三个部分:
- 获取资源ID
基于资源ID查询资源并获取其属性; - 模板声明所要导入的资源
模板驱动,即使是要导入的资源,也需要在模板中进行声明; - 补齐资源模板定义
导入成功后,需要根据资源属性补齐已经在模板中声明的资源定义。
1.1 获取资源ID
在Terraform中,每个被管理的资源有且仅有一个资源ID。Terraform对模板中所定义的资源完成创建之后,都会将所创建的资源ID存储到 Terraform State
中,并通过资源ID来精确地实现对特定资源的查找,变更,删除等持续地管理操作。
阿里云Provider所产生的资源ID通常有两种格式:阿里云后端系统自动生成的ID和阿里云Provider生成的ID。大部分的资源ID都属于前一种,即每个资源在通过阿里云API完成创建之后,系统都会自动生成一个资源的唯一标识符,Provider会将其直接作为资源ID保存在State文件中。第二种格式通常出现在资源关系所对应的 resource
中,如磁盘挂载 alicloud_disk_attachment
,EIP的绑定 alicloud_eip_association
等,这类资源的ID都是通过关系两端的资源ID和特殊字符 :
拼接起来的。在阿里云每个Resource文档最后的 Attribute Reference
部分都会显示当前资源的ID及其格式描述。
对资源ID的获取可以通过Web控制台,CLI,API等多种方式,最简单的方式是通过Terraform的DataSource,输入简单的查询条件,如获取一个负载均衡实例:
data "alicloud_slbs" "default" {
name_regex = "for-demo*"
}
output "slb_ids" {
value = data.alicloud_slbs.default.ids
}
运行 terraform apply
命令即可展示所有符合条件的SLB的ID:
$ terraform apply
data.alicloud_slbs.default: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
slb_ids = [
"lb-gw8vinrqxxxxxxxxxxx",
"lb-gw8axostxxxxxxxxxxx",
]
1.2 模板声明所要导入的资源
和创建资源一样,在导入资源前,也需要在模板中进行资源声明,以便指定所要导入的资源在State中的存放路径。如下所示,声明一个负载均衡实例:
resource "alicloud_slb" "this" {}
简单的声明之后,无需定义具体的参数即可开始资源的导入操作。在Terraform中,导入一个资源的操作通过 import
命令来完成,完整的命令格式为 terraform import <资源类型>.<资源标识> <资源ID>
,详细操作如下:
$ terraform import alicloud_slb.this lb-gw8vinrqxxxxxxxxxxx
alicloud_slb.this: Importing from ID "lb-gw8vinrqxxxxxxxxxxx"...
alicloud_slb.this: Import prepared!
Prepared alicloud_slb for import
alicloud_slb.this: Refreshing state... [id=lb-gw8vinrqxxxxxxxxxxx]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
1.3 补齐资源模板定义
由于模板中没有完成对所导入资源的详细定义,因此,资源导入成功后,模板内容与State存储的内容存在差异,此时如果直接运行 plan
命令,将会看到一个update:
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
data.alicloud_slbs.default: Refreshing state...
alicloud_slb.this: Refreshing state... [id=lb-gw8vinrqxxxxxxxxxxx]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# alicloud_slb.this will be updated in-place
~ resource "alicloud_slb" "this" {
address = "47.254.181.122"
...
~ delete_protection = "on" -> "off"
id = "id=lb-gw8vinrqxxxxxxxxxxx"
...
~ name = "for_demo-test" -> "tf-lb-20191108144235105700000001"
...
}
Plan: 0 to add, 1 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
为了保持资源模板与资源状态的一致,需要在模板中手动补齐缺失的参数定义,直到运行 plan
不会再有变更信息为止:
resource "alicloud_slb" "this" {
delete_protection = "on"
name = "for_demo-test"
}
所要补齐的内容主要以那些引起更新的字段为主,补齐完成后运行terraform plan
进行测试:
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
data.alicloud_slbs.default: Refreshing state...
alicloud_slb.this: Refreshing state... [id=lb-gw8vinrqtqx1ro1r94c96]
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
可以看到,此时已经没有任何需要变更的信息。至此完成了对一个资源的完整导入。
2 Terraform 移除存量资源
在实际操作场景中,经常会遇到资源的误导入,导入路径不符,想要调整资源路径,想要永久保留某资源等多种复杂情况。面对这些情况,整体的实现思路也是非常简单:先将导入后的资源从State中移除,然后重新导入。
资源的移除操作可以通过 state rm
命令来完成,完整的命令格式为 terraform state rm <资源类型>.<资源标识>
,详细操作如下:
$ terraform state rm alicloud_slb.this
Removed alicloud_slb.this
Successfully removed 1 resource instance(s).
state rm
命令只是将指定的资源从State文件中移除,并不会将其真正删除,这也正是为后续的导入操作做好了铺垫。
3 回顾使用场景
在阅读完前面两部分的内容后,再来回顾文章开始提到的四种场景,相信大家都已经找到了答案,本文做一个小结:
场景一的解决方案:不论是Terraform的新手还是“老司机”,都可以通过 terraform import
命令来完成对存量资源的导入,进而使用Terraform统一管理。
场景二的解决方案:在确定清楚参数属性的具体值之后,如果以模板参数值为准,那么只需要运行 apply
命令再变更回来即可;如果以控制台的值为准,那么只需要补充/修改模板参数值即可。
场景三的解决方案:可以先通过 terraform state rm
命令将所有需要重组的资源移出State,等模板重构结束后,再使用 terraform import
将其导入即可。
场景四的解决方案:和上一解决方案一样,通过“先移出再导入”调整一番即可。
4 写在最后
从如上的操作可以看出,Terraform的命令非常灵活和简单,基于模板和State一致性的原理,借助Terraform Import 可以轻松地实现对存量资源的统一管理,不用再担心那些游离在Terraform管理体系之外资源无法管理的痛点,也无需惧怕某个资源从State中移除后无法继续管理的问题,所有的云资源都可以被Terraform统一管理起来,感兴趣的同学赶快动手试试吧。