第一份声明,以及怎么读状态文件
目标
亲手确认一条声明会同时产生真实文件和状态文件这两个结果,并学会打开状态,读懂其中的资源地址、serial 和依赖关系。
为什么重要
初次接触声明式工具时,看上去像是在“背命令”,但真正要学的是工具依据什么来做判断。Terraform 会比较代码(期望状态)、状态文件(最后所知的状态)和真实基础设施(当前存在的东西)这三者来生成计划。所以一旦丢失状态文件,工具就会把已经存在的资源当作“第一次见到”而试图重新创建,而状态中残留的幽灵资源则会被它说成“要删除”。本实验要用 jq 亲自打开状态,原因就在这里。还有一点:值是硬编码还是引用,不是个人喜好,而是图的问题。只有引用,工具才能知道顺序,以后前面的资源变化时,后面的资源也会一起更新。
步骤
- 把
/opt/lab/fixtures/terraform/starter/main.tf复制到/root/tf/first/main.tf,并在该目录中初始化。required_providers块必须原样保留,初始化之后要生成/root/tf/first/.terraform.lock.hcl和/root/tf/first/.terraform/providers,并且锁文件中必须记录localprovider。 - 在
main.tf中声明一个类型为local_file、名称为hello的资源。filename为/root/tf/first/out/hello.txt,存放内容的参数名是content。 - 应用以创建
/root/tf/first/out/hello.txt。内容设为hello opentofu。应用之后,local_file.hello要登记在/root/tf/first/terraform.tfstate中,状态中记录的content与真实文件内容必须完全相同,并且状态的version必须是4。 - 添加并应用一个类型为
random_pet、名称为suffix的资源。length设为2,不要指定prefix。状态中要生成random_pet.suffix,生成的名称必须是단어-단어(韩文,意为“单词-单词”)的形式。 - 把
hello的内容改为hello labhub并重新应用。文件内容和状态中记录的content都必须恰好是hello labhub,并且状态的serial必须不小于2。 - 用
/root/tf/first/out/temp.txt创建一个类型为local_file、名称为temp的资源,然后再把它移除。把移除时的输出保存到/root/tf/first/out/destroy.txt。结束后,temp.txt文件和状态中的local_file.temp必须消失,而local_file.hello必须保留。 - 把状态中登记的资源地址列表保存到
/root/tf/first/out/state-list.txt。local_file.hello和random_pet.suffix必须各占一行,且不得有local_file.temp(在完成第 6 步之后再导出)。 - 用
/root/tf/first/out/summary.txt创建一个类型为local_file、名称为summary的资源。内容中必须同时包含random_pet.suffix生成的名称和hello labhub,并且这两个值都必须通过引用其他资源的属性来填入。应用之后,状态中local_file.summary的dependencies里必须能看到random_pet.suffix。
参考
- 这个环境中的命令是
tofu,也可以叫作terraform。子命令和选项的结构相同。 - 虽然没有互联网,但
local、random、null、tlsprovider 已经放在镜像内的文件系统镜像源中,所以可以完成初始化。 - 与其用眼睛读取状态,不如用
jq提取更快。.version、.serial、.lineage、[.resources[].type + "." + .resources[].name]就足够了。 - 常见错误 1:在内容末尾加了换行,使状态中的
content变成hello labhub\n。第 5 步检查的是完全一致。 - 常见错误 2:用眼睛看第 8 步中生成的 pet 名称,然后原样写进字符串。文件内容虽然能通过,但不会产生依赖关系,因此会失败。
初始化工作目录并获取 provider
把 /opt/lab/fixtures/terraform/starter/main.tf 复制到 /root/tf/first/main.tf,并在该目录中初始化。required_providers 块必须原样保留,初始化之后要生成 /root/tf/first/.terraform.lock.hcl 和 /root/tf/first/.terraform/providers,并且锁文件中必须记录 local provider。
fixture 中的初始文件里已经有 required_providers。把这个文件复制到工作目录再初始化,就会生成锁文件和 .terraform/providers。这个环境不走互联网,而是从镜像内的文件系统镜像源下载。
声明 local_file 资源
在 main.tf 中声明一个类型为 local_file、名称为 hello 的资源。filename 为 /root/tf/first/out/hello.txt,存放内容的参数名是 content。
在 resource "타입" "이름"(占位符依次为类型、名称)中,第二个标签就是地址的后半部分。注意存放文件内容的参数名不是复数形式。
应用,同时创建文件和状态
应用以创建 /root/tf/first/out/hello.txt。内容设为 hello opentofu。应用之后,local_file.hello 要登记在 /root/tf/first/terraform.tfstate 中,状态中记录的 content 与真实文件内容必须完全相同,并且状态的 version 必须是 4。
应用结束后,真实文件和 terraform.tfstate 会同时生成。比较状态中记录的 content 与真实文件内容是否相同——这两者出现偏差的状态就是漂移。也确认 version 字段是多少。
用 random_pet 生成随机名称
添加并应用一个类型为 random_pet、名称为 suffix 的资源。length 设为 2,不要指定 prefix。状态中要生成 random_pet.suffix,生成的名称必须是 단어-단어(韩文,意为“单词-单词”)的形式。
random_pet 会生成用连字符连接单词的名称。找出决定单词数量的参数是什么,并且不要指定前缀。
修改声明并重新应用
把 hello 的内容改为 hello labhub 并重新应用。文件内容和状态中记录的 content 都必须恰好是 hello labhub,并且状态的 serial 必须不小于 2。
只修改内容并重新应用,状态中的 serial 就会增加。状态中记录的值必须与真实文件完全相同,所以不要让末尾混入换行。
只挑出临时资源来删除
用 /root/tf/first/out/temp.txt 创建一个类型为 local_file、名称为 temp 的资源,然后再把它移除。把移除时的输出保存到 /root/tf/first/out/destroy.txt。结束后,temp.txt 文件和状态中的 local_file.temp 必须消失,而 local_file.hello 必须保留。
从声明中删除资源块并重新应用,就只有那个资源会消失。删除之前,先阅读输出,确认本应保留的资源没有被列为对象。
提取状态中登记的地址
把状态中登记的资源地址列表保存到 /root/tf/first/out/state-list.txt。local_file.hello 和 random_pet.suffix 必须各占一行,且不得有 local_file.temp(在完成第 6 步之后再导出)。
有一个子命令可以只显示状态中登记了什么,并且只显示地址。如果已经删除的资源仍留在列表中,说明步骤的顺序做错了。
用引用创建汇总文件
用 /root/tf/first/out/summary.txt 创建一个类型为 local_file、名称为 summary 的资源。内容中必须同时包含 random_pet.suffix 生成的名称和 hello labhub,并且这两个值都必须通过引用其他资源的属性来填入。应用之后,状态中 local_file.summary 的 dependencies 里必须能看到 random_pet.suffix。
用表达式引用其他资源的属性,依赖关系就会自动产生。如果把用眼睛看到的值直接写进字符串,文件内容虽然正确,但 dependencies 是空的,因此会失败。