Skip to content

如何導入 Terragrunt,Terragrunt 好處是什麼?

Notifications You must be signed in to change notification settings

880831ian/terragrunt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

terragrunt

我們接續上一篇的 如何將 Terraform 改寫成 module ? ,我們已經將 Terraform 改成 module 的方式來進行管理,但當我們要管理的資源越來越多,且有分不同的專案時,整個服務架構會長的像以下:


.
├── modules
│   └── google_compute_instance
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
└── projects
    ├── gcp-1234
    │   ├── aaa
    │   │   ├── backend.tf
    │   │   ├── main.tf
    │   │   └── provider.tf
    │   ├── bbb
    │   │   ├── backend.tf
    │   │   ├── main.tf
    │   │   └── provider.tf
    │   └── ccc
    │       ├── backend.tf
    │       ├── main.tf
    │       └── provider.tf
    ├── gcp-2345
    │   ├── aaa
    │   │   ├── backend.tf
    │   │   ├── main.tf
    │   │   └── provider.tf
    │   ├── bbb
    │   │   ├── backend.tf
    │   │   ├── main.tf
    │   │   └── provider.tf
    │   └── ccc
    │       ├── backend.tf
    │       ├── main.tf
    │       └── provider.tf
    └── gcp-3456
        ├── aaa
        │   ├── backend.tf
        │   ├── main.tf
        │   └── provider.tf
        ├── bbb
        │   ├── backend.tf
        │   ├── main.tf
        │   └── provider.tf
        └── ccc
            ├── backend.tf
            ├── main.tf
            └── provider.tf

15 directories, 30 files

這邊的範例是以不同專案來分,再分區不同的服務,每個服務裡面都會有 backend.tfmain.tfprovider.tf 檔案所組成,我們可以來比較一下 gcp-1234 的 aaa 服務以及 gcp-2345 的 aaa 服務差異:


圖片


可以看到在 backend.tf 除了 prefix 路徑以外,其他設定也都一樣,但因為 Terraform 本身沒辦法透過帶入參數的方式來設定 backend.tf 後端部分,所以必須要先寫好每個服務所存放的後端位置,十分的不方便。

terraform {
  backend "gcs" {
    bucket      = "terragrunt-tfstate"
    prefix      = "/gcp-1234-aaa"
  }
}

為了減少上述這些需要一直重複寫差不多檔案的工作內容,因此有了 Terragrunt 這個工具,Terragrunt 是 Terraform 的包裝器,可以彌補 Terraform 上的一些缺陷,並且讓我們的 IaC 更貼近 DRY 原則


{{< admonition note "這邊說明一下 DRY 原則" >}} DRY 全名是 Don’t repeat yourself,也就是不要做重複的事情,能夠一次做完的就不要重複的去做。 {{< /admonition >}}


Terragrunt 安裝方式

那要怎麼使用 Terragrunt 呢!?

第一步當然是安裝它囉,我們系統是 macOS,所以我們安裝方式是 Homebrew 來進行安裝:

brew install terragrunt

接著我們的指令會從 terraform XXX 變成以下:

terragrunt plan
terragrunt apply
terragrunt output
terragrunt destroy

Terragrunt 會將所有命令、參數和選項直接轉發到 Terraform。(所以我們也需要下載 Terraform)

Terragrunt 的預設檔案名稱是 terragrunt.hcl ,Terragrunt 的設定檔案基本上與 Module 差不多,只是有更多更方便的變數可以使用。


Terragrunt 好處


接著介紹一下 Terragrunt 的好處:

  1. 方便管理後端狀態設定

  2. 將後端存儲桶納入管理

  3. 使用 generate 自動生成檔案

  4. 使用 include 檔案來達到 DRY 原則

  5. 管理 Module 之間的依賴性

  6. 產生依賴關聯圖


方便管理後端狀態設定


首先第一個方便管理後端狀態設定,也就是我們上面提到的 backend.tf 設定。在 Terraform 原生為了區別不同專案不同服務的狀態檔,就必須先寫好每個儲存的路徑,但使用 Terragrunt,可以先在該目錄下,也就是 gcp-3456 目錄下先寫一個設定檔案 (我們以 gcp-3456 專案為例),讓底下的 aaa、bbb、ccc 服務可以去 include 它,我們就不需要每個服務都寫幾乎差不多的設定檔,接著我們在 gcp-3456 資料夾下新增 terragrunt.hcl 檔案來說明:

remote_state {
  backend = "gcs"
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite"
  }
  config = {
    bucket  = "terragrunt-tfstate"
    prefix = "${path_relative_to_include()}"
  }
}

這邊的設定其實跟之前的 backend.tf 差不多,只是後端現在儲存的 block 改叫做 remote_state,可以看到 backend 設定我們一樣是存在 gcs 上,generate 這個 block 它會判斷 backend.tf 檔案是否存在,如果沒有它就會幫我們建立,其中設定檔案內容是將狀態檔案存在 terragrunt-tfstate 這個 bucket,並透過${path_relative_to_include()} 這個變數來自動帶入有 include 這份檔案的路徑,並在相對路徑產生 backend.tf 檔案。


