大致了解Terraform的tfstate

本文是OpenSaaS Studio Advent Calendar 2019第20天的文章。

首先

    • なんとなくplanとapplyが動かせるからいいかな〜ってノリでTerraformを使ってる人々(かつての自分)のためのメモです。

 

    • tfstateが分からないと、Terraformの構成を変えたい!と思ったときにリファクタリングすることもできない。

Terraformの構成を最初からうまいこと作るのって本当に難しい。。。
リファクタリングのしにくさがTerraformの課題のひとつだと思う。

因此,我将它总结起来了。

tfstate 的中文本地化表述: 状态文件

tfstate文件是由Terraform管理的资源的当前状态的表示文件。

默认情况下,tfstate文件将在本地生成,但我认为许多现场都是使用远程后端(如S3)进行管理。

tfstate的用途何在

当执行terraform plan时,它会显示当前状态和所描述定义之间的差异,
这实际上是显示了tfstate和HCL定义之间的差异。
我觉得我们应该查看实际资源而不是tfstate中的差异!
但从性能和资源依赖的角度来看,Terraform本身必须对状态进行追踪,
这一点在官方文档中有所提及。
https://www.terraform.io/docs/state/purpose.html

创建 tfstate 文件的单位

就本身而言,是执行了terraform apply命令的单位。
如果针对开发环境、测试环境和生产环境分别执行了terraform apply,
tfstate文件将变为dev.tfstate、stg.tfstate和prd.tfstate这三个文件。

我认为还有一个方法可以根据资源类型将命令执行单元分开。
这样一来,我们就能完全区分高频更改的应用层资源更新,如EC2、ECS等,和不太想频繁更改的网络层资源更新,比如VPC。

我参与的项目中,有很多采用将环境和资源分开的方式。看看保存着tfstate文件的s3,情况大致如下:
桶本身按照环境进行了划分,并且每个资源都有对应的tfstate文件。

スクリーンショット 2019-12-21 14.16.49.png

「简而言之」是我当前参与的项目的名称。

当tfstate文件被分开时,如前所述,可以将变更范围限定在特定的部分,从而获得安全性和执行时间缩短等优势。然而,并非仅有优点,也存在着一些缺点。
当需要改变Terraform的目录结构时,也会需要改变tfstate文件的创建单位。这意味着旧的tfstate文件需要将管理的资源移动到新的tfstate文件中,或者重新导入。这个过程可能会变得复杂。
在这种情况下,如果不了解tfstate结构,就无法有效地进行工作。

tfstate文件的结构

嗯,实际的tfstate内容如下所示的JSON文件。
这是ECS服务的tfstate。省略了不必解释的部分(resources.instances以下的…部分),所以实际文件可能会有所不同。

{
  "version": 4,
  "terraform_version": "0.12.14",
  "serial": 1,
  "lineage": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "outputs": {},
  "resources": [
     {
      "module": "module.ecs",
      "mode": "managed",
      "type": "aws_ecs_service",
      "name": "xxx-api",
      "provider": "provider.aws",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "cluster": "arn:aws:ecs:ap-northeast-1:xxxxxxx:cluster/dev",
            "desired_count": 1,
            "id": "arn:aws:ecs:ap-northeast-1:xxxxxxx:service/dev-xxx-api",
            "launch_type": "FARGATE",
            "name": "dev-xxx-api",
            "task_definition": "dev-xxx-api:1",
            ...
          },
          "dependencies": [
            "module.ecs_service.xxx-api.aws_ecs_task_definition.task"
          ]
        }
      ]
    },
    ...
  ]
}

如果在Terraform 0.12.14上执行,tfstate文件的版本将变为4。
版本4的tfstate文件格式如下。

type stateV4 struct {
    Version          stateVersionV4           `json:"version"`
    TerraformVersion string                   `json:"terraform_version"`
    Serial           uint64                   `json:"serial"`
    Lineage          string                   `json:"lineage"`
    RootOutputs      map[string]outputStateV4 `json:"outputs"`
    Resources        []resourceStateV4        `json:"resources"`
}

实际上被管理的资源信息将被添加到resources目录下。

"module": "module.ecs",
"mode": "managed",
"type": "aws_ecs_service",
"name": "xxx-api",
"provider": "provider.aws",
"instances": [ {...}, ...]

有这样的描述,但分别表示以下的意思。

    • module: moduleタグの名前。 module “ecs” { … }のHCL記述に対応。

 

    • mode: managed or dataの値になる。managedはresourceタグで管理しているもの。dataはdataタグで管理しているもの。

 

    • type: resourceタグのtype。resource “type” “name” { … }のHCL記述に対応。

 

    • name: resourceタグのname。resource “type” “name” { … }のHCL記述に対応。

 

    • provider: このリソースのプロバイダ。

 

    instances: 管理されているリソースの情報。

在Terraform中,要惟一确定一个资源,必须使用`${module}.${type}.${name}`。
顺便提一下,如果未使用module标签,则module部分本身不会包含在tfstate中。

resources下面的数据结构是代码中的这一部分。

type resourceStateV4 struct {
    Module         string                  `json:"module,omitempty"`
    Mode           string                  `json:"mode"`
    Type           string                  `json:"type"`
    Name           string                  `json:"name"`
    EachMode       string                  `json:"each,omitempty"`
    ProviderConfig string                  `json:"provider"`
    Instances      []instanceObjectStateV4 `json:"instances"`
}

在上面提到的tftate文件中,并不包含EachMode,但如果使用Terraform 0.12中新增的for_each功能,该参数也会被包含进来。
https://www.hashicorp.com/blog/hashicorp-terraform-0-12-preview-for-and-for-each/

总结

对Terraform的目录结构进行了考虑,想要进行更改的强烈愿望是促使我查看tfstate的原因。
要更改目录结构,需要执行用于编辑tfstate的terraform state命令,或者使用terraform import命令将现有资源导入到tfstate中。但是,只要略微了解tfstate的概述,就可以考虑并提高工作效率,这真是太好了。
但是,即便如此,手动编辑tfstate仍然是一项艰巨的任务。
希望terraformer等工具能不断发展(同时支持模块化),这是我的祝愿。

bannerAds