《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看。


军书十二卷,卷卷有爷名。

为什么需要状态管理

Terraform的主要作用是管理云平台上的资源,通过声明式的HCL配置来映射资源,如果云平台上没有资源则需要创建,如果有则不用。那Terraform要实现这个功能有多种方式。

一种是每次执行apply命令时都调用API接口检查一下远程的云资源是否与配置文件一致,如果没有则创建,如果有但不同则需要修改,如果有且相同则不用变更。这种机制能保证云平台的资源与HCL配置是一致的。缺点也是非常明显的,每次都需要调用API去检查远程资源,效率很低,特别是当资源特别多的场景。

另一种方式是每次变更资源的时候,都会创建一个映射文件,它保存云平台资源的状态。这样每次执行apply命令时,只需要检查HCL配置与映射文件的差异即可。

Terraform选择的是第二种方式,通过映射文件来保存资源状态,在Terraform的世界里叫状态文件。Terraform这样做是基于以下考虑:

  • 云平台真实状态的映射,解析状态文件即可以知道真实情况。
  • 元数据存储,如资源之间的依赖关系,需要通过依赖关系来知道创建或销毁顺序。
  • 提升性能,特别是在大规模云平台上,多次调用API去查询资源状态是很费时的。
  • 同步状态,通过远程状态文件来同步状态,这也是Terraform最佳的实践。

讲到这里,已经回答了之前在第一章留下的思考题:

如果再次执行apply会不会再次创建一个文件呢?还是创建失败,因为文件已存在?为什么?

答案:不会创建,因为通过状态文件记录了变更,Terraform判断不再需要创建了。

状态管理的示例

为了更多注意力放在状态管理上,我们还是使用最简单的例子local_file,具体代码如下:

resource "local_file" "terraform-introduction" {
content = "https://www.pkslow.com"
filename = "${path.root}/terraform-guides-by-pkslow.txt"
}

我们以实际操作及现象来讲解状态文件的作用和工作原理:

操作 现象及说明
terraform apply 生成资源:第一次生成
terraform apply 没有变化:状态文件生成,不需要再创建
terraform destroy 删除资源:根据状态文件的内容删除
terraform apply 生成资源:状态显示没有资源,再次生成
删除状态文件 没有变化
terraform apply 生成资源:没有状态文件,直接生成资源和状态文件(插件做了容错处理,已存在也会新生成覆盖)
删除状态文件 没有变化
terraform destroy 无法删除资源,没有资源存在的状态

我们一直在讲状态文件,我们先来看一下它的真面目。首先它的默认文件名是terraform.tfstate,默认会放在当前目录下。它是以json格式存储的信息,示例中的内容如下:

{
"version": 4,
"terraform_version": "1.0.11",
"serial": 1,
"lineage": "acb408bb-2a95-65fd-02e6-c23487f7a3f6",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "local_file",
"name": "test-file",
"provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"content": "https://www.pkslow.com",
"content_base64": null,
"directory_permission": "0777",
"file_permission": "0777",
"filename": "./terraform-guides-by-pkslow.txt",
"id": "6db7ad1bbf57df0c859cd5fc62ff5408515b5fc1",
"sensitive_content": null,
"source": null
},
"sensitive_attributes": [],
"private": "bnVsbA=="
}
]
}
]
}

可以看到它记录了Terraform的版本信息,还有资源的详细信息:包括类型、名字、插件、属性等。有这些信息便可直接从状态文件里解析出具体的资源。

状态管理命令

可以通过terraform state做一些状态管理:

显示状态列表:

$ terraform state list
local_file.test-file

查看具体资源的状态信息:

$ terraform state show local_file.test-file
# local_file.test-file:
resource "local_file" "test-file" {
content = "https://www.pkslow.com"
directory_permission = "0777"
file_permission = "0777"
filename = "./terraform-guides-by-pkslow.txt"
id = "6db7ad1bbf57df0c859cd5fc62ff5408515b5fc1"
}

显示当前状态信息:

$ terraform state pull

重命名:

$ terraform state mv local_file.test-file local_file.pkslow-file
Move "local_file.test-file" to "local_file.pkslow-file"
Successfully moved 1 object(s). $ terraform state list
local_file.pkslow-file

要注意这里只是修改状态文件的名字,代码里的HCL并不会修改。

删除状态里的资源:

$ terraform state rm local_file.pkslow-file
Removed local_file.pkslow-file
Successfully removed 1 resource instance(s).

远程状态

状态文件默认是在本地目录上的terraform.tfstate文件,在团队使用中,每个人的电脑环境独立的,那么需要保证每个人当前的状态文件都是最新且与现实资源真实对应,简直是天方夜谭。而状态不一致所带的灾难也是极其可怕的。所以,状态文件最好是要存储在一个独立的大家可共同访问的位置。对于状态的管理的配置,Terraform称之为Backends

Backend是两种模式,分别是localremotelocal模式很好理解,就是使用本地路径来存储状态文件。配置示例如下:

terraform {
backend "local" {
path = "pkslow.tfstate"
}
}

