Terraform 自動化的基礎架構介紹

Chi-Hsuan Huang
12 min readJul 3, 2019

--

幾年前開始 DevOps 便開始非常的火熱,許多公司也引入了 DevOps 精神,而同時間也有越來越多工具產生,如 Chef, Ansible, Packer…等,Terraform 則是近幾年來另一個非常好用的工具。

Terraform 是什麼?為什麼需要它?

如今,我們在開發產品服務時,大量使用像是 AWS、GCP…等雲端平台,而隨著產品的規模,我們的機器規格可能會需要提升,或是架構需要調整,需要新增 Cache 的機制…等,又或是我們有許多需要像是資料庫、網路路由、安全設定…等諸多需要雲端資源,這時候 Terraform 便是很好的工具去管理這些雲端的基礎架構。

透過 Terraform,我們不再需要手動到 Web 介面開啟一台機器,不再需要手動設定 VPC,只要撰寫好 Terraform 腳本,一個鍵就可以達到你想要做的事情,簡單來說幫助你透過程式碼去管理這些雲端資源,同時擁有以下好處:

  • 可以版本化去管理你的雲端服務
  • 團隊成員都可以透過 Terraform 腳本瞭解目前所使用的雲端服務與其設定
  • 可以自動化測試雲端架構
  • 確保不同的環境 (e.g Dev, Staging, Prod) 都是相同的雲端配置

Terraform 與其他的工具有什麼不同呢?

其他工具如 Chef, Ansible 他們最主要的功能是「配置管理」,亦即是決定要安裝什麼套件、設定什麼環境變數…等之類的,而相較之下 Terraform 則是決定要什麼樣的機器。

跟 Terraform 最接近的工具則是 AWS 自行推出的 CloudFormation,然而雖然 CloudFormation 也是透過文件去維護雲端架構,但是 Terraform 除了 AWS 可以同時擴充支援各家雲端服務廠商,同時 Terraform 所制定的撰寫方式相對於 CloudFormation 是非常的清晰易懂,容易上手維護,就我個人的經驗上也是較推薦使用 Terraform。

安裝 Terraform

Terraform 的安裝,官方是推薦直接到官方網頁下載

根據你的作業系統下載後,得到的便是已經編譯好的執行檔,你可以將它放到預設定系統環境變數中,像是放到 /usr/bin/、/usr/local/bin 的目錄下,這樣便可以直接在終端機執行 Terraform 指令

❯ Terraform
Usage: terraform [-version] [-help] <command> [args]
The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you’re just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.

如何撰寫 Terraform 腳本?

Terraform 的檔案副檔名是 *.tf,而基本的一個 Terraform 配置是透過叫做 HCL 語言撰寫,長得像是這樣

provider "aws" {
profile = "default"
region = "us-east-1"
}

resource "aws_instance" "example" {
ami = "ami-2757f631"
instance_type = "t2.micro"
}

上述表示是需要使用 AWS 的雲端服務,然後需要開啟一台 EC2 Instance 的機器,其規格類型是 t2.micro

provider 是決定要對哪一個平台操作 profile, region 是 aws 需要的屬性。

而這裡這裡resource 的欄位意思是如下面的表示:


resource 雲端資源名稱 自定義的名稱 {
屬性 = 值
}

自定義的名稱可以用來被其他資源引用。

除了這個最基本的定義資源語法之外,Terraform 還提供了像是:

Input Variables 讓你可以像寫程式一樣使用變數,在重複用到的地方,或是一些需要動態給予的值。

variable "image_id" {
type = string
}

variable "availability_zone_names" {
type = list(string)
default = ["us-west-1a"]
}

你可以透過 var.* 引用變數

resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = var.image_id
}

Output Values 輸出一些資源創建後你需要的值,最常見的像是 IP

output "instance_ip_addr" {
value = aws_instance.example.private_ip
}

你透過 “<PROVIDER>_<類型>_<NAME>_<ATTRIBUTE>” 去取值。

Modules 當你有多個環境像是 Dev, Staging, Prod,或是多個專案,透過 Modules 可以讓你不用在這些地方都寫一份類似的 Terraform,可以集中起還在寫一些 Modules 然後在用到的地方引入。

module "servers" {
source = "./app-cluster"

servers = 5
}

