如何在GCP以及AWS設定 remote backend 管理 terraform 狀態檔

更新於 發佈於 閱讀時間約 30 分鐘


raw-image

👨‍💻簡介

terraform在每次執行terraform planterraform apply時,是如何知道應該要管理哪些資源?

其實就是透過在每次執行terraform時,將建立或要變更的資源都記錄在terraform.state這份狀態檔,預設檔案使用JSON格式。

假設建立一個google cloud storage的resource,tf設定檔如下:

resource "google_storage_bucket" "bucket" {
name = "terraform-alan-test-bucket"
location = "ASIA-EAST1"
storage_class = "STANDARD"
public_access_prevention = "enforced"
force_destroy = true
uniform_bucket_level_access = true
}

建立後的terraform.tfstate的一小部分如下:

{
"version": 4,
"terraform_version": "1.5.7",
"serial": 3,
"lineage": "abda0fda-b807-b8b3-0a36-8b0c2f92e3f5",
"outputs": {},
"resources": [
{
"module": "module.base-bucket",
"mode": "managed",
"type": "google_storage_bucket",
"name": "bucket",
"provider": "module.base-bucket.provider[\"registry.terraform.io/hashicorp/google\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"autoclass": [],
"cors": [],
"custom_placement_config": [],
"default_event_based_hold": false,
"effective_labels": {},
"encryption": [],
"force_destroy": true,
"id": "terraform-alan-test-bucket",
"labels": null,
"lifecycle_rule": [],
"location": "ASIA-EAST1",
"logging": [],
"name": "terraform-alan-test-bucket",
}
}
]
}
]
}

可以看到id的屬性,當每次執行plan或是apply時,terraform就是拿這個屬性從本地terraform設定檔與雲上資源做比對。

如果你的terraform用在個人專案,則將狀態檔存在本機即可;不過如果是一個團隊要使用terraform,則可能會遇到幾個問題:

  • 狀態檔需要共用:需要確保團隊成員都能正常存取相同的terraform狀態檔。
  • 鎖定狀態檔:需要確保團隊成員使用terraform進行操作時,只有一人能對狀態檔做修改,否則可能會因為多個terraform進程對狀態檔做併發更新,導致資料丟失或是狀態檔損壞。

要解決上述問題,常見的做法是將狀態檔進行版本控制(git),但因為某些原因,將狀態檔進行版控並不是一個好想法。有底下幾個原因:

  • 手動操作錯誤:有時候可能在操作terraform時,忘記拉取最新狀態檔,或是在執行完terraform後,忘記將狀態檔更新到版控。可能團隊成員使用到舊的狀態檔導致資源回滾。
  • 鎖定問題:多數的版本控制系統沒提供任何形式的鎖定,防止兩個團隊成員同時對同一份狀態檔執行操作。
  • 加密:terraform狀態檔的資料預設都是以明文形式儲存。但某些資源需要儲存敏感資料,像是資料庫創建時需要建立初始化使用者以及密碼,這些資料不應該以明文的形式儲存在版本控制。

因為以上原因,terraform官方推出 remote backend的支援,可以將狀態檔儲存在所使用的雲平台,而不是使用版本控制,而remote backend解決了剛才列出的三個問題:

  • 手動操作錯誤:terraform每次運行時會自動從 remote backend 載入狀態檔,並且每次運行後都會自動將狀態檔儲存在 remote backend,解決了手動操作錯誤。
  • 鎖定問題:大多數 remote backend 都支援鎖定。運行terraform時會自動將檔案進行上鎖,確保只會有一人進行操作。
  • 加密:大多數 remote backend 都支援狀態檔的傳輸中加密和靜態加密。

使用gcp平台推薦使用 google cloud storage,整理的原因如下:

  • 這是託管服務,不需部署和管理額外基礎設施即可使用。
  • gcs旨在實現99.99%的持久性和年度耐用性。

数据可用性和耐用性 | Cloud Storage | Google Cloud

  • gcs支援加密,預設使用server side encrypt,另外也可使用ckms服務

数据加密选项 | Cloud Storage | Google Cloud

  • 支援版本控制

使用对象版本控制 | Cloud Storage | Google Cloud

  • 費用便宜,使用免費額度即可滿足需求。

价格 | Cloud Storage | Google Cloud

GCP GCS作為terraform remote backend

要使用 Google cloud stroage作為 terraform remote backend,首先是先建立 gcs bucket。 在新資料夾中建立一個provider.tf檔案

