Terraform管理雲資源實踐

leason001發表於2024-08-26

背景

Terraform是一款開源的Cli工具,網上的很多文章都是單機安裝一個然後建立個目錄就去操作雲資源;如果在高可用的前提,如何將Terraform cli變成一個嵌入運維流程的一個元件?不僅僅是人編寫tf模板然後去apply?

自動化的驅動Terraform,無非包含這幾個步驟:

  • 初始化Terraform
  • 填充資源模板
  • apply資源
  • show資源

初始化Terraform

建立一個雲資源目錄,如cloudxxx-test001
雲資源的目錄下需要有Terrafor的Provider資訊,以及例項宣告資訊。
建立好了模板檔案,就需要初始化Terraform,以及下載Provider外掛,建議提前下載好外掛到指定的目錄,使用容器可以直接打到映象裡
這樣初始化直接指定plugin地址:

/usr/local/bin/terraform init -plugin-dir=/Users/lixiangli/.terraform.d/plugins

注意:確保外掛地址內有你宣告的外掛版本

由於Terraform apply是不支援選擇apply哪個資源,因此上面的實現方式可以發現,一個目錄是放一個雲資源。為了讓每次操作的影響範圍是可控的。這種方式會帶來一個問題,就是state的檔案儲存也必須是隔離的,否則出現的情況是apply 資源cloudxxx-test001時 cloudxxx-test002會被直接刪除。

模板檔案生成

透過程式碼的方式去驅動Terraform, 無法避免的一步就是生成所對應的雲資源的模板檔案,我們這邊使用golang,所以找到需要對接的雲的Provider的文件然後定義成如下:

resource "ucloud_disk" "ucloud_disk_{{ .ObjectMeta.UID }}" {
  availability_zone = "{{ .Spec.Zone }}"
  name              = "{{ .Spec.InstanceName }}"
  disk_size         = "{{ .Spec.InstanceSize }}"
  disk_type         = "{{ .Spec.InstanceType }}"
  charge_type       = "{{ .Spec.ChargeType }}"
}

在程式執行時動態填充這些模板即可

選擇合適的狀態儲存

Terraform是個有狀態的元件,如果部署多個例項的話,官方預設的state檔案的模式必然是無法滿足需求的。
所以我們這邊選擇的是etcdv3
配置如下:

terraform {
  required_providers {
    ucloud = {
      source = "ucloud/ucloud"
      version = "~>1.23.0"
    }
  }
  backend "etcdv3" {
    endpoints = ["http://127.0.0.1:2379/"]
    lock      = true
    prefix    = "/terraform-state/clouddisk/77c2d636-7a59-11eb-9d32-12caef3c0b88"
    cacert_path = ""
    cert_path = ""
    key_path = ""
  }
}
provider "ucloud" {
    public_key  = "xxxxxxx"
    private_key = "xxxxxx"
    region      = "cn-bj2"
    project_id  = ""
}

backend的prefix資源加了uuid,實際上是為了解決上面一個目錄是放一個雲資源鎖帶來的問題,也就是說那個uuid實際上是對應的單獨資源id,每個資源都有單獨的state檔案

如何支援多雲

支援多雲是Terraform的強項,支援多雲依然需要在上次軟體做好一定的遮蔽工作。
Terraform需要做的就是準備好多套雲的tf模板去填充
如騰訊雲:

resource "tencentcloud_cbs_storage" "tencentcloud_disk_{{ .ObjectMeta.UID }}" {
  storage_type      = "{{ .Spec.InstanceType }}"
  storage_name      = "{{ .Spec.InstanceName }}"
  storage_size      = "{{ .Spec.InstanceSize }}"
  availability_zone = "{{ .Spec.Zone }}"
  project_id        = "{{ .Spec.ProjectID }}"
}

如優刻得:

resource "ucloud_disk" "ucloud_disk_{{ .ObjectMeta.UID }}" {
  availability_zone = "{{ .Spec.Zone }}"
  name              = "{{ .Spec.InstanceName }}"
  disk_size         = "{{ .Spec.InstanceSize }}"
  disk_type         = "{{ .Spec.InstanceType }}"
  charge_type       = "{{ .Spec.ChargeType }}"
}

上層的資料結構可以宣告成一樣的,所有的差異由tf模板來遮蔽

相關文章