ในโลกของการพัฒนาและบริหารจัดการระบบคลาวด์ที่เปลี่ยนแปลงอย่างรวดเร็วในปัจจุบัน การสร้างและดูแลโครงสร้างพื้นฐานด้วยวิธีการแบบดั้งเดิมที่ต้องคลิกไปมาบนหน้าคอนโซล (Manual Configuration) ไม่เพียงแต่เสียเวลาและมีโอกาสเกิดความผิดพลาดสูง แต่ยังเป็นอุปสรรคสำคัญต่อการปรับขนาดและการสร้างระบบที่สอดคล้องกันอีกด้วยครับ ลองนึกภาพว่าคุณต้องสร้าง VPC, EC2, RDS และ S3 กว่าร้อยรายการด้วยมือ จะเกิดอะไรขึ้นหากคุณต้องทำซ้ำสำหรับสภาพแวดล้อม Development, Staging และ Production? หรือต้องสร้างใหม่ทั้งหมดในกรณีเกิดภัยพิบัติ? นี่คือจุดที่แนวคิด Infrastructure as Code (IaC) เข้ามาพลิกโฉมวงการ และ Terraform คือเครื่องมือที่ทรงพลังที่สุดตัวหนึ่งที่พร้อมจะเข้ามาเป็นขุมพลังให้คุณจัดการโครงสร้างพื้นฐานบน Cloud AWS ได้อย่างมืออาชีพ ตั้งแต่ระดับเริ่มต้นไปจนถึงระดับ Enterprise เลยครับ
- การปฏิวัติวงการด้วย Infrastructure as Code (IaC)
- Terraform คืออะไร? ทำไมต้อง Terraform?
- เริ่มต้นใช้งาน Terraform กับ AWS: ขั้นตอนพื้นฐาน
- สถาปัตยกรรม AWS พื้นฐานด้วย Terraform: ตัวอย่าง Code จริง
- การจัดการ State File ใน Terraform: หัวใจสำคัญ
- การจัดระเบียบ Code ด้วย Terraform Modules
- แนวปฏิบัติที่ดีที่สุด (Best Practices) ในการใช้ Terraform กับ AWS
- ตัวอย่าง Use Cases และ Advanced Topics
- คำถามที่พบบ่อย (FAQ) เกี่ยวกับ Terraform บน AWS
- สรุปและก้าวต่อไป
การปฏิวัติวงการด้วย Infrastructure as Code (IaC)
ก่อนที่เราจะดำดิ่งลงไปในโลกของ Terraform เรามาทำความเข้าใจแนวคิดพื้นฐานที่เป็นรากฐานของมันก่อนครับ นั่นคือ Infrastructure as Code (IaC) IaC คือการบริหารจัดการและจัดเตรียมโครงสร้างพื้นฐานทางคอมพิวเตอร์ (เช่น Network, Virtual Machines, Load Balancers, Databases) ด้วยการใช้ไฟล์คำสั่งหรือ Code แทนที่จะเป็นการกำหนดค่าด้วยตนเองผ่าน GUI หรือการรัน Script แบบเดิมๆ ครับ
ทำไม IaC ถึงสำคัญและเป็นการปฏิวัติวงการ?
- ความรวดเร็วและประสิทธิภาพ: การ Deploy โครงสร้างพื้นฐานขนาดใหญ่จะใช้เวลาเพียงไม่กี่นาที แทนที่จะเป็นชั่วโมงหรือวัน ด้วยการรันคำสั่งเพียงชุดเดียว โครงสร้างพื้นฐานทั้งหมดก็พร้อมใช้งานทันทีครับ
- ความสอดคล้องและลดข้อผิดพลาด: Code จะช่วยให้มั่นใจว่าโครงสร้างพื้นฐานในสภาพแวดล้อม Development, Staging และ Production มีการกำหนดค่าที่เหมือนกันทุกประการ ลดความเสี่ยงจาก “configuration drift” หรือข้อผิดพลาดที่เกิดจากการกำหนดค่าด้วยมือครับ
- การควบคุมเวอร์ชัน (Version Control): ไฟล์ IaC สามารถจัดเก็บในระบบควบคุมเวอร์ชัน (เช่น Git) ได้ ทำให้สามารถติดตามการเปลี่ยนแปลง, ย้อนกลับไปยังเวอร์ชันก่อนหน้าได้ง่าย, และทำงานร่วมกันในทีมได้อย่างมีประสิทธิภาพ เหมือนกับการพัฒนาซอฟต์แวร์เลยครับ
- การทำงานร่วมกัน: ทีมงานหลายคนสามารถทำงานบนโครงสร้างพื้นฐานเดียวกันได้ โดยมีการตรวจสอบและอนุมัติการเปลี่ยนแปลงผ่าน Code Review
- ความสามารถในการทำซ้ำ (Repeatability): คุณสามารถสร้างและทำลายโครงสร้างพื้นฐานได้บ่อยเท่าที่ต้องการ โดยมั่นใจว่าทุกครั้งที่สร้างขึ้นมาใหม่ จะได้ผลลัพธ์ที่เหมือนเดิมเสมอ เหมาะสำหรับการทดสอบและการกู้คืนระบบเมื่อเกิดภัยพิบัติ (Disaster Recovery) ครับ
- การประหยัดค่าใช้จ่าย: ด้วยความสามารถในการสร้างและทำลายทรัพยากรได้ตามต้องการ ทำให้สามารถบริหารจัดการทรัพยากรได้อย่างมีประสิทธิภาพ และลดค่าใช้จ่ายที่ไม่จำเป็นได้ครับ
กล่าวโดยสรุป IaC ช่วยให้การจัดการโครงสร้างพื้นฐานกลายเป็นกระบวนการที่เชื่อถือได้, ทำซ้ำได้, มีประสิทธิภาพ และสามารถปรับขนาดได้ในแบบที่ไม่เคยมีมาก่อนครับ
Terraform คืออะไร? ทำไมต้อง Terraform?
เมื่อพูดถึง IaC เครื่องมือที่ได้รับความนิยมและทรงพลังที่สุดตัวหนึ่งคือ Terraform ครับ พัฒนาโดย HashiCorp, Terraform เป็นเครื่องมือ IaC แบบ Open Source ที่ช่วยให้คุณสามารถกำหนดและจัดเตรียมโครงสร้างพื้นฐานได้ในรูปแบบของ Code
คุณสมบัติเด่นของ Terraform:
- Declarative Configuration: คุณเพียงแค่บอก Terraform ว่าคุณต้องการให้โครงสร้างพื้นฐานของคุณมีลักษณะอย่างไร (เช่น ฉันต้องการ VPC ที่มี CIDR นี้, มี Subnet 2 อัน, และ EC2 Instance 1 ตัวใน Subnet แรก) โดยไม่ต้องระบุขั้นตอนการสร้าง Terraform จะจัดการ “วิธีการ” สร้างหรืออัปเดตให้เองครับ
- Idempotent Operations: ไม่ว่าคุณจะรัน Code เดิมกี่ครั้ง ผลลัพธ์ที่ได้จะเหมือนเดิมเสมอ Terraform จะตรวจสอบสถานะปัจจุบันของโครงสร้างพื้นฐานและปรับเปลี่ยนเฉพาะส่วนที่จำเป็นเท่านั้นครับ
- Provider-agnostic: นี่คือจุดแข็งที่สำคัญของ Terraform! มันสามารถทำงานร่วมกับ Cloud Provider ได้หลากหลาย ไม่ว่าจะเป็น AWS, Azure, Google Cloud Platform (GCP), Alibaba Cloud รวมถึง On-premise Infrastructure และ SaaS Providers อื่นๆ อีกมากมาย ด้วยระบบ “Providers” ที่มีความยืดหยุ่นสูง ทำให้คุณสามารถใช้ Terraform ในการจัดการทรัพยากรจากหลายแพลตฟอร์มพร้อมกันได้เลยครับ
- State Management: Terraform จะเก็บสถานะของโครงสร้างพื้นฐานที่มันจัดการไว้ในไฟล์ที่เรียกว่า “State File” ซึ่งเป็นสิ่งสำคัญในการติดตามว่าทรัพยากรใดถูกสร้างขึ้น, กำหนดค่าอย่างไร, และช่วยให้ Terraform สามารถวางแผนการเปลี่ยนแปลงได้อย่างถูกต้องครับ
- Execution Plan: ก่อนที่จะทำการเปลี่ยนแปลงใดๆ กับโครงสร้างพื้นฐานจริง Terraform จะสร้าง “Execution Plan” ขึ้นมา เพื่อแสดงให้คุณเห็นว่ามันจะสร้าง, อัปเดต, หรือทำลายทรัพยากรใดบ้าง คุณสามารถตรวจสอบแผนนี้ก่อนที่จะยืนยันการเปลี่ยนแปลงได้ ช่วยลดความผิดพลาดและเพิ่มความมั่นใจในการ Deploy ครับ
Terraform vs. CloudFormation: การเปรียบเทียบเครื่องมือ IaC ยอดนิยมบน AWS
บน AWS นั้นมี CloudFormation ซึ่งเป็นบริการ IaC แบบ Native ของ AWS เองครับ หลายคนอาจสงสัยว่าควรเลือกใช้ตัวไหนดี ตารางนี้จะช่วยให้เห็นภาพรวมของการเปรียบเทียบได้ชัดเจนขึ้นครับ
| คุณสมบัติ | Terraform | AWS CloudFormation |
|---|---|---|
| Cloud Vendor Agnostic | รองรับหลาย Cloud Provider (AWS, Azure, GCP, ฯลฯ) | เฉพาะ AWS เท่านั้น |
| ภาษาที่ใช้ | HashiCorp Configuration Language (HCL) เป็นภาษาเฉพาะของ Terraform ที่อ่านง่ายและยืดหยุ่น | YAML หรือ JSON |
| การจัดการ State File | ต้องจัดการ State File เอง (มักใช้ S3 + DynamoDB สำหรับ Remote State) | AWS จัดการ State ให้โดยอัตโนมัติ |
| Modules / Reusability | มีระบบ Modules ที่ทรงพลัง สามารถสร้างและใช้ Modules ซ้ำได้ง่าย ทั้งภายในและภายนอก (Terraform Registry) | Nested Stacks สำหรับการนำกลับมาใช้ใหม่ |
| การเรียนรู้ | มี Learning Curve เล็กน้อยสำหรับ HCL และแนวคิดเรื่อง State Management | เรียนรู้ YAML/JSON และแนวคิด Stack/Nested Stack |
| ความเร็วในการพัฒนา Features ใหม่ | มักจะรองรับ AWS Services ใหม่ๆ ได้ค่อนข้างเร็ว (ผ่านการอัปเดต AWS Provider) | ขึ้นอยู่กับการพัฒนาของ AWS เอง |
| การจัดการการเปลี่ยนแปลง (Drift Detection) | สามารถตรวจจับ “Drift” ได้ผ่านคำสั่ง terraform plan |
มีฟีเจอร์ Drift Detection ในตัว |
| ราคา | เป็น Open Source, ไม่มีค่าใช้จ่ายสำหรับตัว Terraform (มีเวอร์ชัน Enterprise สำหรับฟีเจอร์เพิ่มเติม) | ไม่มีค่าใช้จ่ายสำหรับ CloudFormation เอง แต่มีค่าใช้จ่ายสำหรับทรัพยากรที่สร้างขึ้น |
โดยสรุป หากคุณทำงานในสภาพแวดล้อมแบบ Multi-Cloud หรือต้องการเครื่องมือที่ยืดหยุ่น Terraform จะเป็นตัวเลือกที่ดีเยี่ยมครับ แต่ถ้าคุณทำงานบน AWS เพียงอย่างเดียว และต้องการความง่ายในการเริ่มต้น CloudFormation ก็เป็นทางเลือกที่น่าสนใจเช่นกันครับ อย่างไรก็ตาม ด้วยความยืดหยุ่นของ Terraform และ Ecosystem ที่ใหญ่ ทำให้มันเป็นตัวเลือกอันดับต้นๆ สำหรับองค์กรส่วนใหญ่ที่ต้องการ IaC ในปัจจุบันครับ
เริ่มต้นใช้งาน Terraform กับ AWS: ขั้นตอนพื้นฐาน
มาถึงส่วนที่น่าตื่นเต้นครับ เราจะมาดูวิธีการเริ่มต้นใช้งาน Terraform เพื่อจัดการทรัพยากรบน AWS กันแบบทีละขั้นตอนเลยครับ
การติดตั้ง Terraform
Terraform เป็น Single Binary ที่ติดตั้งง่ายมากๆ ครับ
- สำหรับ macOS (ใช้ Homebrew):
brew tap hashicorp/tap brew install hashicorp/tap/terraform - สำหรับ Linux (ใช้ apt – Debian/Ubuntu):
sudo apt update && sudo apt install -y gnupg software-properties-common wget -O- https://apt.releases.hashicorp.com/gpg | \ gpg --dearmor | \ sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg gpg --no-default-keyring \ --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \ --fingerprint echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \ sudo tee /etc/apt/sources.list.d/hashicorp.list sudo apt update sudo apt install terraform - สำหรับ Windows: ดาวน์โหลดไฟล์ .zip จาก เว็บไซต์ Terraform และแตกไฟล์ จากนั้นเพิ่ม Path ของไฟล์ที่แตกไปใน Environment Variables ของระบบครับ
หลังจากติดตั้งแล้ว ลองตรวจสอบเวอร์ชันเพื่อยืนยันว่าติดตั้งสำเร็จครับ
terraform --version
การตั้งค่า AWS Credentials
Terraform จำเป็นต้องมีสิทธิ์ในการเข้าถึง AWS Account ของคุณเพื่อสร้าง, อัปเดต, หรือลบทรัพยากร วิธีที่แนะนำและปลอดภัยที่สุดคือการสร้าง IAM User ที่มีสิทธิ์เฉพาะเจาะจง และใช้ Access Key / Secret Key ของ User นั้นครับ
ขั้นตอน:
- สร้าง IAM User ใหม่ใน AWS Console
- มอบ Policy ที่จำเป็นให้กับ User นั้น (เช่น
AdministratorAccessสำหรับการทดลอง หรือ Custom Policy ที่จำกัดสิทธิ์มากขึ้นในการใช้งานจริง) - สร้าง Access Key และ Secret Key สำหรับ User นั้น
- ตั้งค่า Credentials ในเครื่องของคุณ มีหลายวิธี แต่วิธีที่นิยมคือใช้ AWS CLI:
aws configure AWS Access Key ID [None]: YOUR_ACCESS_KEY_ID AWS Secret Access Key [None]: YOUR_SECRET_ACCESS_KEY Default region name [None]: ap-southeast-1 (หรือ Region ที่คุณต้องการ) Default output format [None]: jsonหรือกำหนดใน Environment Variables:
export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY_ID" export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY" export AWS_DEFAULT_REGION="ap-southeast-1"หรือกำหนดโดยตรงในไฟล์ Terraform (ไม่แนะนำสำหรับการใช้งานจริงเพราะไม่ปลอดภัย):
provider "aws" { region = "ap-southeast-1" access_key = "YOUR_ACCESS_KEY_ID" secret_key = "YOUR_SECRET_ACCESS_KEY" }
เราจะใช้ไฟล์ ~/.aws/credentials และ ~/.aws/config ที่ AWS CLI สร้างให้เป็นหลักครับ
โครงสร้างไฟล์ Terraform เบื้องต้น
โดยทั่วไป โครงสร้างไฟล์ Terraform สำหรับโปรเจกต์หนึ่งๆ จะประกอบด้วยไฟล์หลักๆ ดังนี้ครับ
main.tf: ไฟล์หลักสำหรับกำหนดทรัพยากร (Resource) ต่างๆ ที่ต้องการสร้างvariables.tf: ไฟล์สำหรับประกาศตัวแปร (Variables) ที่จะใช้ใน Codeoutputs.tf: ไฟล์สำหรับประกาศค่าผลลัพธ์ (Outputs) ที่ต้องการแสดงหลังจาก Deploy เสร็จสิ้นversions.tf: ไฟล์สำหรับกำหนดเวอร์ชันของ Terraform และ Providers ที่ใช้งานproviders.tf(บางครั้ง): แยกออกมาจากmain.tfเพื่อกำหนด Provider
มาดูตัวอย่าง versions.tf กันก่อนครับ
# versions.tf
terraform {
required_version = "~> 1.0" # กำหนดเวอร์ชันของ Terraform CLI ที่ต้องการ
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # กำหนดเวอร์ชันของ AWS Provider ที่ต้องการ
}
}
}
provider "aws" {
region = "ap-southeast-1" # กำหนด AWS Region ที่จะทำงานด้วย
# profile = "default" # หากคุณใช้ AWS CLI Profile ที่ไม่ใช่ 'default'
}
ไฟล์นี้จะบอก Terraform ว่าต้องใช้ Terraform CLI เวอร์ชันใด และ AWS Provider เวอร์ชันใด พร้อมทั้งกำหนด Default Region ที่จะทำงานด้วยครับ
สถาปัตยกรรม AWS พื้นฐานด้วย Terraform: ตัวอย่าง Code จริง
คราวนี้เราจะมาลองเขียน Code Terraform เพื่อสร้างโครงสร้างพื้นฐาน AWS ที่ใช้งานได้จริงกันครับ เราจะสร้าง VPC, Subnet, EC2 Instance, S3 Bucket และ RDS Database
การสร้าง VPC และ Subnet
VPC (Virtual Private Cloud) คือเครือข่ายเสมือนส่วนตัวของคุณบน AWS ซึ่งเป็นรากฐานของโครงสร้างพื้นฐานทั้งหมดครับ
สร้างไฟล์ main.tf และเพิ่ม Code ด้านล่างนี้
# main.tf
# 1. สร้าง VPC (Virtual Private Cloud)
resource "aws_vpc" "main_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "my-terraform-vpc"
Environment = "Dev"
}
}
# 2. สร้าง Internet Gateway (สำหรับให้ VPC สื่อสารกับอินเทอร์เน็ต)
resource "aws_internet_gateway" "main_igw" {
vpc_id = aws_vpc.main_vpc.id
tags = {
Name = "my-terraform-igw"
}
}
# 3. สร้าง Public Subnet
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.main_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-southeast-1a" # เลือก AZ ที่ต้องการ
map_public_ip_on_launch = true # ทำให้ EC2 ใน Subnet นี้ได้รับ Public IP
tags = {
Name = "my-terraform-public-subnet"
}
}
# 4. สร้าง Private Subnet
resource "aws_subnet" "private_subnet" {
vpc_id = aws_vpc.main_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-southeast-1a" # เลือก AZ เดียวกันกับ Public Subnet เพื่อความง่าย
tags = {
Name = "my-terraform-private-subnet"
}
}
# 5. สร้าง Route Table สำหรับ Public Subnet
resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.main_vpc.id
route {
cidr_block = "0.0.0.0/0" # กำหนดเส้นทางออกไปอินเทอร์เน็ตทั้งหมด
gateway_id = aws_internet_gateway.main_igw.id
}
tags = {
Name = "my-terraform-public-route-table"
}
}
# 6. เชื่อม Public Subnet เข้ากับ Public Route Table
resource "aws_route_table_association" "public_subnet_association" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.public_route_table.id
}
คำอธิบาย Code:
resource "aws_vpc" "main_vpc" { ... }: ประกาศการสร้างทรัพยากร VPC โดยใช้ Typeaws_vpcและตั้งชื่อ Logic Name ว่าmain_vpccidr_block: กำหนดช่วง IP Address ของ VPCenable_dns_hostnames,enable_dns_support: เปิดใช้งาน DNS ใน VPCtags: กำหนด Tag เพื่อช่วยในการระบุและจัดระเบียบทรัพยากรaws_internet_gateway: สร้าง IGW เพื่อให้ VPC เข้าถึงอินเทอร์เน็ตได้aws_subnet: สร้าง Subnet โดยอ้างอิงvpc_idจาก VPC ที่สร้างไว้map_public_ip_on_launch = true: สำหรับ Public Subnet เพื่อให้ EC2 ได้รับ Public IP โดยอัตโนมัติaws_route_table: สร้าง Route Table และกำหนด Route สำหรับการออกอินเทอร์เน็ต (0.0.0.0/0ไปยัง IGW)aws_route_table_association: เชื่อม Subnet เข้ากับ Route Table ที่เหมาะสม
การ Deploy EC2 Instance
เราจะสร้าง EC2 Instance ใน Public Subnet เพื่อให้สามารถเข้าถึงได้จากอินเทอร์เน็ตได้ครับ
เพิ่ม Code นี้ลงใน main.tf
# main.tf (ต่อจากด้านบน)
# 7. สร้าง Security Group สำหรับ EC2 (อนุญาต SSH และ HTTP)
resource "aws_security_group" "web_sg" {
name = "web_security_group"
description = "Allow SSH and HTTP traffic"
vpc_id = aws_vpc.main_vpc.id
ingress {
description = "Allow SSH from anywhere"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Egress (Allow all outbound traffic) - Default
egress {
from_port = 0
to_port = 0
protocol = "-1" # -1 means all protocols
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-server-sg"
}
}
# 8. สร้าง Key Pair สำหรับ SSH เข้า EC2 (คุณต้องมีไฟล์ .pub อยู่แล้ว)
# สมมติว่าคุณมีไฟล์ "my-key.pub" อยู่ในไดเรกทอรีเดียวกับไฟล์ Terraform ของคุณ
resource "aws_key_pair" "my_key" {
key_name = "my-terraform-key"
public_key = file("my-key.pub")
}
# 9. สร้าง EC2 Instance
resource "aws_instance" "web_server" {
ami = "ami-0eb26176e1039a755" # ID ของ Amazon Linux 2 AMI ใน ap-southeast-1 (อาจมีการเปลี่ยนแปลง)
instance_type = "t2.micro"
subnet_id = aws_subnet.public_subnet.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
key_name = aws_key_pair.my_key.key_name
associate_public_ip_address = true # EC2 ใน Public Subnet ต้องได้รับ Public IP
user_data = <<-EOF
#!/bin/bash
sudo yum update -y
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
echo "Hello from Terraform EC2!" > /var/www/html/index.html
EOF
tags = {
Name = "my-terraform-web-server"
Purpose = "Web Server"
}
}
หมายเหตุ:
- สำหรับ
amiคุณต้องค้นหา AMI ID ที่ถูกต้องสำหรับ Region ของคุณและระบบปฏิบัติการที่คุณต้องการ (เช่น Amazon Linux 2, Ubuntu) ผ่าน AWS Console หรือ AWS CLI ครับ - สำหรับ
aws_key_pairคุณต้องสร้าง SSH Key Pair ในเครื่องของคุณก่อน (ssh-keygen) และนำ Public Key (ไฟล์ .pub) มาเก็บไว้ในไดเรกทอรีเดียวกับไฟล์ Terraform ของคุณ หรือระบุ Path ที่ถูกต้องครับ
การจัดการ S3 Bucket
S3 (Simple Storage Service) เป็นบริการจัดเก็บข้อมูล Object Storage ที่ยืดหยุ่นและปรับขนาดได้ไม่จำกัดครับ
เพิ่ม Code นี้ลงใน main.tf
# main.tf (ต่อจากด้านบน)
# 10. สร้าง S3 Bucket
resource "aws_s3_bucket" "my_data_bucket" {
bucket = "siamlancard-my-unique-data-bucket-12345" # ต้องเป็นชื่อ Bucket ที่ไม่ซ้ำกันทั่วโลก
tags = {
Name = "my-terraform-data-bucket"
}
}
# 11. กำหนดการควบคุมการเข้าถึง Bucket (เพื่อให้เป็น Private โดยค่าเริ่มต้น)
resource "aws_s3_bucket_ownership_controls" "my_data_bucket_ownership_controls" {
bucket = aws_s3_bucket.my_data_bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
# 12. บล็อก Public Access ทั้งหมดสำหรับ Bucket นี้ (แนะนำสำหรับ Bucket ส่วนใหญ่)
resource "aws_s3_bucket_public_access_block" "my_data_bucket_public_access_block" {
bucket = aws_s3_bucket.my_data_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# 13. เปิดใช้งานการเข้ารหัส Default (SSE-S3) สำหรับ Bucket
resource "aws_s3_bucket_server_side_encryption_configuration" "my_data_bucket_encryption" {
bucket = aws_s3_bucket.my_data_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256" # AWS S3-managed keys
}
}
}
สำคัญ: ชื่อ S3 Bucket ต้องไม่ซ้ำกันทั่วโลก! คุณต้องเปลี่ยน "siamlancard-my-unique-data-bucket-12345" เป็นชื่อที่ไม่ซ้ำกันของคุณเองครับ
การสร้าง RDS Database (PostgreSQL)
RDS (Relational Database Service) เป็นบริการ Managed Database ที่ช่วยให้คุณสามารถ Deploy และจัดการฐานข้อมูล Relational ได้อย่างง่ายดายครับ
เพิ่ม Code นี้ลงใน main.tf
# main.tf (ต่อจากด้านบน)
# 14. สร้าง Security Group สำหรับ RDS (อนุญาตเข้าถึงจาก Private Subnet หรือจาก EC2 Instance)
resource "aws_security_group" "rds_sg" {
name = "rds_security_group"
description = "Allow traffic to RDS instance"
vpc_id = aws_vpc.main_vpc.id
ingress {
description = "Allow Postgres from Private Subnet"
from_port = 5432 # Default port for PostgreSQL
to_port = 5432
protocol = "tcp"
cidr_blocks = [aws_subnet.private_subnet.cidr_block] # อนุญาตจาก Private Subnet
}
tags = {
Name = "rds-db-sg"
}
}
# 15. สร้าง RDS Instance (PostgreSQL)
resource "aws_db_instance" "my_database" {
allocated_storage = 20
engine = "postgres"
engine_version = "14.7" # ตรวจสอบเวอร์ชันที่รองรับ
instance_class = "db.t3.micro"
db_name = "mydatabase"
username = "admin"
password = "MyStrongPassword123!" # ควรใช้ Secrets Manager ใน Production
parameter_group_name = "default.postgres14"
skip_final_snapshot = true # สำหรับการทดสอบ ไม่ต้อง Backup ตอนลบ
vpc_security_group_ids = [aws_security_group.rds_sg.id]
db_subnet_group_name = aws_db_subnet_group.default.name # อ้างอิง DB Subnet Group ที่เราจะสร้าง
tags = {
Name = "my-terraform-rds-db"
}
}
# 16. สร้าง DB Subnet Group สำหรับ RDS (จำเป็นต้องมีอย่างน้อย 2 Subnets ใน AZ ที่แตกต่างกัน)
resource "aws_db_subnet_group" "default" {
name = "my-db-subnet-group"
subnet_ids = [aws_subnet.private_subnet.id] # ใน Production ควรมีอย่างน้อย 2 Subnet ใน 2 AZs
tags = {
Name = "My DB Subnet Group"
}
}
สำคัญ:
- สำหรับ
passwordใน Production ควรใช้ AWS Secrets Manager หรือ HashiCorp Vault เพื่อเก็บรักษารหัสผ่านอย่างปลอดภัย ไม่ควร hardcode ไว้ในไฟล์ Terraform ครับ - สำหรับ
aws_db_subnet_groupใน Production ควรมี Subnet อย่างน้อย 2 ตัวที่อยู่ใน Availability Zones ที่แตกต่างกัน เพื่อให้ RDS มี Multi-AZ Failover ได้ครับ ในตัวอย่างนี้เราใช้แค่ Private Subnet เดียวเพื่อความง่ายในการทดสอบครับ
การใช้ Variables และ Outputs
เพื่อให้ Code ของเรายืดหยุ่นและนำกลับมาใช้ใหม่ได้ง่ายขึ้น เราควรใช้ Variables และ Outputs ครับ
Variables: สร้างไฟล์ variables.tf
# variables.tf
variable "aws_region" {
description = "The AWS region to deploy resources into."
type = string
default = "ap-southeast-1"
}
variable "vpc_cidr_block" {
description = "CIDR block for the VPC."
type = string
default = "10.0.0.0/16"
}
variable "public_subnet_cidr" {
description = "CIDR block for the public subnet."
type = string
default = "10.0.1.0/24"
}
variable "private_subnet_cidr" {
description = "CIDR block for the private subnet."
type = string
default = "10.0.2.0/24"
}
variable "instance_type" {
description = "Type of EC2 instance to deploy."
type = string
default = "t2.micro"
}
variable "ami_id" {
description = "AMI ID for the EC2 instance."
type = string
default = "ami-0eb26176e1039a755" # Amazon Linux 2 (ap-southeast-1)
}
variable "db_password" {
description = "Password for the RDS database."
type = string
sensitive = true # ควรทำแบบนี้เพื่อไม่ให้แสดงค่าใน Output
}
จากนั้น ใน main.tf ให้เปลี่ยนค่าที่ Hardcode ให้เป็น Variables ครับ
# main.tf (ตัวอย่างการนำ variable มาใช้)
# ใน versions.tf
provider "aws" {
region = var.aws_region
}
# ใน aws_vpc
resource "aws_vpc" "main_vpc" {
cidr_block = var.vpc_cidr_block
# ...
}
# ใน aws_instance
resource "aws_instance" "web_server" {
ami = var.ami_id
instance_type = var.instance_type
# ...
}
# ใน aws_db_instance
resource "aws_db_instance" "my_database" {
password = var.db_password # เมื่อรัน `terraform apply` จะมีการ prompt ให้ใส่ค่า
# ...
}
Outputs: สร้างไฟล์ outputs.tf เพื่อแสดงข้อมูลสำคัญหลังการ Deploy
# outputs.tf
output "vpc_id" {
description = "The ID of the created VPC."
value = aws_vpc.main_vpc.id
}
output "public_subnet_id" {
description = "The ID of the public subnet."
value = aws_subnet.public_subnet.id
}
output "web_server_public_ip" {
description = "The public IP address of the web server."
value = aws_instance.web_server.public_ip
}
output "s3_bucket_name" {
description = "The name of the S3 bucket."
value = aws_s3_bucket.my_data_bucket.bucket
}
output "rds_endpoint" {
description = "The endpoint of the RDS database."
value = aws_db_instance.my_database.address
}
วิธีการรัน Code Terraform:
- เปิด Terminal ในไดเรกทอรีที่มีไฟล์ Terraform ทั้งหมด
terraform init: เพื่อดาวน์โหลด Providers และเริ่มต้น Backend (ทำครั้งแรกและเมื่อมีการเปลี่ยนแปลงเวอร์ชันหรือ Backend)terraform plan: เพื่อดูว่า Terraform จะทำอะไรบ้าง (จะสร้าง, อัปเดต, หรือลบทรัพยากรใด)terraform apply: เพื่อยืนยันและดำเนินการตามแผนที่แสดงในplanหากมี Variables ที่ไม่ได้กำหนด Default ไว้ ก็จะมีการ prompt ให้ใส่ค่าครับterraform destroy: เพื่อลบทรัพยากรทั้งหมดที่ Terraform สร้างขึ้น (ใช้ด้วยความระมัดระวัง!)
ขอแสดงความยินดีครับ! คุณได้สร้างโครงสร้างพื้นฐาน AWS ที่ซับซ้อนด้วย Terraform เป็นครั้งแรกแล้วครับ
การจัดการ State File ใน Terraform: หัวใจสำคัญ
หัวใจสำคัญอย่างหนึ่งที่ทำให้ Terraform ทำงานได้อย่างมีประสิทธิภาพคือ “State File” ครับ การทำความเข้าใจและจัดการ State File อย่างถูกต้องเป็นสิ่งสำคัญอย่างยิ่งในการใช้งาน Terraform
State File คืออะไร?
State File (terraform.tfstate) คือไฟล์ JSON ที่ Terraform ใช้เพื่อเก็บสถานะของโครงสร้างพื้นฐานที่มันกำลังจัดการครับ มันประกอบด้วยข้อมูลสำคัญดังนี้:
- Map ของทรัพยากร: ระบุว่าทรัพยากร Terraform ใน Code ของคุณ (เช่น
aws_instance.web_server) แมปกับทรัพยากรจริงบน Cloud (เช่น EC2 Instance IDi-0123456789abcdef0) อย่างไร - Attribute ของทรัพยากร: เก็บค่า Attribute ปัจจุบันของทรัพยากรที่สร้างขึ้น เช่น Public IP ของ EC2, ชื่อ VPC, Endpoint ของ RDS เป็นต้น
Terraform ใช้ State File ในการ:
- วางแผนการเปลี่ยนแปลง: เมื่อคุณรัน
terraform planTerraform จะเปรียบเทียบสถานะจริงบน Cloud กับ State File และ Code ของคุณ เพื่อหาความแตกต่างและวางแผนว่าจะต้องทำอะไรบ้าง - ติดตามทรัพยากร: มันรู้ว่าทรัพยากรใดถูกสร้างขึ้นโดย Terraform
- ปรับปรุงทรัพยากร: เมื่อคุณเปลี่ยนแปลง Code, Terraform จะใช้ State File เพื่อทราบว่าควรแก้ไขทรัพยากรที่มีอยู่แล้ว หรือสร้างใหม่
- อ้างอิงข้อมูล: Output Variables และ Data Sources ใช้ข้อมูลจาก State File
Local State vs. Remote State
- Local State: โดยค่าเริ่มต้น Terraform จะสร้างไฟล์
terraform.tfstateในไดเรกทอรีที่คุณรันคำสั่ง หากคุณทำงานคนเดียวและไม่ต้องการแชร์ State กับใคร นี่อาจเป็นทางเลือกที่ง่าย แต่ไม่แนะนำสำหรับการทำงานเป็นทีมหรือใน Production ครับ - Remote State: เมื่อทำงานเป็นทีม หรือต้องการความทนทานของ State File คุณควรใช้ Remote State ครับ Remote State จะเก็บไฟล์
.tfstateไว้ในที่เก็บข้อมูลภายนอก เช่น Amazon S3, Azure Storage, Google Cloud Storage, หรือ Terraform Cloud/Enterprise
ข้อดีของ Remote State:
- การทำงานร่วมกัน: ทีมงานหลายคนสามารถใช้ State File เดียวกันได้
- ความทนทาน: State File จะไม่สูญหายหากเครื่องของคุณพัง
- State Locking: ป้องกันไม่ให้ผู้ใช้สองคนพยายามแก้ไขโครงสร้างพื้นฐานพร้อมกัน ซึ่งอาจนำไปสู่ความขัดแย้งและข้อมูลเสียหายได้
- การเข้ารหัส: ผู้ให้บริการ Remote State มักจะรองรับการเข้ารหัสข้อมูล
การตั้งค่า S3 Backend สำหรับ Remote State
บน AWS การใช้ S3 Bucket ในการเก็บ State File เป็นวิธีที่นิยมและแนะนำอย่างยิ่งครับ
ขั้นตอน:
- สร้าง S3 Bucket: คุณต้องสร้าง S3 Bucket ที่จะใช้เก็บ State File ด้วยมือ หรือใช้ Terraform แยกต่างหาก (Bootstrap) ก็ได้ครับ (แนะนำให้ตั้งชื่อ Bucket เป็นชื่อเฉพาะ เช่น
siamlancard-terraform-state-bucket-12345) - เปิดใช้งาน Versioning บน S3 Bucket: เพื่อให้สามารถย้อนกลับเวอร์ชันของ State File ได้หากเกิดข้อผิดพลาด
- เปิดใช้งาน Server Side Encryption (SSE) บน S3 Bucket: เพื่อเข้ารหัส State File ที่จัดเก็บ
- เพิ่ม Backend Configuration ในไฟล์ Terraform: ในไฟล์
versions.tf(หรือไฟล์อื่นที่คุณกำหนด Provider) เพิ่มบล็อกbackend "s3" { ... }เข้าไปครับ
# versions.tf
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "siamlancard-terraform-state-bucket-12345" # เปลี่ยนเป็นชื่อ Bucket ของคุณ
key = "dev/aws-infra.tfstate" # Path ภายใน Bucket, เช่น environment/project.tfstate
region = "ap-southeast-1"
encrypt = true # เปิดใช้งานการเข้ารหัส SSE
dynamodb_table = "terraform-state-locking" # ใช้สำหรับ State Locking (จะกล่าวถึงในหัวข้อถัดไป)
}
}
provider "aws" {
region = "ap-southeast-1"
}
หลังจากเพิ่ม Backend Config แล้ว ให้รัน terraform init อีกครั้ง Terraform จะถามว่าคุณต้องการ Migrate Local State ไปยัง S3 หรือไม่ ให้ตอบ yes ครับ
State Locking ด้วย DynamoDB
เมื่อใช้ Remote State ที่ S3 การมีผู้ใช้หลายคนพยายามรัน terraform apply พร้อมกันอาจทำให้ State File เสียหายได้ครับ เพื่อป้องกันปัญหานี้ Terraform จึงมีกลไก State Locking ซึ่งมักจะใช้ AWS DynamoDB
ขั้นตอน:
- สร้าง DynamoDB Table: สร้าง DynamoDB Table ด้วยมือ หรือด้วย Terraform แยกต่างหากก็ได้ครับ (ใช้ชื่อ
terraform-state-lockingตามที่ระบุใน Backend Config) - กำหนด Partition Key: ชื่อ Partition Key ต้องเป็น
LockIDและเป็น Type String ครับ
# เพื่อสร้าง DynamoDB table สำหรับ State Locking (รันด้วยไฟล์ Terraform แยกต่างหาก)
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-state-locking" # ต้องตรงกับ dynamodb_table ใน backend config
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Locking Table"
Environment = "Shared"
}
}
เมื่อ DynamoDB Table พร้อมแล้ว และคุณได้กำหนด dynamodb_table ใน Backend Config ของ S3 แล้ว Terraform จะใช้ DynamoDB นี้เพื่อ Lock State File ในขณะที่มีการทำงาน เพื่อป้องกันความขัดแย้งครับ
คำสั่ง `terraform state` ที่ควรรู้
Terraform มีคำสั่งย่อยสำหรับจัดการ State File โดยเฉพาะครับ
terraform state list: แสดงรายการทรัพยากรทั้งหมดที่อยู่ใน State Fileterraform state show [resource_address]: แสดงรายละเอียดของทรัพยากรที่ระบุใน State Fileterraform state rm [resource_address]: ลบทรัพยากรออกจาก State File (แต่ไม่ลบทรัพยากรจริงบน Cloud)terraform state mv [source_address] [destination_address]: ย้ายทรัพยากรใน State File (เช่น เมื่อคุณเปลี่ยนชื่อทรัพยากรใน Code)terraform import [resource_address] [aws_resource_id]: นำเข้าทรัพยากรที่สร้างด้วยมือบน Cloud เข้ามาอยู่ใน State File ของ Terraform (มีประโยชน์มากเมื่อต้องการนำทรัพยากรเก่ามาบริหารด้วย IaC)
การจัดการ State File อย่างถูกวิธีเป็นสิ่งสำคัญอย่างยิ่งในการใช้ Terraform ในสภาพแวดล้อม Production ครับ การใช้ Remote State และ State Locking จะช่วยให้คุณทำงานได้อย่างปลอดภัยและมีประสิทธิภาพมากขึ้น
การจัดระเบียบ Code ด้วย Terraform Modules
เมื่อโครงสร้างพื้นฐานของคุณเริ่มซับซ้อนขึ้น การจัดระเบียบ Code ให้เป็นระเบียบและนำกลับมาใช้ใหม่ได้ (Reusable) จะกลายเป็นสิ่งสำคัญอย่างยิ่งครับ และนี่คือที่มาของ Terraform Modules
Modules คืออะไร? และทำไมต้องใช้?
Module ใน Terraform คือชุดของไฟล์ .tf ที่รวมทรัพยากรหลายๆ อย่างเข้าด้วยกันเป็นหน่วยตรรกะเดียว เพื่อให้สามารถนำไปใช้ซ้ำได้ง่ายขึ้นครับ
ข้อดีของการใช้ Modules:
- การนำกลับมาใช้ใหม่ (Reusability): คุณสามารถสร้าง Module สำหรับ VPC, EC2, หรือ RDS ที่มีการกำหนดค่าพื้นฐานไว้แล้ว และนำไปใช้ซ้ำในหลายๆ โปรเจกต์ หรือหลายๆ สภาพแวดล้อม (Dev, Staging, Prod) ได้
- การจัดระเบียบ Code: ช่วยให้ Code มีโครงสร้างที่เป็นระเบียบ อ่านง่าย และจัดการง่ายขึ้น
- ลดความซับซ้อน: แทนที่จะต้องเขียน Code ซ้ำๆ สำหรับทรัพยากรพื้นฐานทุกครั้งที่ต้องการสร้าง คุณเพียงแค่เรียกใช้ Module
- ความสอดคล้อง: รับประกันว่าทรัพยากรที่สร้างจาก Module เดียวกันจะมีการกำหนดค่าที่สอดคล้องกัน
- การทำงานร่วมกัน: ทีมสามารถพัฒนา Modules แยกกันและนำมารวมกันได้
ทุกไฟล์ Terraform ที่คุณเขียนเป็นของตัวเองก็ถือเป็น “Root Module” อยู่แล้วครับ แต่แนวคิดของ “Child Module” คือการจัดกลุ่มทรัพยากรย่อยๆ เข้าด้วยกัน
การสร้าง Custom Module
สมมติว่าเราต้องการสร้าง Module สำหรับ VPC ที่นำกลับมาใช้ใหม่ได้ครับ
โครงสร้างไดเรกทอรี:
.
├── main.tf # ไฟล์หลักของ Root Module
├── versions.tf
├── outputs.tf
├── variables.tf
└── modules/
└── vpc/
├── main.tf
├── variables.tf
└── outputs.tf
Code สำหรับ modules/vpc/main.tf:
# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
tags = merge(var.tags, {
Name = var.name
})
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = merge(var.tags, {
Name = "${var.name}-igw"
})
}
# (สามารถเพิ่ม Subnets, Route Tables, NAT Gateway เข้าไปใน Module นี้ได้อีก)
Code สำหรับ modules/vpc/variables.tf:
# modules/vpc/variables.tf
variable "name" {
description = "Name for the VPC and related resources."
type = string
}
variable "vpc_cidr" {
description = "CIDR block for the VPC."
type = string
}
variable "enable_dns_hostnames" {
description = "A boolean flag to enable/disable DNS hostnames in the VPC."
type = bool
default = true
}
variable "enable_dns_support" {
description = "A boolean flag to enable/disable DNS support in the VPC."
type = bool
default = true
}
variable "tags" {
description = "A map of tags to add to all resources."
type = map(string)
default = {}
}
Code สำหรับ modules/vpc/outputs.tf:
# modules/vpc/outputs.tf
output "vpc_id" {
description = "The ID of the VPC."
value = aws_vpc.this.id
}
output "vpc_cidr_block" {
description = "The CIDR block of the VPC."
value = aws_vpc.this.cidr_block
}
output "internet_gateway_id" {
description = "The ID of the Internet Gateway."
value = aws_internet_gateway.this.id
}
วิธีการเรียกใช้ Custom Module ใน Root Module (main.tf):
# main.tf (ใน Root Module)
module "my_new_vpc" {
source = "./modules/vpc" # ระบุ Path ไปยังไดเรกทอรีของ Module ที่สร้างไว้
name = "production-vpc"
vpc_cidr = "10.10.0.0/16"
tags = {
Environment = "Production"
Project = "SiamLancardApp"
}
}
# สามารถใช้ output จาก module ได้
output "production_vpc_id" {
value = module.my_new_vpc.vpc_id
}
เมื่อรัน terraform init Terraform จะรู้จัก Module ที่คุณสร้างขึ้น และคุณสามารถเรียกใช้ terraform plan และ terraform apply ได้ตามปกติครับ
การใช้ Public Modules จาก Terraform Registry
นอกจากการสร้าง Custom Module แล้ว คุณยังสามารถใช้ Modules ที่ชุมชนสร้างขึ้นและเผยแพร่ใน Terraform Registry ได้อีกด้วยครับ Modules เหล่านี้มักจะได้รับการบำรุงรักษาอย่างดีและครอบคลุม Use Case ที่หลากหลาย
ตัวอย่างการใช้ Module จาก Terraform Registry:
# main.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws" # ระบุผู้เผยแพร่/ชื่อ Module/Cloud Provider
version = "3.18.1" # ระบุเวอร์ชันที่ต้องการ
name = "my-registry-vpc"
cidr = "10.0.0.0/16"
azs = ["ap-southeast-1a", "ap-southeast-1b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
output "vpc_id" {
value = module.vpc.vpc_id
}
การใช้ Terraform Registry Modules ช่วยให้คุณสามารถสร้างโครงสร้างพื้นฐานที่ซับซ้อนได้อย่างรวดเร็วและน่าเชื่อถือ โดยไม่ต้องเริ่มต้นจากศูนย์เลยครับ
การนำ Modules มาใช้ในโปรเจกต์ของคุณจะช่วยยกระดับการจัดการ IaC ให้มีประสิทธิภาพและขยายขนาดได้ดียิ่งขึ้นไปอีกขั้นครับ อ่านเพิ่มเติมเกี่ยวกับการออกแบบ Modules ที่ดี
แนวปฏิบัติที่ดีที่สุด (Best Practices) ในการใช้ Terraform กับ AWS
การใช้ Terraform อย่างมีประสิทธิภาพในสภาพแวดล้อมจริง โดยเฉพาะในระดับองค์กร จำเป็นต้องมีแนวปฏิบัติที่ดีที่สุด (Best Practices) เพื่อให้มั่นใจในเรื่องความปลอดภัย, ความน่าเชื่อถือ, และความสามารถในการบำรุงรักษาครับ
โครงสร้าง Repository: Monorepo vs. Multirepo
- Monorepo: เก็บ Code Terraform ทั้งหมดสำหรับทุกสภาพแวดล้อมและทุก Microservice ไว้ใน Repository เดียวกัน
- ข้อดี: จัดการ Dependency ได้ง่าย, Refactoring ทำได้ง่าย, ความสอดคล้องสูง
- ข้อเสีย: Repository อาจมีขนาดใหญ่มาก, CI/CD อาจซับซ้อนขึ้น
- Multirepo: แยก Code Terraform ออกเป็นหลาย Repository ตามบริการ, ทีม, หรือสภาพแวดล้อม
- ข้อดี: Repository มีขนาดเล็ก, แยกความรับผิดชอบชัดเจน, CI/CD อาจง่ายขึ้นสำหรับแต่ละส่วน
- ข้อเสีย: จัดการ Dependency ข้าม Repository ยาก, อาจเกิดความซ้ำซ้อนของ Code
ไม่มีคำตอบที่ตายตัวว่าแบบไหนดีที่สุดครับ ขึ้นอยู่กับขนาดขององค์กร, โครงสร้างทีม, และความซับซ้อนของโครงสร้างพื้นฐานครับ สำหรับโปรเจกต์ขนาดเล็กถึงกลาง Multirepo อาจจะจัดการง่ายกว่า แต่สำหรับองค์กรขนาดใหญ่ที่มีทีม DevOps จำนวนมาก Monorepo อาจจะให้ประโยชน์ในระยะยาวมากกว่าครับ
การจัดการสภาพแวดล้อมด้วย Workspaces หรือ Folder Structure
คุณมักจะต้องสร้างโครงสร้างพื้นฐานที่เหมือนกันในหลายสภาพแวดล้อม (Dev, Staging, Production) มีสองแนวทางหลักในการจัดการสิ่งนี้:
- Terraform Workspaces: Terraform มีฟีเจอร์ Workspace ในตัว (
terraform workspace new dev,terraform workspace select prod) ซึ่งสร้าง State File แยกกันสำหรับแต่ละ Workspace- ข้อดี: จัดการ State File แยกกันได้ง่าย, เปลี่ยนสภาพแวดล้อมได้รวดเร็ว
- ข้อเสีย: ไม่ได้แยก Code อย่างสมบูรณ์ ทำให้ต้องใช้ Conditional Logic ใน Code (
count,for_each) ซึ่งอาจซับซ้อน, ไม่เหมาะกับการใช้ Modules ที่แตกต่างกันมากในแต่ละสภาพแวดล้อม
- Folder Structure (แนะนำ): สร้างไดเรกทอรีแยกสำหรับแต่ละสภาพแวดล้อม (เช่น
env/dev,env/staging,env/prod) และแต่ละไดเรกทอรีจะมีไฟล์ Terraform ของตัวเอง- ข้อดี: แยก Code อย่างชัดเจน, แต่ละสภาพแวดล้อมสามารถมี Code ที่แตกต่างกันได้โดยสิ้นเชิง, จัดการสิทธิ์การเข้าถึงได้ง่าย, เหมาะกับการใช้ Modules เพื่อลดความซ้ำซ้อนของ Code
- ข้อเสีย: ต้อง Copy & Paste Code หากไม่มี Modules ที่ดีพอ, อาจต้องจัดการ Remote State Backend แยกกัน
สำหรับโปรเจกต์ส่วนใหญ่และใน Production การใช้ Folder Structure ร่วมกับ Modules เป็นแนวทางที่ยืดหยุ่นและบำรุงรักษาได้ดีที่สุดครับ หากต้องการความสามารถระดับสูงในการจัดการ State และ Deploy ในหลายสภาพแวดล้อม ลองพิจารณาเครื่องมืออย่าง Terragrunt ครับ มันเป็น Wrapper รอบ Terraform ที่ช่วยให้การจัดการ Code ซ้ำๆ และ Remote State ในหลายสภาพแวดล้อมง่ายขึ้นมาก
ความปลอดภัยและการจัดการ Secrets
- หลักการ Least Privilege: IAM User/Role ที่ Terraform ใช้ควรมีสิทธิ์เท่าที่จำเป็นในการสร้าง/อัปเดต/ลบทรัพยากรเท่านั้น
- อย่า Hardcode Secrets: ห้ามใส่รหัสผ่าน, API Keys, หรือข้อมูลละเอียดอ่อนอื่นๆ โดยตรงในไฟล์ Terraform โดยเด็ดขาด
- ทางเลือก: ใช้ AWS Secrets Manager, AWS Parameter Store (Secure String), HashiCorp Vault, หรือ Environment Variables (แต่ก็ยังต้องระมัดระวัง)
- ใช้
sensitive = trueสำหรับ Output Variables ที่มีข้อมูลละเอียดอ่อน เพื่อไม่ให้แสดงในหน้าจอ Output
- State File Security:
- จัดเก็บ Remote State ใน S3 Bucket ที่มีการเข้ารหัส (SSE) และเปิดใช้งาน Versioning
- จำกัดการเข้าถึง S3 Bucket และ DynamoDB Table ที่ใช้สำหรับ State Locking ด้วย IAM Policies
การทำงานร่วมกับ CI/CD
การรวม Terraform เข้ากับ Pipeline ของ Continuous Integration/Continuous Delivery (CI/CD) เป็นสิ่งสำคัญสำหรับกระบวนการ Deploy ที่มีประสิทธิภาพครับ
- ขั้นตอนใน Pipeline:
- Validate:
terraform validate(ตรวจสอบ Syntax) - Plan:
terraform plan(สร้าง Execution Plan และเก็บเป็น Artifact) - Review: ให้ทีมงานตรวจสอบ Plan (อาจมีการอนุมัติด้วยมือ)
- Apply:
terraform apply [plan_file](ใช้ Plan ที่ถูกอนุมัติเพื่อ Deploy)
- Validate:
- เครื่องมือ: สามารถใช้ AWS CodePipeline, Jenkins, GitLab CI/CD, GitHub Actions, CircleCI หรือเครื่องมือ CI/CD อื่นๆ ได้
- Role-based Access: CI/CD Pipeline ควรใช้ IAM Role ที่มีสิทธิ์จำกัดในการ Deploy แทนการใช้ Access Key/Secret Key โดยตรง
การตรวจสอบและควบคุม (Code Review, Policy as Code)
- Code Review: ทุกการเปลี่ยนแปลง Code Terraform ควรผ่านการ Code Review โดยเพื่อนร่วมทีม เพื่อตรวจสอบข้อผิดพลาด, Security Best Practices, และการปฏิบัติตามมาตรฐานขององค์กร
- Policy as Code: ใช้เครื่องมือเช่น Sentinel (สำหรับ Terraform Enterprise) หรือ Open Policy Agent (OPA) เพื่อบังคับใช้กฎระเบียบด้านความปลอดภัยและการปฏิบัติตามข้อกำหนด (Compliance) ก่อนที่ Terraform จะทำการ Deploy ทรัพยากรจริงครับ ตัวอย่างเช่น ห้ามสร้าง S3 Bucket ที่เปิด Public Access โดยเด็ดขาด
การทำลายทรัพยากรอย่างปลอดภัย
- ใช้
terraform destroyด้วยความระมัดระวัง: คำสั่งนี้จะลบทรัพยากรทั้งหมดที่อยู่ใน State File โดยไม่ถามซ้ำ หาก State File ของ Production ถูกลบ ก็จะลบทรัพยากร Production ทั้งหมดครับ - Protection: สำหรับทรัพยากรสำคัญใน Production ให้ใช้ Meta-argument
prevent_destroy = trueในบล็อกlifecycleของ Resource เพื่อป้องกันการลบโดยไม่ตั้งใจ
resource "aws_db_instance" "production_db" {
# ...
lifecycle {
prevent_destroy = true
}
}
การนำแนวทางปฏิบัติเหล่านี้ไปใช้ จะช่วยให้การจัดการโครงสร้างพื้นฐานด้วย Terraform ของคุณเป็นไปอย่างราบรื่น, ปลอดภัย, และยั่งยืนในระยะยาวครับ
ตัวอย่าง Use Cases และ Advanced Topics
เมื่อคุณคุ้นเคยกับพื้นฐานของ Terraform แล้ว ก็ถึงเวลาที่จะสำรวจ Use Cases ที่ซับซ้อนขึ้นและคุณสมบัติขั้นสูงของมันครับ
Multi-Account และ Multi-Region Deployments
ในสภาพแวดล้อม Enterprise มักจะมีการใช้ AWS Account หลาย Account (เช่น Dev, Staging, Prod แยกกัน) และอาจจะ Deploy ในหลาย Region ครับ
- Multi-Account:
- Cross-Account IAM Roles: Terraform ใน Account A สามารถ Assume Role ใน Account B เพื่อ Deploy ทรัพยากรได้
- แยก State File: แต่ละ Account ควรมี State File ของตัวเอง (ใน S3 Bucket ของ Account นั้นๆ) เพื่อแยกการจัดการทรัพยากรอย่างชัดเจน
- Terragrunt: เครื่องมือนี้ช่วยให้การจัดการ Code ซ้ำๆ ข้าม Account และ Region ง่ายขึ้นมาก โดยเฉพาะการจัดการ State Backend
- Multi-Region:
- กำหนด Region ใน Provider: สามารถกำหนด Provider หลายตัวสำหรับแต่ละ Region ได้
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}provider "aws" {
alias = "ap_southeast_1"
region = "ap