lifecycle 指定資源的生命週期,像是先創建新的,再移除舊的資源

resource "azurerm_resource_group" "example" {
# ...

lifecycle {
create_before_destroy = true
}
}

如何使用 Terraform?

在使用 Terraform 前你需要先擁有你雲端服務的帳號,像是 AWS 帳號、GCP 帳號,而以 AWS 為例你需要先配置好 AWS 的 credentials

再來在你的專案目錄下,創建一個 main.tf 的檔案,並撰寫你需要的配置,`然後下 terraform init 指令

❯ terraform initInitializing the backend...Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (terraform-providers/aws) 2.17.0...
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.aws: version = "~> 2.17"Terraform has been successfully initialized!

在這個指令中,Terraform 會去下載需要的套件,像是 AWS plugin。

接著透過 terraform plan 指令,可以讓你在實際執行之前觀察 Terraform 將做哪些哪些改變,這是為了防止我們修改到我們不應該修改的東西,或是有不是我們預期的結果

❯ terraform plan
Refreshing Terraform state in-memory prior to plan…
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:# aws_instance.example will be created
+ resource “aws_instance” “example” {
+ ami = “ami-2757f631”
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ id = (known after apply)
...

你可以看到上面 Terraform 告訴我們有哪些東西會被建立,而當如果我們已經有服務在上面時,他也會告訴我們有哪些會被刪除。這裡裡面的 known after apply 是表示在資源建立後才會知道的值。

接下來如果 plan 的改變都沒問題,就可以執行 terraform apply 指令,實際去創建或改變雲端架構

❯ terraform applyAn execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
....
Plan: 1 to add, 0 to change, 0 to destroy.Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:

這裡 Terraform 一樣會輸出 plan 的內容,告訴你會有哪些改變,並讓你輸入去確認是否真的要執行。

輸入 yes,就會實際開啟一個 EC2 機器了。

最後,如果要刪除呢?你只要透過 terraform destroy 就可以清除所有的資源了

❯ terraform destroy
aws_instance.example: Refreshing state... [id=i-05f8863ba523a0082]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:# aws_instance.example will be destroyed
- resource "aws_instance" "example" {
- ami = "ami-2757f631" -> null
- arn = "arn:aws:ec2:us-east-1:848660357346:instance/i-05f8863ba523a0082" -> null
- associate_public_ip_address = true -> null
- availability_zone = "us-east-1d" -> null
- cpu_core_count = 1 -> null
- cpu_threads_per_core = 1 -> null
...

Terraform 一樣會告訴你將要刪除的東西,並要你在輸入 “yes” 做為確認。

其他常用的 Terraform CLI 指令

除了 init, plan, apply, destroy 之外

  • terraform fmt 因為多人撰寫 Terraform 的 coding style 很大的可能會不同,該指令會統一轉成一定格式的內容。
  • terraform graph 讓你可以用圖形看到所有資源的相異性。
  • terraform import 如果你已經在雲端上手動建立資源了,可以透過該指令導入 Terraform 中
  • terraform validate 是否存在語法錯誤

Terraform Remote State

由於 Terraform 預設將所有創建的雲端資源狀態存成 terraform.tfstate 的檔案,當有更動的時候會去這裡讀取狀態,然而當多人合作時,不同人電腦上的狀態檔是會不一致的,因此 Terraform 提供了 Remote State 的機制,讓 state 檔案統一放在一個地方。

Terraform 提供了以下可以存放的方式

並提供了 terraform workspace 指令可以切換不同的 state,因為像是 Dev 環境的 state 會跟 Prod 不一樣,應該要分開。

當然即使同一存在一個地方,也可能發生 read write 衝突的問題,所以 Terraform 預設會 lock 避免發生衝突。

結論

到這裡我們簡單地介紹了 Terraform 的用途、如何撰寫使用 Terraform,實際使用還有需要地方需要注意,但希望透過這樣的介紹能夠讓你簡單快速的認識 Terraform,有更多興趣推薦可以閱讀 Terraform: Up & Running 這本書,講解許多實際情況 Terraform 的使用方式。

--

--

Chi-Hsuan Huang

Back to The Mind: Follow Your Heart. Co-Founder at Taiwanstat; Full Stack Developer. https://github.com/chihsuan