Terraform 状态管理

LinuxBeginner
立即练习

介绍

Terraform 使用一个状态文件(state file)来存储它所管理的基础设施的信息。这个状态文件至关重要,因为它将你的配置文件中定义的资源映射到现实世界中的资源。通过维护这个状态,Terraform 才能确定需要创建、更新还是销毁哪些资源。

在这个 Lab 中,你将获得管理 Terraform 状态的实践经验。你将学习如何使用各种 terraform state 子命令来检查当前状态、列出已管理的资源、在状态中重命名一个资源,以及刷新状态以使其与实际基础设施保持一致。这些是任何 Terraform 用户都应掌握的基本技能。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 89%。获得了学习者 91% 的好评率。

使用 terraform show 查看当前状态

在这一步中,你将学习如何使用 terraform show 命令来查看当前状态。这个命令提供了 Terraform 当前管理的所有资源(记录在状态文件中)的可读性输出。这是一种安全的、只读的方式来检查你的基础设施的状态。

本 Lab 的设置脚本已经初始化了 Terraform 并创建了一个资源,生成了一个 terraform.tfstate 文件。让我们来检查一下它。

在你的终端中运行以下命令:

terraform show

你将看到一个详细的输出,描述了 Terraform 正在管理的 local_file 资源。

## local_file.example:
resource "local_file" "example" {
    content              = "This is an example file managed by Terraform."
    content_base64sha256 = "F5EYZhFNzSXdE4CUftwQoDVqvdiufZpVyiLMqyZVOcQ="
    content_base64sha512 = "m1hvaxMuc/EhaKintyI54NSTTJ5yXpqHPCBNoHubF0rvF3JAj36lMj20aPcv21+3/OK+SqkiTlnT/LdvLCwqDA=="
    content_md5          = "a43bdd236c2f0f4d87452ba2ef0867e4"
    content_sha1         = "ec3adcab998872def2df6200fb03992ac6f237a4"
    content_sha256       = "17911866114dcd25dd1380947edc10a0356abdd8ae7d9a55ca22ccab265539c4"
    content_sha512       = "9b586f6b132e73f12168a8a7b72239e0d4934c9e725e9a873c204da07b9b174aef1772408f7ea5323db468f72fdb5fb7fce2be4aa9224e59d3fcb76f2c2c2a0c"
    directory_permission = "0777"
    file_permission      = "0777"
    filename             = "./example.txt"
    id                   = "ec3adcab998872def2df6200fb03992ac6f237a4"
}

此输出确认 Terraform 了解一个资源,即 local_file.example,并列出了它的所有属性。

检查 state 文件 terraform.tfstate

在这一步中,你将检查原始的状态文件 terraform.tfstate。虽然 terraform show 提供了一个用户友好的视图,但查看原始的 JSON 文件有助于你理解 Terraform 如何存储数据。

警告: 你几乎永远不应该手动编辑 terraform.tfstate 文件。不正确的修改可能会破坏你的状态并导致严重问题。我们仅出于教育目的查看它。

使用 cat 命令显示状态文件的内容:

cat terraform.tfstate

输出将是一个 JSON 对象。它包含元数据(metadata),如 Terraform 版本(version),以及最重要的是,一个 resources 数组。

{
  "version": 4,
  "terraform_version": "1.13.3",
  "serial": 1,
  "lineage": "2a568739-a81d-ced7-dee1-7a92a1666022",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "local_file",
      "name": "example",
      "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "content": "This is an example file managed by Terraform.",
            "content_base64sha256": "...",
            "content_base64sha512": "...",
            "content_md5": "...",
            "content_sha1": "...",
            "content_sha256": "...",
            "content_sha512": "...",
            "directory_permission": "0777",
            "file_permission": "0777",
            "filename": "./example.txt",
            "id": "ec3adcab998872def2df6200fb03992ac6f237a4",
            "sensitive_content": null,
            "source": null
          },
          "sensitive_attributes": [
            [
              {
                "type": "get_attr",
                "value": "sensitive_content"
              }
            ]
          ],
          "identity_schema_version": 0
        }
      ]
    }
  ],
  "check_results": null
}

这些原始数据就是 Terraform 用于创建执行计划(execution plans)的内容。

运行 terraform state list 以列出资源

在这一步中,你将使用 terraform state list 来获取状态中所有资源的简洁列表。当你的基础设施增长,拥有几十个或几百个资源时,terraform show 可能会过于冗长。state list 命令提供了一个简单的、以换行符分隔的资源地址列表。

在你的终端中执行该命令:

terraform state list

输出将非常直接,只显示我们创建的资源的地址。

local_file.example

当你需要使用其他命令来定位特定资源时,这个命令对于脚本编写或快速识别特定资源的地址非常有用。

使用 terraform state mv 重命名资源

在这一步中,你将学习如何使用 terraform state mv 来重命名状态文件(state file)中的资源。这是重构(refactoring)Terraform 代码时常见的操作。如果你重命名 .tf 文件中的资源块(resource block),Terraform 会认为你想销毁旧资源并创建一个新资源。state mv 命令允许你更新状态文件以匹配代码更改,从而避免不必要的销毁和创建。

