在 Terraform 中,狀態檔(State File)是追蹤基礎設施資源的核心機制。正確管理狀態檔對於團隊協作、資源追蹤和基礎設施維護至關重要。本文將深入探討狀態檔的概念、遠端後端配置以及常用的狀態操作指令。
State 檔概念(State File Concepts)
Terraform State 是一個 JSON 格式的檔案,用於儲存 Terraform 管理的基礎設施資源的當前狀態。預設情況下,這個檔案名為 terraform.tfstate,存放在工作目錄中。
State 檔的作用
| 功能 | 說明 |
|---|
| 資源追蹤 | 記錄 Terraform 管理的所有資源及其屬性 |
| 效能優化 | 避免每次執行時都需要查詢雲端 API |
| 相依性管理 | 維護資源之間的相依關係 |
| 變更偵測 | 比對計畫變更與實際狀態的差異 |
State 檔結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| {
"version": 4,
"terraform_version": "1.5.0",
"serial": 10,
"lineage": "abc123-def456-ghi789",
"outputs": {
"vpc_id": {
"value": "vpc-0123456789abcdef0",
"type": "string"
}
},
"resources": [
{
"mode": "managed",
"type": "aws_vpc",
"name": "main",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "vpc-0123456789abcdef0",
"cidr_block": "10.0.0.0/16",
"enable_dns_hostnames": true,
"tags": {
"Name": "main-vpc"
}
}
}
]
}
]
}
|
State 檔的重要性
- 真相來源(Source of Truth):State 檔是 Terraform 認知的基礎設施真實狀態
- 資源映射:將設定檔中的資源對應到雲端的實際資源
- 元資料儲存:包含資源相依性和其他重要元資料
- 敏感資料:可能包含密碼、金鑰等敏感資訊(需妥善保護)
本地 vs 遠端狀態(Local vs Remote State)
本地狀態(Local State)
本地狀態是 Terraform 的預設行為,狀態檔直接存放在工作目錄中。
本地狀態的優點
- 設定簡單,無需額外配置
- 適合個人開發和學習
- 執行速度快
本地狀態的缺點
- 不適合團隊協作
- 無法防止並行操作衝突
- 檔案可能遺失或損壞
- 敏感資料暴露風險
遠端狀態(Remote State)
遠端狀態將 State 檔儲存在遠端儲存服務中,如 AWS S3、Azure Blob Storage 或 Terraform Cloud。
遠端狀態的優點
| 優點 | 說明 |
|---|
| 團隊協作 | 多人可安全共享相同的狀態 |
| 狀態鎖定 | 防止並行操作造成衝突 |
| 版本控制 | 支援狀態檔版本歷史 |
| 安全性 | 加密儲存和存取控制 |
| 可靠性 | 利用雲端服務的高可用性 |
遠端狀態的缺點
- 需要額外的基礎設施設定
- 可能增加網路延遲
- 需要管理存取權限
選擇建議
1
2
3
| 個人專案或學習 → 本地狀態
團隊協作專案 → 遠端狀態
生產環境 → 遠端狀態 + 狀態鎖定
|
S3 後端設定(S3 Backend Configuration)
AWS S3 是最常用的 Terraform 遠端後端之一,配合 DynamoDB 可實現狀態鎖定功能。
建立 S3 儲存桶和 DynamoDB 表
首先,需要建立存放狀態檔的基礎設施:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| # 建立 S3 儲存桶
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state-bucket"
tags = {
Name = "Terraform State Bucket"
Environment = "management"
}
}
# 啟用版本控制
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
# 啟用伺服器端加密
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# 封鎖公開存取
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# 建立 DynamoDB 表用於狀態鎖定
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-state-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Locks"
Environment = "management"
}
}
|
配置 S3 後端
在專案中配置 S3 後端:
1
2
3
4
5
6
7
8
9
| terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
}
}
|
後端配置參數說明
| 參數 | 說明 | 必填 |
|---|
bucket | S3 儲存桶名稱 | 是 |
key | 狀態檔在儲存桶中的路徑 | 是 |
region | S3 儲存桶所在區域 | 是 |
encrypt | 是否啟用伺服器端加密 | 否 |
dynamodb_table | 狀態鎖定用的 DynamoDB 表 | 否 |
profile | AWS CLI profile 名稱 | 否 |
role_arn | 用於存取的 IAM 角色 ARN | 否 |
多環境狀態管理
使用不同的 key 來區分環境:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 開發環境
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "dev/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
}
}
# 正式環境
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
}
}
|
使用 Workspace 管理多環境
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 建立新的 workspace
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# 切換 workspace
terraform workspace select prod
# 列出所有 workspace
terraform workspace list
# 顯示當前 workspace
terraform workspace show
|
配合 workspace 的後端配置:
1
2
3
4
5
6
7
8
9
10
| terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
workspace_key_prefix = "environments"
}
}
|
狀態檔路徑將會是:environments/<workspace_name>/terraform.tfstate
狀態鎖定(State Locking)
什麼是狀態鎖定?
狀態鎖定是一種防止多個使用者同時修改狀態檔的機制。當一個操作正在進行時,其他操作將被阻擋,直到鎖定被釋放。
狀態鎖定的重要性
1
2
3
4
5
6
7
8
9
10
11
12
| 沒有狀態鎖定的風險:
┌─────────────┐ ┌─────────────┐
│ 使用者 A │ │ 使用者 B │
│ terraform │ │ terraform │
│ apply │ │ apply │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────────────────────────┐
│ terraform.tfstate │
│ (衝突發生!) │
└─────────────────────────────────┘
|
DynamoDB 狀態鎖定機制
當使用 DynamoDB 進行狀態鎖定時:
- 取得鎖定:Terraform 在 DynamoDB 表中建立一筆記錄
- 執行操作:進行 plan/apply 等操作
- 釋放鎖定:操作完成後刪除鎖定記錄
鎖定記錄範例:
1
2
3
4
5
6
7
8
9
10
| {
"LockID": "my-terraform-state-bucket/prod/terraform.tfstate",
"Info": {
"ID": "abc123-def456",
"Operation": "OperationTypeApply",
"Who": "user@hostname",
"Version": "1.5.0",
"Created": "2023-10-09T10:00:00Z"
}
}
|
處理鎖定問題
查看鎖定狀態
1
2
3
4
5
6
7
8
9
| # 如果遇到鎖定錯誤,會顯示鎖定資訊
terraform plan
# Error: Error acquiring the state lock
# Lock Info:
# ID: abc123-def456
# Path: my-terraform-state-bucket/prod/terraform.tfstate
# Operation: OperationTypeApply
# Who: user@hostname
# Created: 2023-10-09T10:00:00Z
|
強制解除鎖定
1
2
3
4
5
| # 使用 Lock ID 強制解除鎖定(謹慎使用!)
terraform force-unlock abc123-def456
# 確認解除
terraform force-unlock -force abc123-def456
|
跳過鎖定(僅限特殊情況)
1
2
| # 不建議在生產環境使用
terraform apply -lock=false
|
鎖定超時設定
1
2
| # 設定鎖定等待時間(預設為 0s)
terraform apply -lock-timeout=5m
|
狀態操作指令(State Commands)
Terraform 提供多種指令來管理和操作狀態檔。
列出狀態檔中的所有資源:
1
2
3
4
5
6
7
8
9
10
11
12
| # 列出所有資源
terraform state list
# 輸出範例:
# aws_vpc.main
# aws_subnet.public[0]
# aws_subnet.public[1]
# aws_internet_gateway.main
# aws_route_table.public
# 使用過濾器
terraform state list aws_subnet.*
|
顯示特定資源的詳細資訊:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 顯示資源詳細資訊
terraform state show aws_vpc.main
# 輸出範例:
# resource "aws_vpc" "main" {
# arn = "arn:aws:ec2:ap-northeast-1:123456789012:vpc/vpc-0123456789abcdef0"
# cidr_block = "10.0.0.0/16"
# enable_dns_hostnames = true
# enable_dns_support = true
# id = "vpc-0123456789abcdef0"
# instance_tenancy = "default"
# tags = {
# "Name" = "main-vpc"
# }
# }
|
移動或重新命名資源:
1
2
3
4
5
6
7
8
9
10
11
| # 重新命名資源
terraform state mv aws_vpc.main aws_vpc.primary
# 移動到模組
terraform state mv aws_vpc.main module.network.aws_vpc.main
# 從模組移出
terraform state mv module.network.aws_vpc.main aws_vpc.main
# 移動整個模組
terraform state mv module.old_name module.new_name
|
從狀態中移除資源(不會刪除實際資源):
1
2
3
4
5
6
7
8
| # 移除單一資源
terraform state rm aws_instance.example
# 移除模組中的資源
terraform state rm module.vpc.aws_subnet.public
# 移除整個模組
terraform state rm module.vpc
|
從遠端後端拉取狀態檔:
1
2
3
4
5
| # 拉取狀態並輸出到標準輸出
terraform state pull
# 儲存到檔案
terraform state pull > terraform.tfstate.backup
|
推送本地狀態到遠端後端:
1
2
3
4
5
| # 推送狀態(謹慎使用!)
terraform state push terraform.tfstate.backup
# 強制推送
terraform state push -force terraform.tfstate.backup
|
匯入現有資源到 Terraform 管理:
1
2
3
4
5
6
7
8
| # 匯入 VPC
terraform import aws_vpc.main vpc-0123456789abcdef0
# 匯入 EC2 執行個體
terraform import aws_instance.web i-0123456789abcdef0
# 匯入到模組
terraform import module.vpc.aws_vpc.main vpc-0123456789abcdef0
|
匯入前需要先在設定檔中定義資源:
1
2
3
4
| # 先定義資源區塊
resource "aws_vpc" "main" {
# 匯入後再填寫詳細設定
}
|
更新狀態檔以反映實際基礎設施:
1
2
3
4
5
| # 刷新狀態
terraform refresh
# 注意:在 Terraform 1.5+ 中,建議使用
terraform apply -refresh-only
|
狀態操作最佳實踐
| 操作 | 建議 |
|---|
| 狀態備份 | 操作前先執行 terraform state pull 備份 |
| 狀態移動 | 使用 terraform state mv 而非手動編輯 |
| 資源匯入 | 匯入後立即執行 terraform plan 驗證 |
| 狀態推送 | 除非必要,否則避免使用 state push |
| 鎖定解除 | 確認無其他操作進行中才使用 force-unlock |
進階技巧
狀態檔資料源
從其他狀態檔讀取資料:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 從其他專案讀取 VPC ID
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "network/terraform.tfstate"
region = "ap-northeast-1"
}
}
# 使用讀取的資料
resource "aws_instance" "web" {
ami = "ami-0123456789abcdef0"
instance_type = "t3.micro"
subnet_id = data.terraform_remote_state.network.outputs.public_subnet_ids[0]
}
|
敏感資料處理
1
2
3
4
5
| # 標記輸出為敏感
output "database_password" {
value = aws_db_instance.main.password
sensitive = true
}
|
狀態檔加密
使用 AWS KMS 加密狀態檔:
1
2
3
4
5
6
7
8
9
10
| terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
kms_key_id = "arn:aws:kms:ap-northeast-1:123456789012:key/12345678-1234-1234-1234-123456789012"
dynamodb_table = "terraform-state-locks"
}
}
|
總結
Terraform 狀態檔管理是基礎設施即程式碼實踐中的關鍵環節。透過本文介紹的概念和技術,您可以:
- 理解 State 檔:掌握狀態檔的結構、作用和重要性
- 選擇適當的後端:根據專案需求選擇本地或遠端狀態
- 配置 S3 後端:使用 AWS S3 和 DynamoDB 建立安全的遠端狀態管理
- 實施狀態鎖定:防止並行操作造成的衝突
- 操作狀態檔:使用各種指令管理和維護狀態
正確的狀態管理將大幅提升團隊協作效率和基礎設施的可靠性。
參考資源