terraform {
required_providers {
google = {
source = "hashicorp/google"
version = ">=4.0"
}
}
}

provider "google" {
region ="asia-east1"
}

接著再建立一個名為bucket.tf檔案,使用 google_storage_bucket resource 建立 gcs bucket。

resource "google_storage_bucket" "bucket" {
name = "terraform-alan-test-bucket"
location = "ASIA-EAST1"
storage_class = "STANDARD"
public_access_prevention = "enforced"
force_destroy = false
uniform_bucket_level_access = true
}

參數說明如下:

  • name:定義 gcs bucket 名稱
  • location:指定 bucket 儲存位置
  • storage_class:指定 bucket 儲存類型
  • public_access_prevention:設定公開存取防止策略,enforce表示不允許公開訪問
  • force_destroy:設定是否可強制刪除
  • uniform_bucket_level_access:設定是否啟用統一訪問控制

當執行完terraform init以及 terraform apply部署完後,就會看到gcs已經建立完成。但目前狀態檔還是儲存在本地。要將狀態檔儲存到 gcs bucket,需要再額外設定 remote backend,語法如下:

terraform {
backend "<BACKEND_NAME>" {
[CONFIG...]
}
}

其中 BACKEND_NAME是要使用的資源,CONFIG是對這個後端的參數設定。以下是 gcs bucket的後端設定:

terraform {
backend "gcs" {
bucket = "terraform-alan-test-bucket"
prefix = "bucket-state"
}
}

參數說明如下:

  • bucket:要使用的bucket name,這邊填入剛剛建立的 bucket
  • prefix:定義狀態檔的路徑和名稱,以上述為例,狀態檔會被存放在 bucket-state資料夾

要將狀態檔儲存到 gcs ,只需要重新執行 terraform init 指令。該指令不僅可以下載 provider code,還可以設定 terraform backend。此外,init的指令是冪等的,多次執行可以確保每次都是預期的行為。

> terraform init
Initializing the backend...
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "local" backend to the
newly configured "gcs" backend. No existing state was found in the newly
configured "gcs" backend. Do you want to copy this state to the new "gcs"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value:

執行後terraform會自動檢查本地已有的狀態檔,並跳出提示說要將狀態檔複製到 gcs backend。如果輸入 yes,則會看到以下內容:

Successfully configured the backend "gcs"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing modules...
Initializing provider plugins...
- Reusing previous version of hashicorp/google from the dependency lock file
- Using previously-installed hashicorp/google v5.3.0
Terraform has been successfully initialized!

執行完畢後可以到 gcs查看狀態檔是否已經儲存上去。

raw-image

如果之後有資源使用一樣的backend,terraform執行時就會自動從這個 gcs拉取最新的狀態,並且執行後會自動將最新狀態推送到這個 gcs。

當有兩人同時執行時,則第二個人會出現以下訊息:

> terraform plan
Acquiring state lock. This may take a few moments...

│ Error: Error acquiring the state lock

│ Error message: writing
"gs://terraform-alan-test-bucket/bucket-state/default.tflock" failed:
│ googleapi: Error 412: At least one of the pre-conditions you specified did not
│ hold., conditionNotMet
│ Lock Info:
ID: 1698225515771180
│ Path: gs://terraform-alan-test-bucket/bucket-state/default.tflock
│ Operation: OperationTypeApply
│ Who: alan_wang
│ Version: 1.5.7
│ Created: 2023-10-25 09:18:35.490893 +0000 UTC
│ Info:


│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.

這也確保了執行只會有一人,其他人要操作都會被擋下來。

AWS S3作為terraform remote backend

依照慣例先來個 provider.tf起手式

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.22.0"
}
}
}

provider "aws" {
alias = "ap-east-1"
region = "ap-east-1"
}

接著建立一個名為bucket.tf檔案

resource "aws_s3_bucket" "terraform_state" {
bucket = "terraform-alan-test-bucket"
provider = aws.ap-east-1
force_destroy = false
# Prevent accidental deletion of this S3 bucket
lifecycle {
prevent_destroy = true
}
}

參數說明如下:

  • bucket:定義 s3 bucket 名稱
  • provider:指定 bucket 儲存位置
  • force_destroy:設定是否可強制刪除
  • prevent_destroy:任何嘗試刪除該資源的操作都將導致terraform退出並顯示錯誤。

接下來要替s3增加一些額外的保護

首先新增版本控制,使用aws_s3_bucket_versioning resource開啟版本控制,確保每次更新都會創建新版。當出現問題時也能恢復成舊版:

