介紹
Terraform 在 Github 上有一萬九千多顆星星(截自發文日)的開源專案,由 HashiCorp 這間專注於 DevOps 工具開發的公司所維護,主要透過 DSL 編寫定義檔,管理跨雲端架構,讓架構也可以程式碼化,近一步更好的協作
、版本控制
等好處,達到 Infrastructure as code 的目標。
Terraform 可以達到以下幾件事:
- 架構代碼化
Terraform 採用宣告式程式語言(declarative language)的 DSL,但同樣提供基礎的程式語言該有的功能,例如變數、輸入輸出、模組化等,其中模組化也支援加載外部模組,不用擔心違反 DRY,內建一些函示也都很好用; 當你有多個環境要部署時,可以用同樣的架構但是不同的機器規格與參數,管理上很方便 - 跨平台服務
Azure / GCP / AWS / Heroku 都可以,其他的工具包山包海,可以參考文件 Providers - 自動管理架構升級
架構異動時,Terraform 會自動更新或替換正確的資源,同時也可以一鍵刪除 - 團隊協作
提供多樣的解決方案,可以用官方的 Terraform Cloud 或 AWS S3 等,在團隊內共同管理 - 與現有架構整合
Terraform 提供兩種方式與既有架構整合,一是維持唯讀型態只存取資源(例如讀 AWS arn 綁定到 Lambda 上)、二是 Import 資源一並由 Terraform 管理(增加、刪除、修改) - DX 很好
Developer Experience 還不賴,官方的文件、教學,以及整體的設計上都很友善,錯誤也會很直接顯示哪一行的哪一部分語法錯誤,學習上 Debug 上都很容易,HashiCorp 員工有分享這是他們在 0.12 很大的修正,讓用戶更快找出錯誤是他們重視的一環
這次目標跟上次的 CDK 研究一樣,部署一個每五分鐘執行的 Lambda,並分佈到多個區域,CDK 教學連結 AWS-CDK教學 — Infrastructore As Code 用程式碼管理架構
事前準備
請先安裝 Terraform,並設定好 AWS configuration,也可以先玩過官方教學 Terraform getting started; 另一個很棒的參考資料 An Introduction to Terraform,系列文超仔細也超實用,比官方文件還推薦,作者也有出書,有機會應該會入手
部署單區域的 Lambda 與 IAM Role
創建一個檔案,先命名為 main.tf
,在 Terraform 中檔案分成 root module
與 module
,沒有特別宣告是 module 則為 root module,目錄下可以有多個 root module,檔案名稱沒有進入點問題,只要結尾是 .tf
即可
首先第一步,先部署單一區域的 Lambda,與建立對應需要的 IAM Role,以下程式碼主要做幾件事
- 宣告 aws 部署的區域
- 建立新的 IAM role 命名為 iam_for_lambda,並給予調用 lambda 的權限
- 建立 IAM Policy 命名為 lambda_logging,給予 Cloudwatch log 權限
- 將 IAM Policy 賦予 IAM role,lambda_logging 給 iam_for_lambda
- 等等 Lambda 會用到一些 node_modules,建立 Lambda layer 命名為 lambda-layer_fetch
- 建立 Lambda function aws_lambda_function,綁定 lambda-layer_fetch 與執行角色 iam_for_lambda
寫完介紹,剛好一步對照一塊程式碼,如果對 AWS 有點熟悉的人應該可以很快理解語法,尤其是變數命名跟後台設定很雷同,所以上手相當輕鬆
|
|
Provider
指定資源是套用在哪個平台,目前是指定在 us-east-1 AWS 上,如果沒有指定 alias
則代表是預設的 provider
Resource
命名的方式是
|
|
資源參數對應資源類型,可以從文件中找範例與定義的方式,資源的定義依賴於 Provider 平台的不同,可以指定 provider
,不指定則用預設
除了個別的資源定義外,像有些資源會相依,例如 Lambda 要綁定特定的 IAM Role,注意到這邊的寫法是
|
|
要引用其他資源的參數,需要用"${資源類型.資源名稱.arn}"
,arn 是 AWS 用來識別資源的全域 ID,透過這樣的方式就能夠綁定資源,這邊我指定要用 Lambda 的 Execution Role 是 iam_for_lambda 這個 Role。
另外為了效率,定義的資源會並行建立,但有些資源有相依性,Terraform 會自動處理相依性,所以上述的資源理論上要 Policy > Role > Lambda Layer > Lambda,但我們不用宣告 Terraform 會自行處理;
但有時候相依性不明顯或是有特殊需求,可以顯示宣告 depends_on
。
Lambda Function 建立完成後,如果只是要單純更改 Lambda 內容而不調整架構,可以宣告
|
|
source_code_hash
是指說如果 hash 值改變就更新 Lambda 內容,而 filebase64sha256()
是 Terraform 的內建函示,自動用 sha 256 算出檔案 hash 值並用 base64 編碼
題外話,Lambda 的 zip file 記得解壓縮後不要有額外的資料夾,不然會失敗,正確應該要是
|
|
部署架構
編寫好架構,此時要調用 Terraform CLI 來部署架構 首先初始化環境與載入需要的執行資源
$ terraform init
一開始 Terraform 並不知道建立的 Provider 是誰,直到初始化才會下載對應的 Library,放在專案路徑底下的 .terraform
資料夾下
成功後,就可以部署架構了
$ terraform apply
此時 Terraform 會列出更動的資源,+
代表需要新建的資源、-
代表會被刪除的資源、~
代表會被更新的資源,注意資源更新可是刪除舊的資源部署新的資源,依照各家 provider 的 API 而有所不同,需要特別留意
如果確認就輸入 “yes”,等 Terraform 幫忙部署
這樣就完成了,後續有什麼調整就重複 $ terraform apply
步驟,可以到 AWS 後台確認資源的建立狀況
加上 Cloudwatch
這一段雷同,補上 cloudwatch event rule / cloudwatch event targe,最後別忘了要加綁定 lambda permission 不然觸發 Lambda 會失敗
|
|
Import 現有的 IAM Role
回過頭說一下之前採用 AWS CDK 的最大問題,當初研究時沒有看到 AWS CDK 與現有架構的整合,這導致公司要採用需要很大的決心,或是只能用在測試或新的環境建設,沒有辦法 graceful 轉移,這點我覺得對於要導入新技術來說,有點麻煩,尤其是架構這麼重要的地方;
另一點是角色的權限管理,公司都會有針對不同的職位給予不同的權限,AWS CDK 預設就要 CreateRole 等權限,基本上很難直接要到這麼高的權限,也是當初要在公司專案嘗試 AWS CDK 最大失敗的原因
Terraform 現行支援 import
既有的資源,但是資源內容要自己填寫,未來宣稱會支援自動載入內容;existed_role
是我預先在 AWS 創建的 IAM Role,權限跟上面的 iam_for_lambda
一樣,記得要先在設定檔宣告,接著執行指令就完成了,之後 terraform destroy
也會一並刪除 (需要留意)
$ terraform import aws_iam_role.existed_role existed_role
|
|
Variables - 抽離參數
目前的參數都是寫死的,例如說部署的區域、Lambda 名稱等,Terraform 支援 variable 定義,可以有以下幾種類型
- string
- boolean
- number
- set
- map
- object (等同於 map,但會蓋過 map)
- tuple
如果要在參數使用變數的話,必須要先在資源檔 .tf
宣告
|
|
type 必填,但是 default 跟 description 不用,當沒有 default 且後續變數沒有賦值的話,在 >$terraform apply
時會中斷要求輸入
在資源定義檔上,可以採用 var.變數名稱
|
|
接著,透過以下幾種方式賦值給 variable
- 環境變數
1 2
$export TF_VAR_image_id=ami-abc123 $export TF_VAR_availability_zone_names='["us-west-1b","us-west-1d"]'
terraform.tfvars
檔案中1 2
image_id = "ami-abc123" availability_zone_names=["us-west-1b","us-west-1d"]
terraform.tfvars.json
檔案中1 2 3 4
{ "image_id": "ami-abc123", "availability_zone_names": ["us-west-1a", "us-west-1c"] }
*.auto.tfvars
或是*.auto.tfvars.json
,順序按照檔名- 在 CLI 執行時指定
-var
-var-file
$terraform apply -var=“image_id=ami-abc123”
如果有同樣的變數名稱,按照上面的規則順序後者蓋過前者,例如 -var
會蓋過其他檔案的宣告
Output - 輸出參數
對於 root module 來說,設定 output 會在 > $terraform apply
時打印,例如說 EC2 Instance 的 public DNS等;
對 module 來說,Output 等同於 function 的 return value,決定哪些資源讓外部讀取
在範例程式的目錄下有獨立的 output.tf
|
|
會在 apply 成功後打印出來
|
|
State - Terraform 如何掌管架構的更動
當架構異動的時候,Terraform 如何知道前後架構的差異呢?
每次在執行 $ terraform plan
時,專案目錄底下有 terraform.tfstate
檔案,用 JSON 描述架構中的所有資源,每當下次執行 $ terraform plan
時,Terraform 會根據 tfstate 中的資源 ID 取得最新的資訊,接著與描述檔做 diff 決定哪部分資源要更新
當我們要跨團隊協作時,就需要把 terraform 描述檔 + terraform.tfstate 與團隊共享,此時會有有三個要素
- 共享檔案與版本控制
檔案共享是最基本的協作必備條件,同時將程式碼做版本控制也是很必要的功能 - Lock 機制
當共享了之後,Lock 就變成是必須考量的因素,避免團隊同時多人同步修改,造成前後衝突的狀況 - 獨立不同的 state 狀態
在實際應用上,可能會有 development / staging / production 不同環境,希望共用程式碼建立雷同的架構,但又因為環境不同希望有不同的配置,例如機器大小或 VPC 等,此時就需要考量如何獨立不同環境
在跨同團隊協作很容易想到 git
,但嚴格來說 git 只能滿足第一點,所以在 Terraform 可以指定不同的 state 管理方式,除了官方的 Terraform Cloud,可以採用 AWS S3 + DynamoDB
達到上述的條件,並附帶版本控制,再之後會更詳細的描述操作流程,詳情可以參考 How to manage Terraform state
結語
就這樣完成了單區域的 Lambda 部署與最基本的 Terraform 學習,以下是這次教學的程式碼 terraform-investigation
下一篇將整個架構模組化,並一鍵部署多個區域