《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是兩種模式,分別是local和remote,local模式很好理解,就是使用本地路徑來存盤狀態檔案,配置示例如下:
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.tfvars、uat.tfvars和prod.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
}
要注意的是,只有根模塊的輸出變數才能被共享,子模塊是不能被獲取的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543523.html
標籤:其他
上一篇:python的學習之路之day1
