理解 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
广告
将在 10 秒后关闭
bannerAds