用Terraform管理雲端Infra
Terrform
Terraform是一套管理雲端資源的工具
傳統上在設定雲端資源都需要透過UI人工處理
進階一點可透過一些shell script加上各雲端平台的CLI工具來建立資源
甚至像AWS還有推出Cloud Formation這種工具來快速佈署資源做到自動化
但每個平台的設定方式都不相同
Terrform則是可以跨各種平台使用
它其實就類似AWS的Cloud Formation但不限於用在AWS上
用在AWS你可以開EC2、S3 Bucket、RDS
也可以同樣用在GCP上開Compute Engine、Cloud Storage Bucket、Cloud SQL
而且這些資源可同時間一起佈建
要刪除也是一鍵就可以同時刪除所有資源
建立AWS EC2資源
這邊參考Terraform的AWS Turtorial
設定AWS憑證
首先要設定一組AWS憑證(Key ID/Key Secret)
然後放到~/.aws/credentials中設定好profile
例如這邊profile設定為terraform-dev
[default]
aws_access_key_id = key_id_1
aws_secret_access_key = key_secret_1
[terraform-dev]
aws_access_key_id = key_id_2
aws_secret_access_key = key_secret_2
建立terrform設定檔
依照官方Turtoial的範例我做了一些小調整
首先可以在provider區塊看到我將AWS Region設定在ap-southeast-1(新加坡)、憑證profile選擇前面設定的terraform-dev
接著EC2做了一些設定(resource "aws_instance"區塊)
- ami(ref): Instance Image, 這邊設定為Ubuntu 18.04
- instance_type(ref): t3.nano
- security_groups(ref): 設定為預先建立好的web, 這邊要注意security group要設定name不是ID
- key_name(ref): EC2 Key Pair Name
- user_data(ref): EC2的user data, 可以在EC2啟動的時候執行一些shell script
- root_block_device(ref): EC2的Storage設定, 這邊設定為gp2/10GB
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "ap-southeast-1"
# aws credentials profile
profile = "terraform-dev"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["amazon/ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
# 安裝ubuntu
ami = data.aws_ami.ubuntu.id
instance_type = "t3.nano"
# 設定security group name(不是ID)
security_groups = ["web"]
# key pair
key_name = "devops"
# startup script
user_data = <<EOF
#!/bin/bash
touch /home/ubuntu/foobar.txt
EOF
# disk設定
root_block_device {
volume_size = 10
volume_type = "gp2"
}
tags = {
Name = "terraform test ec2"
developer = "ciao chung"
}
}
Init
使用terraform init指令
此步驟terraform會將provider的一些相關要用的程式先下載下來放到.terraform目錄中
另外terraform還會建立一個叫.terraform.lock.hcl的lock檔案鎖定provider的版本
以便後續有新的版本的時候比對是否要下載
Format and validate the configuration
這邊主要是說可以透過terraform fmt指令來將terraform設定檔排版好
以及可以用terraform validate指令來驗證設定檔是否正確
除了可驗證格式外
還可以根據每個設定屬性去驗證填入的值是否正確
例如如果將EC2的storage disk type改為"gp1"
驗證的時候就會跳出錯誤
並且跟你說可用值有哪些
開始建立資源
使用terraform apply指令即可開始建立資源
執行之後會顯示這次要操作的資源設定細節
並跳出confirm再次確認是否真的要執行
如果在自動化的情況想跳過這個confirm
可使用terraform apply --auto-approve直接執行
更動資源
當資源建立好之後想更動資源設定
可以重新apply一次
apply的時候terraform會顯示異動的設定
以下方截圖來說
我將instance type由t3.nano改為t2.nano
這時候就會顯示差異化的部份
確認後就可以進行自動更新
刪除資源
刪除資源也非常簡單
使用terraform destroy指令確認後即可將所有資源全部刪除
使用File Function載入檔案內容
以前面的EC2設定來說
user_data如果要執行的shell script非常多會造成terraform設定檔很亂
所以可以額外抽出一個獨立的shell檔案(例如: start-script.sh)
接著使用File Function將這個start-script.sh載入即可
start-script.sh
#!/bin/bash
mkdir -p /home/ubuntu/foo/bar
echo "text..." > /home/ubuntu/foo/bar/data.txt
terraform的user_data這樣設定即可
user_data = file("start-script.sh")
Terraform變數
Terraform內還可以定義變數
variable "instance_name" {
description = "Value of the Name tag for the EC2 instance"
type = string
default = "ExampleAppServerInstance"
}
然後使用var套用在其他地方
resource "aws_instance" "app_server" {
ami = "ami-08d70e59c07c61a3a"
instance_type = "t2.micro"
tags = {
Name = var.instance_name
}
}
在terrform apply的時候
還可以帶入變數將其取代
terraform apply -var "instance_name=new-instance-1"
Store Remote State
前面在執行terraform apply後
會發現在執行目錄下會出現terraform.tfstate檔案
仔細去查看該檔案會看到terraform在上面紀錄這些佈署資源的資訊
這樣才能知道更新或刪除的時候要操作哪些資源
通常在單人運作的情況下這些檔案存在本機沒什麼問題
但如果是多人一起佈署同樣的一個資源
就會出現terraform.tfstate檔案無法同步的問題
所以terraform提供將state存到雲端的方式大家可以一起共用協作
這部份可直接參考這份文件
透過大家登入共同的Terraform Workspace
就可以共用AWS的憑證以及State
建立EC2的時候一併建立固定IP並綁定
有時候可能需要一建立EC2的時候就一併建立固定IP並且綁定在一起
可參考此段落範例
terrform設定檔
resource "aws_instance" "app_server" {
ami = "ami-031b15fc0d7ee2414"
instance_type = "t3.nano"
security_groups = ["web"]
key_name = "ciao"
tags = {
Name = "EC2+EIP"
}
}
resource "aws_eip" "app_eip" {
vpc = true
tags = {
Name = "App Static IP"
}
}
resource "aws_eip_association" "eip_assoc" {
instance_id = aws_instance.app_server.id
allocation_id = aws_eip.app_eip.id
}
aws_eip
AWS Elastic IP資源
aws_eip_association
將EC2與Elastic IP綁定的操作
透過設定instance_id及allocation_id(elastic ip的id)來綁定
這邊可以看到instance_id的格式為aws_instance.app_server.id
此格式分解如下
- aws_instance用來指定設定檔內的resource type
- app_server則是用來指定該resource name
- id: 該資源的ID, 這個是terraform在管理的所以它會自動去找出來關聯
而allocation_id的值aws_eip.app_eip.id其實也是一樣的意思
格式就是<resource_type>.<resource_name>.id
使用在GCP上
建立可操作GCP資源的Service Account
這部份照官方文件建立即可就不贅述
只要依照需求把該給SA的權限給夠即可
terraform設定檔
這邊的範例為建立一個Cloud Storage Bucket
主要就是credentials要設定至service account json key的位置
然後設定project id
接下來就是依照要設定的資源去做找該資源的設定文件即可
這邊範例將project跟credentials抽為變數使用
另外這邊可以看到使用一個random_id的資源
可以用來隨機產生亂數用來建立bucket name
因為bucket name是唯一值用此方式才不會容易撞名
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "sa_key" {
description = "Service account json key path"
type = string
}
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "4.43.0"
}
}
}
provider "google" {
credentials = var.sa_key
project = var.project_id
}
resource "google_storage_bucket" "my-bucket" {
name = "${random_id.bucket_prefix.hex}-my-bucket"
location = "asia"
force_destroy = true
}
resource "random_id" "bucket_prefix" {
byte_length = 8
}
使用.tfvars檔案帶入變數
雖然可以使用terraform apply --var project_id=foobar --var sa_key=/path/to/json-key這種方式來帶入變數
但如果變數越來越多
指令將會越來越複雜
因此可使用.tfvars這種方式將所有指令的變數集中至一個.tfvars變數檔案檔案中管理
例如建立一個var.tfvars檔案內容如下
# GCP project id
project_id="foobar"
# service account json key
"sa_key"="/path/to/json-key"
如此一來執行指令只要向這樣使用-var-file指定var.tfvars即可
terraform apply -var-file var.tfvars
使用tfvars.json檔案帶入變數
tfvars.json則是另一種變數檔案的格式
例如建立一個var.tfvars.json檔案內容如下
{
"project_id": "foobar",
"sa_key": "/path/to/json-key"
}
一樣使用-var-file指定tfvars.json即可
terraform apply -var-file var.tfvars.json
其他
.gitignore
有些terraform操作的相關資訊不需要進版控
因此.gitignore需要排除這些檔案
.terraform/
*.tfstate
*.tfstate.backup
*.tfstate.lock.info
*.tfvars