resource "aws_s3_bucket_versioning" "enabled" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}

參數說明如下:

  • bucket:要啟用版本控制的 S3 bucket 的 ID
  • versioning_configuration
  • status:可用選項為EnabledDisabledSuspended,設為 “Enabled” 表示啟用版本控制

接著開啟加密設定,使用 aws_s3_bucket_server_side_encryption_configuration,開啟後寫入到這個 S3 的所有資料都會啟用 server 端的加密。確保狀態檔以及任何存在 S3 的敏感資料都在硬碟上加密:

resource "aws_s3_bucket_server_side_encryption_configuration" "server_side_encryption" {
bucket = aws_s3_bucket.terraform_state.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

參數說明如下:

  • bucket:要啟用版本控制的 S3 bucket 的 ID
  • apply_server_side_encryption_by_default:指定預設的 server 端加密設定。
  • sse_algorithm:設定 server 端加密算法,這裡使用的是 “AES256”。

補充一下其他可用選項

  1. AES256 (SSE-S3):
  • 描述:使用 Amazon S3 托管的密鑰(SSE-S3)進行加密。
  • 算法:AES-256。
  • 密鑰管理:由 Amazon S3 自動處理。

2. aws:kms (SSE-KMS):

  • 描述:使用 AWS Key Management Service (KMS) 托管的 CMK(客戶主密鑰)進行加密。
  • 算法:通常是 AES-256,但具體取決於 KMS。
  • 密鑰管理:由 AWS KMS 處理,允許更細緻的控制和審計跟踪。

3. aws:kms:dsse (SSE-KMS with Double-Wrap):

  • 描述:這是一種特殊的 KMS 加密方法,其中數據首先使用一個隨機數據加密密鑰 (DEK) 進行加密,然後 DEK 本身使用 KMS CMK 進行加密,從而實現雙重加密。
  • 密鑰管理:由 AWS KMS 處理。

區別:

  • 密鑰管理AES256 由 S3 自動管理,aws:kms 和 aws:kms:dsse 允許更多的控制和稽核,由 AWS KMS 處理。
  • 加密強度aws:kms:dsse 提供雙重加密,可提供更高的安全性。

推薦:

  • 對於大多數使用者,AES256 是一個簡單且安全的選擇。
  • 如果需要更細緻的密鑰管理、審計或控制,則推薦使用 aws:kms
  • 如果需要更高的安全性和加密強度,則可以考慮使用 aws:kms:dsse,但請注意,這可能會增加一些複雜性和成本。

如果開啟 aws:kms 時,推薦也需要設定 bucket_key_enabled,可減少對 kms請求,降低成本。 當使用SSE-KMS進行 server 端加密時,每次S3對象操作都會導致KMS請求,這可能會增加成本。使用S3 Bucket Keys,可以減少這些請求,從而減少成本。

審計:系統性的檢查和評估過程,用於確定某些標準、政策、法規或其他準則是否被遵循。

第三個設定是存取權的控制,阻止對 S3 bucket 的所有公開存取。

resource "aws_s3_bucket_public_access_block" "public_access" {
bucket = aws_s3_bucket.bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

參數說明如下:

  • bucket:要設定公開存取阻止的 S3 bucket ID。
  • block_public_acls:設定為 true 阻止公開 ACL。
  • block_public_policy:設定為 true 阻止公開策略。
  • ignore_public_acls:設定為 true 忽略公開 ACL。
  • restrict_public_buckets:設定為 true 限制公開 bucket。

最後一個要設定的是用來當作鎖的 DynamoDB table,使用 aws_dynamodb_table 的強一制性讀取和條件寫入,達到分散式鎖系統。

resource "aws_dynamodb_table" "terraform_locks" {
name = var.dynamodb_table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}

參數說明如下:

  • name:這是 DynamoDB 表的名稱。
  • billing_mode:這指定了計費模式,能使用的值有PROVISIONEDPAY_PER_REQUEST,這裡使用"PAY_PER_REQUEST",依照需請求計費。另一種則是確定有固定的讀寫,則可以設定讀寫量。

BillingModeSummary — Amazon DynamoDB

  • hash_key:這是表的主鍵名稱。
  • attribute
  • name:這是屬性的名稱,這裡是 “LockID”
  • type:這是屬性的類型,可以用的類型有S(字串)、N(數字)、B(二進制)

依照上面的設定,即可完成s3以及dynamodb的建立,一樣下 terraform init terraform apply完成建置。接著來設定 remote backend

terraform {
backend "s3" {
bucket = "terraform-alan-test-bucket"
key = "bucket-state/terraform.tfstate"
region = "ap-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}

參數說明如下:

  • bucket:要使用的bucket name,這邊填入剛剛建立的 bucket
  • key:要建立在 s3 的路徑和名稱
  • region:指定 s3 的區域
  • dynamodb_table:用來儲存狀態檔的鎖定,防止多人同時使用 terraform 操作同一份檔案,可防止同時修改,避免數據不一致
  • encrypt:決定是否加密 terraform state 檔案,設定為 true 將使用 s3 的 sse 進行加密
  • prefix:定義狀態檔的路徑和名稱,以上述為例,狀態檔會被存放在 bucket-state資料夾

一樣重下 terraform init 重設 backend

> terraform init
Initializing the backend...
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "local" backend to the
newly configured "s3" backend. No existing state was found in the newly
configured "s3" backend. Do you want to copy this state to the new "s3"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value:

執行後terraform會自動檢查本地已有的狀態檔,並跳出提示說要將狀態檔複製到 gcs backend。如果輸入 yes,則會看到以下內容:

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing modules...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.22.0
Terraform has been successfully initialized!

一樣執行完畢後上去 s3 看一下狀態檔是否已經儲存上去

raw-image

當有兩人同時執行時,則第二個人會出現以下訊息:

> terraform apply
Acquiring state lock. This may take a few moments...

│ Error: Error acquiring the state lock

│ Error message: ConditionalCheckFailedException: The conditional request failed
│ Lock Info:
ID: 46f562fd-ab59-a657-6984-5e164ec4bf
│ Path: terraform-alan-test-bucket/bucket-state/terraform.tfstate
│ Operation: OperationTypeApply
│ Who: alan_wang@Alan-wangdeMacBook-Pro.local
│ Version: 1.5.7
│ Created: 2023-10-26 09:33:34.15838 +0000 UTC
│ Info:


│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.

📚Reference

留言
avatar-img
留言分享你的想法!
avatar-img
Alan的開發者天地
19會員
83內容數
golang
Alan的開發者天地的其他內容
2024/09/15
本文介紹如何使用 MongoDB 的命令行工具 Mongorestore 將先前備份的資料還原到資料庫中。Mongorestore 支援資料庫的整體恢復、特定集合的恢復,以及從遠端伺服器進行恢復等功能。無論是初學者還是有經驗的使用者,都能夠快速掌握如何使用 Mongorestore 工具。
Thumbnail
2024/09/15
本文介紹如何使用 MongoDB 的命令行工具 Mongorestore 將先前備份的資料還原到資料庫中。Mongorestore 支援資料庫的整體恢復、特定集合的恢復,以及從遠端伺服器進行恢復等功能。無論是初學者還是有經驗的使用者,都能夠快速掌握如何使用 Mongorestore 工具。
Thumbnail
2024/09/08
👨‍💻簡介 在資料庫管理和系統維護中,備份是非常重要的一環。對於使用 MongoDB 的開發者和資料庫管理員來說,mongodump 是一個非常實用的命令行工具,能夠快速且輕鬆地完成資料庫的備份和恢復。無論是進行資料遷移、系統升級,還是面對突發的故障,mongodump 都能提供穩定的資料保護
Thumbnail
2024/09/08
👨‍💻簡介 在資料庫管理和系統維護中,備份是非常重要的一環。對於使用 MongoDB 的開發者和資料庫管理員來說,mongodump 是一個非常實用的命令行工具,能夠快速且輕鬆地完成資料庫的備份和恢復。無論是進行資料遷移、系統升級,還是面對突發的故障,mongodump 都能提供穩定的資料保護
Thumbnail
2024/06/20
本文介紹如何對 Telegram 憑證監控機器人的代碼進行優化,包括新增指令、讀取變數、提高可讀性和可維護性。
Thumbnail
2024/06/20
本文介紹如何對 Telegram 憑證監控機器人的代碼進行優化,包括新增指令、讀取變數、提高可讀性和可維護性。
Thumbnail
看更多
你可能也想看
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
在本篇文章中,將會設定 Cloud Run,以便每當將程式修改並推送到 GitHub 時,它都會使用 Cloud Build 自動構建和部署應用程序的最新版本。
Thumbnail
在本篇文章中,將會設定 Cloud Run,以便每當將程式修改並推送到 GitHub 時,它都會使用 Cloud Build 自動構建和部署應用程序的最新版本。
Thumbnail
本文介紹如何在GCP上使用Terraform建立CloudFlare DNS解析和模組化。通過閱讀本文,可以瞭解如何設置Terraform建立CloudFlare DNS解析以及取得GCS上的Terraform state file並透過Terraform建立CloudFlare DNS解析的步驟。
Thumbnail
本文介紹如何在GCP上使用Terraform建立CloudFlare DNS解析和模組化。通過閱讀本文,可以瞭解如何設置Terraform建立CloudFlare DNS解析以及取得GCS上的Terraform state file並透過Terraform建立CloudFlare DNS解析的步驟。
Thumbnail
👨‍💻簡介 這篇文章將會說明如何快速在 Google Cloud Platform 上使用 Terraform 建立外部與內部的全球 IP 。 前提條件 Google Cloud Platform (GCP) 帳號: 確保有一個有效的 GCP 帳號。 安裝Terraform: 還沒安裝可
Thumbnail
👨‍💻簡介 這篇文章將會說明如何快速在 Google Cloud Platform 上使用 Terraform 建立外部與內部的全球 IP 。 前提條件 Google Cloud Platform (GCP) 帳號: 確保有一個有效的 GCP 帳號。 安裝Terraform: 還沒安裝可
Thumbnail
👨‍💻 簡介 這篇文章將會說明如何快速在 Google Cloud Platform 上使用 Terraform 建立外部和內部的區域 IP 。
Thumbnail
👨‍💻 簡介 這篇文章將會說明如何快速在 Google Cloud Platform 上使用 Terraform 建立外部和內部的區域 IP 。
Thumbnail
👨‍💻簡介 terraform在每次執行terraform plan或terraform apply時,是如何知道應該要管理哪些資源? 其實就是透過在每次執行terraform時,將建立或要變更的資源都記錄在terraform.state這份狀態檔,預設檔案使用JSON格式。
Thumbnail
👨‍💻簡介 terraform在每次執行terraform plan或terraform apply時,是如何知道應該要管理哪些資源? 其實就是透過在每次執行terraform時,將建立或要變更的資源都記錄在terraform.state這份狀態檔,預設檔案使用JSON格式。
Thumbnail
今天簡單演示如何在Kubernetes cluster利用local-path provider建構出storage class來提供應用程式進行資料的存放。
Thumbnail
今天簡單演示如何在Kubernetes cluster利用local-path provider建構出storage class來提供應用程式進行資料的存放。
Thumbnail
Prerequisites 建立 S3 bucket 設定 IAM role for OpenSearch (TheSnapshotRole) { "Version": "2012-10-17", "Statement": [{ "Action": [
Thumbnail
Prerequisites 建立 S3 bucket 設定 IAM role for OpenSearch (TheSnapshotRole) { "Version": "2012-10-17", "Statement": [{ "Action": [
Thumbnail
1.首先 這是 “AWS 上的工作負載發現” 的解決方案,我嘗試了一下,覺得很方便,所以介紹一下~ https://aws.amazon.com/tw/solutions/implementations/workload-discovery-on-aws/ 2. 部署 官方提供 CloudF
Thumbnail
1.首先 這是 “AWS 上的工作負載發現” 的解決方案,我嘗試了一下,覺得很方便,所以介紹一下~ https://aws.amazon.com/tw/solutions/implementations/workload-discovery-on-aws/ 2. 部署 官方提供 CloudF
Thumbnail
宣告式管理是一種管理方法,其中您描述系統或資源的期望狀態,而不必關心實際如何達到該狀態。kubectl apply 命令在 Kubernetes 中實現了這種宣告式管理方式,以下是執行指令時的運作流程...
Thumbnail
宣告式管理是一種管理方法,其中您描述系統或資源的期望狀態,而不必關心實際如何達到該狀態。kubectl apply 命令在 Kubernetes 中實現了這種宣告式管理方式,以下是執行指令時的運作流程...
Thumbnail
最近在研究terraform與ansible的整合,不過還在摸索中,機器的版控對於維運來講相對重要,這時候有terraform的出現,在建立機器後會隨之產生機器的狀態檔,而ansible則是在建立好機器後幫助我完成我所需的機器相關設定 這是一款terraform結合ansible的小玩具,在gcp環
Thumbnail
最近在研究terraform與ansible的整合,不過還在摸索中,機器的版控對於維運來講相對重要,這時候有terraform的出現,在建立機器後會隨之產生機器的狀態檔,而ansible則是在建立好機器後幫助我完成我所需的機器相關設定 這是一款terraform結合ansible的小玩具,在gcp環
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News