通过这样配置后,不再使用默认的terraform.tfstate文件,而是使用自定义的文件名pkslow.tfstate

对于remote模式,则有多种配置方式,Terraform支持的有:

  • s3
  • gcs
  • oss
  • etcd
  • pg
  • http
  • kubernetes

等,能满足主流云平台的需求。每一个配置可以参考官网,在本地我采用数据库postgresql的方式,让大家都能快速实验。

我通过Docker的方式启动PostgreSQL,命令如下:

$ docker run -itd \
--name terraform-postgres \
-e POSTGRES_DB=terraform \
-e POSTGRES_USER=pkslow \
-e POSTGRES_PASSWORD=pkslow \
-p 5432:5432 \
postgres:13

terraform块中配置backend,这里指定数据库连接信息即可,更多参数请参考:https://www.terraform.io/language/settings/backends/pg

terraform {
backend "pg" {
conn_str = "postgres://pkslow:pkslow@localhost:5432/terraform?sslmode=disable"
}
}

当然,把敏感信息直接放在代码中并不合适,可以直接在命令行中传入参数:

terraform init -backend-config="conn_str=postgres://pkslow:pkslow@localhost:5432/terraform?sslmode=disable"

执行init和apply之后,连接数据库查看,会创建一个叫terraform_remote_state的Schema,在该Schema下有一张states表来存储对应的状态信息,如下:

表中字段name是namespace,而data是具体的状态信息,如下:

{
"version": 4,
"terraform_version": "1.0.11",
"serial": 0,
"lineage": "de390d13-d0e0-44dc-8738-d95b6d8f1868",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "local_file",
"name": "test-file",
"provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"content": "https://www.pkslow.com",
"content_base64": null,
"directory_permission": "0777",
"file_permission": "0777",
"filename": "./terraform-guides-by-pkslow.txt",
"id": "6db7ad1bbf57df0c859cd5fc62ff5408515b5fc1",
"sensitive_content": null,
"source": null
},
"sensitive_attributes": [],
"private": "bnVsbA=="
}
]
}
]
}

Workspace 工作区

如果我们用Terraform代码生成了dev环境,但现在需要uat环境,该如何处理呢?

首先,不同环境的变量一般是不一样的,我们需要定义各种的变量文件如dev.tfvarsuat.tfvarsprod.tfvars等。但只有各自变量是不够的,因为还有状态。状态也必须要隔离,而Workspace就是Terraform用来隔离状态的方式。默认的工作区为default,如果没有指定,则表示工作于default工作区中。而当指定了工作区,状态文件就会与工作区绑定。

创建一个工作区并切换:

$ terraform workspace new pkslow

切换到已存在的工作区:

$ terraform workspace select pkslow

而当我们处于某个工作区时,是可以获取工作区的名字的,引用为:${terraform.workspace},示例如下:

resource "aws_instance" "example" {
count = "${terraform.workspace == "default" ? 5 : 1}" # ... other arguments
}

之前讲过默认的状态文件名为terraform.tfstate;而在多工作区的情况下(只要你创建了一个非默认工作区),状态文件就会存在terraform.tfstate.d目录下。而在远程状态的情况下,也会有一个映射,Key为工作区名,Value一般是状态内容。

敏感数据

本地状态文件都是明文存储状态信息的,所以要保护好自己的状态文件。对于远程状态文件,有些存储方案是支持加密的,会对敏感数据(sensitive)进行加密。

状态锁

本地状态文件下不需要状态锁,因为只有一个人在变更。而远程状态的情况下,就可能出现竞争了。比如一个人在apply,而另一个人在destroy,那就乱了。而状态锁可以确保远程状态文件只能被一个人使用。但不是所有远程状态的方式都支持锁的,一般常用的都会支持,如GCS、S3等。

所以,每当我们在执行变更时,Terraform总会先尝试去拿锁,如果拿锁失败,就该命令失败。可以强制解锁,但要非常小心,一般只建议在自己明确知道安全的时候才使用,比如死锁了。

共享状态-数据源

既然远程状态文件是可以共享的,那状态信息也是可以共享的。这样会带来的一个好处是,即使两个根模块,也是可以共享信息的。比如我们在根模块A创建了一个数据库,而根模块B需要用到数据库的信息如IP,这样通过远程状态文件就可以共享给根模块B了。

注意这里我强调的是根模块,因为如果A和B在同一个根模块下,那就不需要通过远程状态的方式来共享状态了。

远程状态的示例:

data "terraform_remote_state" "vpc" {
backend = "remote" config = {
organization = "hashicorp"
workspaces = {
name = "vpc-prod"
}
}
} resource "aws_instance" "foo" {
# ...
subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
}

本地状态的示例:

data "terraform_remote_state" "vpc" {
backend = "local" config = {
path = "..."
}
} resource "aws_instance" "foo" {
# ...
subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
}

要注意的是,只有根模块的输出变量才能被共享,子模块是不能被获取的。