有點抽象,所以我畫一個比較簡單的架構圖來做說明一下,假設現在有三個服務,如下:

.
├── aaa
│   └── terragrunt.hcl
├── bbb
│   └── terragrunt.hcl
├── ccc
│   └── terragrunt.hcl
└── terragrunt.hcl

我們剛剛的後端設定是寫在此根目錄的 terragrunt.hcl 檔案(第 8 行),然後 ccc 這個服務去 include 根目錄的 terragrunt.hcl 檔案, Terragrunt 就會自動幫你產生以下的 backend.tf 檔案:

terraform {
  backend "gcs" {
    bucket      = "terragrunt-tfstate"
    prefix      = "/ccc"
  }
}

這樣就可以省下我們要重複寫 backend.tf 的時間,在維護上也會更加的方便。


將後端存儲桶納入管理

接著,大家有沒有想過,我們都已經使用 Terraform 來管理 IaC ,並把狀態檔案放到 gcs 上面來保存,但一開始還沒有用 Terraform 管理 gcs 的資源,我們還需要在設定 backend.tf 前,先手動去新增一個 gcs ,才能來存放 tfstate 狀態檔案呢?


因此在 Terragrunt remote_state 的 config 時,可以多設定 gcs 的 project id 以及 location,Terragrunt 在初始化後端時,會檢查是否有該 gcs bucket,如果沒有就會自動建立,設定檔如下:

remote_state {
  backend = "gcs"
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite"
  }
  config = {
    project = "gcp-xxxxxx"
    location = "asia"
    bucket  = "terragrunt-tfstate"
    prefix = "${path_relative_to_include()}"
  }
}

使用 generate 自動生成檔案

在剛剛我們可以使用 generate 來自動 backend.tf 檔案,那代表我們也可以把每個 provider.tf 的內容,也透過 generate 來生成,如下:

generate "provider" {
  path = "provider.tf"
  if_exists = "overwrite"
  contents = <<EOF
terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "~> 4.48.0"
    }
  }
}
EOF
}

這樣子每個 include 這份設定檔的服務除了 backend.tf 檔案以外,還會自動產生 provider.tf 檔案。


使用 include 檔案來達到 DRY 原則

我們搞定了 backend.tfprovider.tf 後,還剩下 main.tf,所以我們也將它改成 Terragrunt 的格式如下:

terraform {
  source = "${get_path_to_repo_root()}/modules/google_compute_instance"
}

include "root" {
  path = find_in_parent_folders()
}

inputs = {
  instance_name = "gcp-3456-ccc"
  machine_type  = "e2-small"
  instance_zone = "asia-east1-b"
  instance_tags = []
  instance_labels = {}
  boot_disk_auto_delete = true
  boot_disk_image_name  = "debian-cloud/debian-10"
  ... 設定部分省略 ...
}

這邊可以看到 terraform source 它就是我們使用 module 的路徑,也可以用 ${get_path_to_repo_root()} 這個變數他會自動抓該專案的根目錄,我們就不需要去特別設定。


此外也可以將 module 獨立成一個專案,或是使用其他人寫好的 module,在 source 的時候可以使用 git::https://[gitlab-網址]/sre/terraform/module.git//google_compute_address 的方式來取得 module,可以設定要使用哪個分支或是 tag,只需要在網址後面加上,?ref=[分之 or tag 名稱] 即可,這樣可以讓開發中的 module 不會影響到線上其他正在使用中的 module 設定。

