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


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

为什么需要状态管理

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

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

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

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

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

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

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

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

状态管理的示例

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

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

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

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

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

  1. {
  2. "version": 4,
  3. "terraform_version": "1.0.11",
  4. "serial": 1,
  5. "lineage": "acb408bb-2a95-65fd-02e6-c23487f7a3f6",
  6. "outputs": {},
  7. "resources": [
  8. {
  9. "mode": "managed",
  10. "type": "local_file",
  11. "name": "test-file",
  12. "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
  13. "instances": [
  14. {
  15. "schema_version": 0,
  16. "attributes": {
  17. "content": "https://www.pkslow.com",
  18. "content_base64": null,
  19. "directory_permission": "0777",
  20. "file_permission": "0777",
  21. "filename": "./terraform-guides-by-pkslow.txt",
  22. "id": "6db7ad1bbf57df0c859cd5fc62ff5408515b5fc1",
  23. "sensitive_content": null,
  24. "source": null
  25. },
  26. "sensitive_attributes": [],
  27. "private": "bnVsbA=="
  28. }
  29. ]
  30. }
  31. ]
  32. }

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

状态管理命令

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

显示状态列表:

  1. $ terraform state list
  2. local_file.test-file

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

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

显示当前状态信息:

  1. $ terraform state pull

重命名:

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

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

删除状态里的资源:

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

远程状态

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

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

  1. terraform {
  2. backend "local" {
  3. path = "pkslow.tfstate"
  4. }
  5. }

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

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

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

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

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

  1. $ docker run -itd \
  2. --name terraform-postgres \
  3. -e POSTGRES_DB=terraform \
  4. -e POSTGRES_USER=pkslow \
  5. -e POSTGRES_PASSWORD=pkslow \
  6. -p 5432:5432 \
  7. postgres:13

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

  1. terraform {
  2. backend "pg" {
  3. conn_str = "postgres://pkslow:pkslow@localhost:5432/terraform?sslmode=disable"
  4. }
  5. }

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

  1. 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是具体的状态信息,如下:

  1. {
  2. "version": 4,
  3. "terraform_version": "1.0.11",
  4. "serial": 0,
  5. "lineage": "de390d13-d0e0-44dc-8738-d95b6d8f1868",
  6. "outputs": {},
  7. "resources": [
  8. {
  9. "mode": "managed",
  10. "type": "local_file",
  11. "name": "test-file",
  12. "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
  13. "instances": [
  14. {
  15. "schema_version": 0,
  16. "attributes": {
  17. "content": "https://www.pkslow.com",
  18. "content_base64": null,
  19. "directory_permission": "0777",
  20. "file_permission": "0777",
  21. "filename": "./terraform-guides-by-pkslow.txt",
  22. "id": "6db7ad1bbf57df0c859cd5fc62ff5408515b5fc1",
  23. "sensitive_content": null,
  24. "source": null
  25. },
  26. "sensitive_attributes": [],
  27. "private": "bnVsbA=="
  28. }
  29. ]
  30. }
  31. ]
  32. }

Workspace 工作区

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

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

创建一个工作区并切换:

  1. $ terraform workspace new pkslow

切换到已存在的工作区:

  1. $ terraform workspace select pkslow

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

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

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

敏感数据

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

状态锁

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

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

共享状态-数据源

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

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

远程状态的示例:

  1. data "terraform_remote_state" "vpc" {
  2. backend = "remote"
  3. config = {
  4. organization = "hashicorp"
  5. workspaces = {
  6. name = "vpc-prod"
  7. }
  8. }
  9. }
  10. resource "aws_instance" "foo" {
  11. # ...
  12. subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
  13. }

本地状态的示例:

  1. data "terraform_remote_state" "vpc" {
  2. backend = "local"
  3. config = {
  4. path = "..."
  5. }
  6. }
  7. resource "aws_instance" "foo" {
  8. # ...
  9. subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
  10. }

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

《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. 浅谈MYSQL的索引以及它的数据结构

    什么是索引 mysql的数据是持久化到磁盘的,写SQL查询数据也就是在磁盘的某个位置查找符合条件的数据,但是磁盘IO比起内存效率是极慢的,特别是数据量大的时候,这时候就需要引入索引来提高查询效率: 在 ...

  2. IIS 配置集中式证书模块实现网站自动绑定证书文件

    在 Windows 环境下如果采用 IIS 作为 网站服务器时,常规的网站绑定 HTTPS 需要一个一个站点手动选择对应的证书绑定,而且证书过期之后更换证书时也是需要一个个重新绑定操作,无法便捷的做到 ...

  3. Dockerfile 使用 SSH docker build

    如果在书写 Dockerfile 时,有些命令需要使用到 SSH 连接,比如从私有仓库下载文件等,那么我们应该怎么做呢? Dockerfile 文件配置 为了使得 Dockerfile 文件中的命令可 ...

  4. 云原生之旅 - 12)使用 Kaniko 在 Kubernetes上构建 Docker 容器镜像

    前言 前一篇文章[云原生之旅 - 11)基于 Kubernetes 动态伸缩 Jenkins Build Agents]有讲到在 Kubernetes Pod (Jenkins build agent ...

  5. oracle 内置函数(三)日期函数

    日期函数概要: 系统时间 日期操作 一.系统时间 sysdate:还是西方的格式,我们一般需要to_char(date,'yyyy-mm-dd hh24:mi:ss') next_day:当前日期的下 ...

  6. 同一服务器部署多tomcat以及MyEclipse添加多tomcat

    tomcat有很多版本但他们的部署方式并不因为版本的不同而改变其的部署方式,操作者不必考虑此等原因.本人办公电脑使用的是tomcat7,家里自用的是6版本,这里就以6版本为例实现同一台机器上部署多to ...

  7. 下载安装MinGW-w64详细步骤

    一.MinGW-w64介绍 MinGW 的全称是:Minimalist GNU on Windows ,实际上是将gcc(c/c++编译器)移植到了 Windows 平台下,并且包含了 Win32AP ...

  8. C++编程笔记(多线程学习)

    目录 一.线程创建 二.线程的相关操作 2.1 join 2.2 detach 2.3 joinable 三.线程参数 3.1传参所引发的资源回收问题 3.2 将对象的成员函数作为入口函数 四.线程的 ...

  9. live-player live-pusher惨案

    昨天遇到的问题,旧项目: 一个页面同时使用live-player和live-pusher时候遇到的问题,live-pusher正常,live-player无效,没有任何报错 打log 所有livepl ...

  10. meta标签补充

    shrink-to-fit=no 使用"width=device-width"会导致页面按比例缩小,以适应超出视口边界的内容. 使用将"shrink-to-fit=no& ...