背景
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模板來遮蔽