(module.git 後面的 // 是 Terraform module source 的規則,如果不加會跳警告訊息)


接著我們可以看到 include "root" {} 這段,裡面有使用 find_in_parent_folders 這邊變數,他就是上面的提到會自動去抓放在父資料夾的 remote_stategenerate terragrunt.hcl 檔案。


後面的 input 就跟使用 module 時一樣,將 module 的參數帶入即可。

補充:所以我們也可以把一些通用的設定寫在根目錄的 terragrunt.hcl 檔案,例如專案的 id,可以寫以下內容來讓 include 它的檔案吃到同一個參數設定:

inputs = {
  project_id = "gcp-3456"
}

管理 Module 之間的依賴性

由於 Terragrunt 是把每個服務拆分成最小化,沒辦法把使用不同 module 的資源放在一起(單純使用 module 的話,可以一次 source 多了 module,並把他放在同一個 tf 檔案中),那像是我們建立 k8s 會使用到 google_container_clustergoogle_container_node_pool 兩種不同的 module 該怎麼辦呢?


首先我們先在 modules 資料夾放上 google_container_clustergoogle_container_node_pool 兩個 module 的設定檔案,詳細程式可以點我前往

├── modules
│   ├── google_container_cluster
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── google_container_node_pool
│       ├── main.tf
│       └── variables.tf

在 projects 底下新增 gke 資料夾,新增 terragrunt.hcl 來放 remote_state provider 的設定,並區分兩個資料夾,分別是 cluster 資料夾來存放 cluster 資訊,以及 test 資料夾來存放 test node-pool 資訊:

└── projects
    └── gke
        ├── cluster
        │   └── terragrunt.hcl
        ├── terragrunt.hcl
        └── test
            └── terragrunt.hcl

cluster 的 terragrunt.hcl 檔案如下:

terraform {
  source = "${get_path_to_repo_root()}/modules/google_container_cluster"
}

include {
  path = find_in_parent_folders()
}

inputs = {
  cluster_name             = "tf-test"
  cluster_location         = "asia-east1-b"
  node_locations           = []
  cluster_version          = "1.24.12-gke.500"
  network_name             = "projects/gcp-202011216-001/global/networks/bbin-testdev"
  subnetwork_name          = "projects/gcp-202011216-001/regions/asia-east1/subnetworks/bbin-testdev-dev-platform"
  node_max_pods            = 64
  remove_default_node_pool = true
  initial_node_count       = 1
  enable_shielded_nodes    = false
  resource_labels = {
    "dept" : "pid",
    "env" : "dev",
    "product" : "bbin"
  }
  dns_enabled                  = false
  cluster_dns                  = "PROVIDER_UNSPECIFIED"
  cluster_dns_scope            = "DNS_SCOPE_UNSPECIFIED"
  private_cluster_ipv4_cidr    = "172.16.0.176/28"
  binary_authorization_enabled = true
  binary_authorization         = "DISABLED"
}

test node-pool 檔案如下:

terraform {
  source = "${get_path_to_repo_root()}/modules/google_container_node_pool"
}

include {
  path = find_in_parent_folders()
}

dependency "cluster" {
  config_path = "../cluster"
}

inputs = {
  cluster_name      = dependency.cluster.outputs.cluster_name
  cluster_location  = dependency.cluster.outputs.cluster_location
  cluster_version   = dependency.cluster.outputs.cluster_version
  node_pool_name    = "test"
  node_count        = 1
  node_machine_type = "e2-small"
  node_disk_size    = 100
  node_disk_type    = "pd-standard"
  node_image_type   = "COS_CONTAINERD"
  node_oauth_scopes = [
    "https://www.googleapis.com/auth/devstorage.read_only",
    "https://www.googleapis.com/auth/logging.write",
    "https://www.googleapis.com/auth/monitoring",
    "https://www.googleapis.com/auth/service.management.readonly",
    "https://www.googleapis.com/auth/servicecontrol",
    "https://www.googleapis.com/auth/trace.append"
  ]
  node_tags                        = []
  node_taint_enabled               = false
  node_taint_key                   = ""
  node_taint_value                 = ""
  node_taint_effect                = ""
  auto_repair                      = true
  auto_upgrade                     = true
  upgrade_max_surge                = 1
  upgrade_max_unavailable          = 0
  upgrade_strategy                 = "SURGE"
  autoscaling_enabled              = true
  autoscaling_max_node_count       = 2
  autoscaling_min_node_count       = 1
  autoscaling_total_max_node_count = 0
  autoscaling_total_min_node_count = 0
}

上面兩個檔案分別是 cluster 的設定,以及 test node-pool 的設定,裡面的設定,上面基本都有提過,這邊要提的是 dependencydependency 他是 Terragrunt 提供讓我們可以方便地去管理 IaC 之間的相依性,像是我們這邊,需要先建立好 cluster 才能建立 node_pool,此時就可以依靠 dependency block 來完成需求。

( 靠 dependency 來取得 cluster 的資訊,並帶入 node_pool 中 )


此時的執行指令是在 gke 目錄下,使用 terragrunt run-all [參數] 來跑整個相依的 module,我們這邊就建立一個名為 tf-test 的 cluster,並且有一個名為 test 的 node_pool ,其他設定請參考上面程式:

圖片

(黃色的 WARN 是因為 gcs 上還沒有存過該狀態檔案,所以會跳出提示)


等到 cluster 建立完成後,會將 cluster 的資訊帶入 node_pool,才開始建立 node_pool 的資源:

圖片


產生依賴關聯圖

當我們服務使用到很多依賴關係,想要釐清是誰依賴誰,如果單純看程式會比較麻煩,在 Terragrunt 還有一個好用的指令,可以使用以下指令,產生對應的依賴關係圖,在檢視時可以更清楚知道關係:

terragrunt graph-dependencies | dot -Tpng > graph.png

( 這個 dot 指令是另外的套件,會將關係圖程式碼轉成圖檔,請先安裝 brew install graphviz )


圖片


參考資料

事半功倍 — 使用 Terragrunt 搭配 Terraform 管理基礎設置


後續文章

經過上面的說明,了解如何將 terraform 改寫成 module 的模式,來避免每個 terraform 檔案都是客製化,再日後維護起來會十分不方便,也可以透過模組方式來提早驗證輸入的參數值,是否符合該變數的值,下一篇會分享如何導入 terragrunt,terragrunt 的好處是什麼。

使用 Terraform 建立 Google Compute Engine

使用 Terraform 建立 Google Kubernetes Engine

About

如何導入 Terragrunt,Terragrunt 好處是什麼?

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages