《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. Windows版CheatSheet——一键显示当前程序快捷键列表

    Windows系统上的各种软件有太多的快捷键,想要记住是几乎不可能的,推荐一个一键显示当前软件快捷键的软件,在使用其他程序的时候,只要按下Ctrl+`就可以理解弹出该软件的所有快捷键列表,还支持收藏功 ...

  2. ironic组件硬件自检服务——ironic-inspector

    介绍 ironic-inspector是一个用于硬件自检的辅助型服务,它可以对被ironic组件管理的裸金属节点进行硬件自检,通过在裸金属节点上运行内存系统,发现裸金属节点的硬件信息,例如CPU数量和 ...

  3. [leetcode] 994. Rotting Oranges

    题目 You are given an m x n grid where each cell can have one of three values: 0 representing an empty ...

  4. IOS AND Android 配置Fiddler环境

    下载:http://rj.baidu.com/soft/detail/10963.html?ald 运行Fiddler点击Tools: 选择设置选项:   1.     选择HTTPS新选项卡. 2. ...

  5. 社论 22.10.9 优化连续段dp

    CF840C 给定一个序列 \(a\),长度为 \(n\).试求有多少 \(1\) 到 \(n\) 的排列 \(p_i\),满足对于任意的 \(2\le i\le n\) 有 \(a_{p_{i-1} ...

  6. Zabbix技术分享——docker组件编译使用教程

    docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源,它可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的Linux机器上,还可以实现 ...

  7. 【极客时间】大数据概述及HDFS介绍

  8. 【Flume】概述及组成、入门案例、进阶(事务、拓扑结构)、不同拓扑案例、自定义、数据流监控Ganglia

    一.概述 1.定义 日志采集.聚合.传输的系统,基于流式结构 即:读取本地磁盘数据,写入HDFS或kafka 2.架构 Agent:JVM进程,以事件形式将数据送到目的地. Agent由三部分组成:S ...

  9. python3中的常见知识点1

    python3中的常见知识点1 简记一些python小知识 字符串输出 docstring(文档字符串) Lambda 函数(匿名函数) python函数之参数调用 参考链接 字符串输出 1.r'原始 ...

  10. C#11新特性-Raw string literals原始字符串研究、示例

    这几天看C# 11的新语法,学习到了Raw string literals 今天给大家分享一下: 原始字符串是字符串的一种新格式. 原始字符串可以包含任意文本,包括空格.新行.嵌入引号和其他特殊字符, ...