理解 Terraform 的状态管理
我对平时使用的Terraform的状态文件进行了一些深入理解,并将其总结在博客中。
国家的目标
对现实世界的映射
Terraform 的状态文件被用于实际部署的基础架构及其映射。据说最初的 Terraform 版本没有状态文件,而是使用标签进行管理,但由于某些云服务提供商并不支持此方式,所以很快就成为了一个问题。
terraform.tf -> terraform配置文件.tf
provider "azurerm" {
version = "=1.39.0"
}
terraform {
backend "azurerm" {
resource_group_name = "RemoveTerraform"
storage_account_name = "tsushistatetf"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
resource "azurerm_resource_group" "test" {
name = "testResourceGroup1-${terraform.workspace}"
location = "West US"
tags = {
environment = "${terraform.workspace} branch"
}
}
让我们来查看针对这个配置文件生成的状态文件。尽管它已经被销毁了,但是基础设施的标识符和其他具体数据,即状态仍然存在。通过这种方式,Terraform 可以了解定义的基础设施与实际基础设施的哪个实例相对应。
{
"version": 4,
"terraform_version": "0.12.18",
"serial": 5,
"lineage": "3822a78b-94a1-0b60-996a-aec909561c62",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "azurerm_resource_group",
"name": "test",
"provider": "provider.azurerm",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "/subscriptions/{MY_SUBSCRIPTION_ID}/resourceGroups/testResourceGroup1",
"location": "westus",
"name": "testResourceGroup1",
"tags": {
"environment": "test branch"
}
},
"private": "{something}"
}
]
}
]
}
元数据
除了映射之外,terraform 还具有保持状态文件的原因。它持有元数据,例如资源之间的依存关系。如果不了解依赖关系,在删除资源时无法正确执行删除操作。此外,还包括 Azure 和 AWS 等提供商的配置信息。
作为示例,我尝试将虚拟网络添加到上述的tf文件中。
terraform.tf的翻译可以是“土壤形态.tf”。
provider "azurerm" {
version = "=1.39.0"
}
terraform {
backend "azurerm" {
resource_group_name = "RemoveTerraform"
storage_account_name = "tsushistatetf"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
resource "azurerm_resource_group" "test" {
name = "testResourceGroup1-${terraform.workspace}"
location = "West US"
tags = {
environment = "${terraform.workspace} branch"
}
}
resource "azurerm_virtual_network" "test" {
name = "virtualNetwork1"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
address_space = ["10.0.0.0/16"]
tags = {
environment = "${terraform.workspace} branch"
}
}
在TF文件中没有明确指定,但添加了依赖项。
{
"version": 4,
"terraform_version": "0.12.18",
"serial": 2,
"lineage": "cf4a560a-1759-63d1-7b4b-6ecae95fe7c3",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "azurerm_resource_group",
"name": "test",
"provider": "provider.azurerm",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one",
"location": "westus",
"name": "testResourceGroup1-pr-branch-one",
"tags": {
"environment": "pr-branch-one branch"
}
},
"private": "{something}"
}
]
},
{
"mode": "managed",
"type": "azurerm_virtual_network",
"name": "test",
"provider": "provider.azurerm",
"instances": [
{
"schema_version": 0,
"attributes": {
"address_space": [
"10.0.0.0/16"
],
"ddos_protection_plan": [],
"dns_servers": null,
"id": "/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1",
"location": "westus",
"name": "virtualNetwork1",
"resource_group_name": "testResourceGroup1-pr-branch-one",
"subnet": [],
"tags": {
"environment": "pr-branch-one branch"
}
},
"private": "{something}",
"dependencies": [
"azurerm_resource_group.test"
]
}
]
}
]
}
表演
状态文件保存着资源属性的缓存。当执行terraform plan或terraform apply时,默认行为是向API发出查询以确认当前部署的资源。当基础设施规模庞大时,这些查询可能成为问题。在云提供商中,通常对每个时间段的API请求有限制,而且很少有一次性获取所有资源响应的API。对于大型基础设施,这可能成为问题。在这种情况下,可以使用-refresh=false和-target标志。顺便提一下,-target选项用于限定要部署的资源,-refresh选项用于从当前基础设施更新状态文件,默认值为true。
同时
默认情况下,Terraform将在本地工作目录中保存状态文件,但在团队合作时,希望共享状态信息。此时,可以使用远程状态,例如将状态保存在存储账户中以便进行共享。
检查和更新 hé
建议使用terraform state子命令,虽然状态文件是Json格式的文件,但不建议手动更新。
让我们考虑将上述虚拟网络部分移动到一个模块的情况,并尝试对tf文件进行重构。创建一个名为”foo”的子目录,然后创建以下两个文件。
foo/variables.tf 可以被改写为:
foo/变量.tf
variable location {
description = "Location of the Virtual Net"
}
variable resource_group_name {
description = "Resource Group Name for the virtual net"
}
virtualnet.tf模块中的foo
resource "azurerm_virtual_network" "test" {
name = "virtualNetwork1"
location = var.location
resource_group_name = var.resource_group_name
address_space = ["10.0.0.0/16"]
tags = {
environment = "${terraform.workspace} branch"
}
}
terraform.tf 文件
provider "azurerm" {
version = "=1.39.0"
}
terraform {
backend "azurerm" {
resource_group_name = "RemoveTerraform"
storage_account_name = "tsushistatetf"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
resource "azurerm_resource_group" "test" {
name = "testResourceGroup1-${terraform.workspace}"
location = "West US"
tags = {
environment = "${terraform.workspace} branch"
}
}
module "virtualnet" {
source = "./foo"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
}
当以这种方式进行重构并尝试执行terraform plan。然后,虽然不应该更改已部署的VirtualNet,但它被删除并重新生成了。
$ 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.
azurerm_resource_group.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one]
azurerm_virtual_network.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
# azurerm_virtual_network.test will be destroyed
- resource "azurerm_virtual_network" "test" {
- address_space = [
- "10.0.0.0/16",
] -> null
- dns_servers = [] -> null
- id = "/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1" -> null
- location = "westus" -> null
- name = "virtualNetwork1" -> null
- resource_group_name = "testResourceGroup1-pr-branch-one" -> null
- tags = {
- "environment" = "pr-branch-one branch"
} -> null
}
# module.virtualnet.azurerm_virtual_network.test will be created
+ resource "azurerm_virtual_network" "test" {
+ address_space = [
+ "10.0.0.0/16",
]
+ id = (known after apply)
+ location = "westus"
+ name = "virtualNetwork1"
+ resource_group_name = "testResourceGroup1-pr-branch-one"
+ tags = {
+ "environment" = "pr-branch-one branch"
}
+ subnet {
+ address_prefix = (known after apply)
+ id = (known after apply)
+ name = (known after apply)
+ security_group = (known after apply)
}
}
Plan: 1 to add, 0 to change, 1 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.
在这种情况下,我试着进行状态的改变。
$ terraform state mv 'azurerm_virtual_network.test' 'module.virtualnet.azurerm_virtual_network.test'
Move "azurerm_virtual_network.test" to "module.virtualnet.azurerm_virtual_network.test"
Successfully moved 1 object(s).
然后,将资源移动到模块中后,它会被识别为当前资源。在更改状态后,再次执行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.
azurerm_resource_group.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one]
module.virtualnet.azurerm_virtual_network.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1]
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
现在它能够正确地识别为同一资源。
格式化
状态的格式是JSON。状态文件保持向后兼容性。如果terraform存在错误,可以轻松地通过自己开发的工具进行更新。版本字段会清楚地指示是否要进行更改并继续进行。
根据我手动尝试,当我手动更改版本时,它的动作表现得好像该资源不存在一样。换句话说,它似乎想要创建一个独立的tf文件资源。当我将版本还原回去时,该资源显示为”up to date”,所以它会回到原来的状态。
总结
我們這次學了關於 Terraform 狀態的部分,下一次我想學習關於 Terraform 的測試工具。
资源 (zī
-
- State
- Create a Terraform Module