《Terraform 101 从入门到实践》 第四章 States状态管理的更多相关文章

  1. #Python编程从入门到实践#第四章笔记

    #Python编程从入门到实践#第四章笔记   操作列表 ​​​1.遍历列表 使用for循环,遍历values列表 for value in values: print(value) 2.数字列表 使 ...

  2. python编程:从入门到实践----第四章>操作列表

    一.遍历整个列表 1-1.假设有一个魔术师名单,需要将其中每个魔术师的名字都打印出来. # 用for循环来打印魔术师名单中的名字 magicians=['alice','david','carolin ...

  3. Python:从入门到实践--第四章--列表操作--练习

    #1.想出至少三种你喜欢的水果,将其名称存储在一个列表中,再使用for循环将每种水果的名称都打印出来. #要求:(1)修改这个for循环,使其打印包含名称的句子,而不是仅仅是水果的名称.对于每种水果, ...

  4. py从入门到实践 第四章

    4.1 遍立列表 ~= shell 数组————————————————————————————————————————————thrink = ['link','path','pwd']for i ...

  5. ArcGIS for Desktop入门教程_第四章_入门案例分析 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第四章_入门案例分析 - ArcGIS知乎-新一代ArcGIS问答社区 1 入门案例分析 在第一章里,我们已经对ArcGIS系列软件的体系结构有了一 ...

  6. D3.js的v5版本入门教程(第四章)—— 理解Update、Enter、Exit

    D3.js的v5版本入门教程(第四章) Update.Enter.Exit是D3.js中很重要的概念,下面来讲一下它们到底是什么?(当你看完后.你就会知道如果数据集个数和选择集个数不匹配的情况下使用d ...

  7. Git工程开发实践(四)——Git分支管理策略

    A successful Git branching model https://nvie.com/posts/a-successful-git-branching-model/ Git工程开发实践( ...

  8. Testlink1.9.17使用方法( 第四章 测试需求管理 )

    第四章 测试需求管理 QQ交流群:585499566 需求规格说明书是我们开展测试的依据.首先,我们可以对项目(产品)的需求规格说明书进行分解和整理,将其拆分为多个需求,一个项目可以包含多个需求,一个 ...

  9. python编程:从入门到实践----第六章>字典

    一.一个简单的字典:alien_0存储外星人的颜色和点数,使用print打印出来 alien_0 = {'color': 'green','points': 5} print(alien_0['col ...

  10. python编程:从入门到实践----第五章>if 语句

    一.一个简单示例 假设有一个汽车列表,并想将其每辆汽车的名称打印出来.遇到汽车名‘bmw’,以全大写打印:其他汽车名,首字母大写 cars=['audi','bmw','subaru','toyota ...

随机推荐

  1. Vue 基础学习总结

    介绍 Vue.js 中文文档地址:https://cn.vuejs.org/guide/introduction.html#what-is-vue Vue.js 是什么 Vue (读音 /vjuː/, ...

  2. Java开发学习(四十二)----MyBatisPlus查询语句之条件查询

    一.条件查询的类 MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合. 这个我们在前面都有见过,比如查询所有和分页查询的时候,都有看到过一个Wrapper类, ...

  3. 微信小程序根据开发环境切换域名

     domain.js // 获取当前账号信息,线上小程序版本号仅支持在正式版小程序中获取,开发版和体验版中无法获取. // envVersion:'develop','trial','release' ...

  4. 关于windows7打不开hlp文件的解决方法

    前言 其实也不是打不开,而是打开后是这样的. 也就是相当于打不开. 解决方案 安装对应架构版本补丁,重启电脑即可. 下载地址 包含64位和32位. 有能力的还望下载这个 下载地址 给我留点积分,感谢!

  5. windows 使用ACR122U-A9设备读写M1卡

    软件地址 M1卡服务程序 安装好驱动后就可以破解,破解成功后的秘钥会显示出来(记录下来),同级目录会生成一个dump文件,这个文件包含此M1卡的全部数据,可以使用hexedit打开,编辑或修改 Mif ...

  6. 深入浅出学习透析 Nginx 服务器的基本原理和配置指南「运维操作实战篇」

    Nginx前提回顾 Nginx 是一个高性能的 Web 和反向代理服务器, 它具有有很多非常优越的特性: Web服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效 ...

  7. [信息抽取]基于ERNIE3.0的多对多信息抽取算法:属性关系抽取

    [信息抽取]基于ERNIE3.0的多对多信息抽取算法:属性关系抽取 实体关系,实体属性抽取是信息抽取的关键任务:实体关系抽取是指从一段文本中抽取关系三元组,实体属性抽取是指从一段文本中抽取属性三元组: ...

  8. 【Shell案例】【while循环、正则表达式、sed、awk、grep】5、打印空行的行号

    描述写一个 bash脚本以输出一个文本文件 nowcoder.txt中空行的行号,可能连续,从1开始 示例:假设 nowcoder.txt 内容如下:ab c d e f 你的脚本应当输出:35791 ...

  9. 【每日一题】【List与Array互转】【工具类的使用】2021年12月10日-56. 合并区间

    以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] .请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入 ...

  10. forms组件源码剖析

    一:forms组件源码剖析 1.forms组件源码切入点: 1.0 form_obj.is_valid() 2.0 def is_valid(self): """ Ret ...