首先,让我们在配置文件中重命名该资源。使用 nano 编辑器打开 main.tf

nano main.tf

将资源名称从 example 更改为 example_renamed。文件应如下所示:

resource "local_file" "example_renamed" {
  content  = "This is an example file managed by Terraform."
  filename = "${path.module}/example.txt"
}

Ctrl+X,然后按 Y,再按 Enter 保存并退出。

现在,运行 terraform plan 查看 Terraform 打算执行的操作:

terraform plan

计划将显示 Terraform 想要销毁 local_file.example 并创建 local_file.example_renamed,这不是我们期望的结果。

local_file.example: Refreshing state... [id=ec3adcab998872def2df6200fb03992ac6f237a4]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  - destroy

Terraform will perform the following actions:

  ## local_file.example will be destroyed
  ## (because local_file.example is not in configuration)
  - resource "local_file" "example" {
      - content              = "This is an example file managed by Terraform." -> null
      - content_base64sha256 = "F5EYZhFNzSXdE4CUftwQoDVqvdiufZpVyiLMqyZVOcQ=" -> null
      - content_base64sha512 = "m1hvaxMuc/EhaKintyI54NSTTJ5yXpqHPCBNoHubF0rvF3JAj36lMj20aPcv21+3/OK+SqkiTlnT/LdvLCwqDA==" -> null
      - content_md5          = "a43bdd236c2f0f4d87452ba2ef0867e4" -> null
      - content_sha1         = "ec3adcab998872def2df6200fb03992ac6f237a4" -> null
      - content_sha256       = "17911866114dcd25dd1380947edc10a0356abdd8ae7d9a55ca22ccab265539c4" -> null
      - content_sha512       = "9b586f6b132e73f12168a8a7b72239e0d4934c9e725e9a873c204da07b9b174aef1772408f7ea5323db468f72fdb5fb7fce2be4aa9224e59d3fcb76f2c2c2a0c" -> null
      - directory_permission = "0777" -> null
      - file_permission      = "0777" -> null
      - filename             = "./example.txt" -> null
      - id                   = "ec3adcab998872def2df6200fb03992ac6f237a4" -> null
    }

  ## local_file.example_renamed will be created
  + resource "local_file" "example_renamed" {
      + content              = "This is an example file managed by Terraform."
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./example.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 1 to destroy.

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

为了解决这个问题,我们将告诉 Terraform 我们只是移动了资源。运行 terraform state mv 命令,提供旧地址和新地址:

terraform state mv local_file.example local_file.example_renamed

你将看到一条确认消息:

Move "local_file.example" to "local_file.example_renamed"
Successfully moved 1 object(s).

现在,再次运行 terraform plan

terraform plan

这次,Terraform 报告不需要更改,因为状态现在与配置匹配。

local_file.example_renamed: Refreshing state... [id=ec3adcab998872def2df6200fb03992ac6f237a4]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

运行 terraform refresh 以更新状态

在这一步中,你将使用 terraform refresh 来协调状态文件与真实世界的基础设施。有时,你的基础设施会发生一些非 Terraform 控制范围内的更改。这被称为“漂移”(drift)。refresh 命令会更新状态文件以反映这些更改。

让我们通过手动删除 Terraform 创建的文件来模拟漂移。

rm example.txt

现在,如果你运行一个计划(plan),Terraform 会检测到文件丢失,需要重新创建。

terraform plan
local_file.example_renamed: Refreshing state... [id=ec3adcab998872def2df6200fb03992ac6f237a4]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  ## local_file.example_renamed will be created
  + resource "local_file" "example_renamed" {
      + content              = "This is an example file managed by Terraform."
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./example.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

然而,terraform refresh 命令也可以用来明确地刷新状态。该命令会检查实际的资源,并相应地更新状态文件。

terraform refresh
local_file.example_renamed: Refreshing state... [id=ec3adcab998872def2df6200fb03992ac6f237a4]

现在再次运行 terraform plan 以确认状态是最新的。

terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  ## local_file.example_renamed will be created
  + resource "local_file" "example_renamed" {
      + content              = "This is an example file managed by Terraform."
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./example.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

你已成功使用 terraform refresh 来修正状态漂移。

总结

恭喜你完成了关于 Terraform 状态管理的这个实验(Lab)!

在这个实验中,你获得了使用基础 terraform state 子命令的实践经验。你学会了如何:

  • 使用 terraform show 来查看基础设施状态的可读表示。
  • 检查原始的 terraform.tfstate JSON 文件以了解其结构。
  • 使用 terraform state list 获取一个干净的、所有被管理资源的列表。
  • 安全地重命名配置文件中的资源,并使用 terraform state mv 相应地更新状态。
  • 使用 terraform refresh 来协调你的状态文件与真实世界的基础设施,并修正漂移(drift)。

掌握这些命令是使用 Terraform 有效且自信地管理你的基础设施的基础。