iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
DevOps

AWS ECS + Gitlab + Laravel + Terraform 從入門到摔坑系列 第 21

Day 21 以 Gitlab 作為 Terraform backend

  • 分享至 

  • xImage
  •  

Day 13 講到 Terraform 會把 resource 狀態 state 存在 backend 裡,到目前為止我們的 backend 都是 local 檔案:

  • terraform.tfstate :記錄 remote resource state 的檔案

  • terraform.tfstate.backup :上述檔案的備份檔,有時候非正常中斷 apply 操作會導致 state file 整個被清空,可以用備份檔救回來

看過 terraform.tfstate 檔案內容的朋友應該知道它就是個文字檔、記錄著所有 terraform 管理的 resource 的資訊,其中包含 credential,例如 IAM user 的 access key id 及 secret。沒錯,它是明碼。所以前面有說 state 檔案不建議 commit 進 git repository,不然就所有 credential 永存 git history 了……安捏母湯~

另外就算 state 檔案裡沒有 credential,以多人協作來說,也不適合使用 local 檔案存放 state。跟程式碼一樣,多人共同改同一個檔案可能會造成 conflict,state 的 conflict 跟 code 不一樣,它會對應 cloud 上真實的 resource,這 conflict 很難解啊~

基於上述原因,我們要改用 remote 且對 state 有所保護的 backend,Gitlab 有提供管理 terraform state 的功能,我們把 state 轉移到 Gitlab 上吧~

Gitlab Terraform State 透過 Terraform 的 http backend 來保存 state file。Gitlab 裡的 terraform state 有 version,我們可以用 api 取得先前版本的 state。重要的是 Gitlab 會加密 state 檔案後才保存,透過 Gitlab API 存取 state 時會自動加解密。

設定 Backend block

backend 設定是放在 terraform block:

terraform {
  backend "remote" {
    organization = "example_corp"

    workspaces {
      name = "my-app-prod"
    }
  }
}

每份 terraform configuration 只能有一個 backend block。修改過 backend block 後,在執行 planapply 等等指令前,要先執行 terraform init 重新設定好 backend。可以使用的 backend 類型可以參考 官方文件

Migrate state

雖然 terraform 本來就有備份 state,但操作前還是多備份 terraform.tfstate 一下~

首先在 Gitlab 的 Settings > General 找到 Project nameProject ID。接著在 main.tf 定義 backend block,使用 http backend:

terraform {
  backend "http" {
  }
}

到 Gitlab 產生個 personal access token,權限要有 api scope :

https://ithelp.ithome.com.tw/upload/images/20231001/20160671GEaUa9KG8W.png

https://ithelp.ithome.com.tw/upload/images/20231001/20160671T46CwWr3PP.png

https://ithelp.ithome.com.tw/upload/images/20231001/20160671FzPyQ8Bpj3.png

下圖隱藏起來的字串就是 token(這頁只會顯示一次):

https://ithelp.ithome.com.tw/upload/images/20231001/20160671iLK9ESw8EP.png

接下來,執行 terraform init 進行 migrate state:

$ terraform init \
    -backend-config="address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-STATE-NAME>" \
    -backend-config="lock_address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-STATE-NAME>/lock" \
    -backend-config="unlock_address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-STATE-NAME>/lock" \
    -backend-config="username=<YOUR-USERNAME>" \
    -backend-config="password=<YOUR-ACCESS-TOKEN>" \
    -backend-config="lock_method=POST" \
    -backend-config="unlock_method=DELETE" \
    -backend-config="retry_wait_min=5" \
    -migrate-state

其中 <YOUR-PROJECT-ID> 要改用你的 Gitlab project ID,<YOUR-STATE-NAME> 可以指定 state 的名稱,可以用環境當作名稱,像是 staging<YOUR-USERNAME> 改成 Gitlab 帳號,<YOUR-ACCESS-TOKEN> 改成剛剛產生的 personal access token。-backend-config 設定的參數也可以寫在 backend block 裡,不過當然不建議把 token 寫進去。-migrate-state 會將目前 state 複製到新的 backend 也就是 Gitlab 去。下這個指令後會問你你要不要把現在在 local 的 state 轉移到新的 backend,我們就是要這麼做~輸入 yes

https://ithelp.ithome.com.tw/upload/images/20231001/201606718NYy2KtIwc.png

完成後,我們可以到 Gitlab 的 Terraform states 頁面看到轉移過去的 state:

https://ithelp.ithome.com.tw/upload/images/20231001/20160671NecSr0fuEE.png

右邊點點點裡的 Download JSON 可以下載 state 的 JSON 檔案,確認 state 有正確進入 Gitlab 後就可以把 local 的 terraform.tfstate* 刪除。

之後我們對 terraform 的操作都會存取 Gitlab 上的 state 檔案,所以在操作 terraform 時必須要連得到 Gitlab。

Gitlab Terraform state 的 lock 機制

為了避免多人協作造成 conflict,terraform 會在 apply 前將 state lock 起來,執行完才 unlock。state 在 locking 期間,另一個人想 apply 必須等到 state 的 lock 解開。之前 backend 是 local 檔案,幾乎是無法協作的,即使用某些同步檔案的方式也會有 conflict 的問題。backend 換成 Gitlab 後,terraform 就能對 state 做 lock 跟 unlock,讓我們可以多人使用 terraform 協作。

lock 中的 terraform state 會顯示 Locked:

https://ithelp.ithome.com.tw/upload/images/20231001/20160671Ne3Re1UVe8.png

有時候如果沒有適當結束 terraform 操作,會導致 state 卡在 Locked 但其實沒有人正在修改它。這時候只要確認不是真的有人在改 state,可以從 Actions 裡 unlock state。

Gitlab Terraform state version API (ref)

Gitlab 網頁介面目前還只能下載最新版本的 state,如果想取得舊版本的 state 得用 API。什麼時候會用到這個功能呢?像是在一陣混亂的操作後,不小心把 state 給清空了……需要舊版本救援……

取得某個版本的 state 的 curl 指令如下:

$ curl --header "Private-Token: [PERSONAL_TOKEN]" \
  "https://[GITLAB_BASE_URL]/api/v4/projects/[PROJECT_ID]/terraform/state/[STATE_NAME]/versions/[VERSION_SERIAL]"

其中 [PERSONAL_TOKEN] 要換成自己的 personal token,token 的 scope 有 api 而且帳號至少有這個 project 的 developer 就能夠取得 state file。[GITLAB_BASE_URL] 則是使用的 Gitlab 的網址,如果是在 Gitlab 官方網站就是 gitlab.com[PROJECT_ID] 是放 terraform state 的 Gitlab project 的 id,可以從 project 首頁看到。[STATE_NAME] 是想取得的 state 的名稱,例如 staging 。最後的 [VERSION_SERIAL] 就是 state 的版本了,它是一個流水號,目前(2023-10)沒有另外的 API 或介面可以看到有哪些版本號,一個小方法是到 Gitlab Terraform state 頁面,從 action 中的 Download JSON 的網址可以看到最新版本號。


上一篇
Day 20 讓 Terraform configuration 與 cloud resource 保持一致
下一篇
Day 22 ECS logging
系列文
AWS ECS + Gitlab + Laravel + Terraform 從入門到摔坑30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言