Chuyển tới nội dung chính

Terraform: Cơ bản, Case Studies & Best Practices trên AWS

Lưu ý: Để xem giới thiệu về bộ Terraform chuẩn hóa cho DevOps Team, vui lòng xem tại đây.

Tài liệu này cung cấp hướng dẫn toàn diện về Terraform khi làm việc với AWS, từ các khái niệm cơ bản đến các ví dụ thực tế và các phương pháp hay nhất (Best Practices).

1. Tổng quan về Terraform

Terraform là một công cụ Infrastructure as Code (IaC) mã nguồn mở được phát triển bởi HashiCorp. Nó cho phép bạn định nghĩa và cung cấp cơ sở hạ tầng trung tâm dữ liệu bằng cách sử dụng ngôn ngữ cấu hình khai báo (HCL - HashiCorp Configuration Language).

Lợi ích chính:

  • Tự động hóa: Giảm thiểu lỗi con người và tăng tốc độ triển khai.
  • Nhất quán: Đảm bảo môi trường (dev, staging, prod) giống hệt nhau.
  • Quản lý trạng thái: Theo dõi tài nguyên đã tạo để cập nhật hoặc xóa an toàn.

Vòng đời cơ bản (Lifecycle):

2. Cài đặt

Có hai cách phổ biến để cài đặt Terraform: cài đặt trực tiếp từ binary/package manager hoặc sử dụng công cụ quản lý phiên bản (khuyên dùng).

2.1. Cách 1: Cài đặt theo tài liệu chính thức (Official Documentation)

Cách này phù hợp nếu bạn chỉ cần phiên bản mới nhất hoặc một phiên bản cố định duy nhất trên máy.

Bạn có thể tải binary đã biên dịch sẵn hoặc cài đặt thông qua package manager của hệ điều hành (Homebrew trên macOS, APT/YUM trên Linux, Chocolatey trên Windows).

Ví dụ cài đặt trên Ubuntu/Debian:

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [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

2.2. Cách 2: Sử dụng Terraform Version Manager (tfenv) (Khuyên dùng)

Trong môi trường làm việc thực tế, bạn thường phải làm việc với nhiều dự án sử dụng các phiên bản Terraform khác nhau. tfenv là công cụ giúp bạn cài đặt và chuyển đổi linh hoạt giữa các phiên bản Terraform một cách dễ dàng.

Bước 1: Cài đặt tfenv

Clone repository từ GitHub về máy:

git clone --depth=1 https://github.com/tfutils/tfenv.git ~/.tfenv

Thêm đường dẫn tfenv vào biến môi trường PATH (ví dụ cho .bashrc hoặc .zshrc):

echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bashrc
# Hoặc nếu dùng zsh
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.zshrc

Reload lại cấu hình shell:

source ~/.bashrc
# Hoặc
source ~/.zshrc

Bước 2: Cài đặt Terraform bằng tfenv

Liệt kê các phiên bản Terraform có sẵn:

tfenv list-remote

Cài đặt một phiên bản cụ thể (ví dụ 1.5.7):

tfenv install 1.5.7

Cài đặt phiên bản mới nhất:

tfenv install latest

Bước 3: Chuyển đổi phiên bản

Để sử dụng phiên bản vừa cài đặt:

tfenv use 1.5.7

Kiểm tra phiên bản hiện tại:

terraform version

Mẹo: Bạn có thể tạo file .terraform-version chứa số phiên bản (ví dụ 1.5.7) trong thư mục gốc của dự án. Khi đó tfenv sẽ tự động nhận diện và chuyển sang phiên bản đúng khi bạn cd vào thư mục đó.

3. Các khái niệm cơ bản

3.1. Provider

Provider là plugin giúp Terraform tương tác với các API của dịch vụ đám mây (như AWS, Azure, GCP).

provider "aws" {
region = "ap-southeast-1" # Singapore
}

3.2. Resources

Resource là thành phần quan trọng nhất, định nghĩa các đối tượng hạ tầng cụ thể (EC2, S3, VPC).

resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

tags = {
Name = "HelloWorld"
}
}

3.3. Variables & Outputs

  • Variables (Input): Tham số hóa cấu hình để tái sử dụng.
  • Outputs: Hiển thị thông tin quan trọng sau khi chạy (ví dụ: IP của server).
variable "instance_type" {
description = "Loại EC2 instance"
default = "t2.micro"
}

output "instance_ip" {
value = aws_instance.web_server.public_ip
}

3.4. State

Terraform lưu trạng thái của hạ tầng trong file terraform.tfstate. File này ánh xạ giữa code của bạn và tài nguyên thực tế trên cloud. Không bao giờ sửa file này thủ công.

3.5. Các lệnh cơ bản

  • terraform init: Khởi tạo thư mục làm việc, tải provider.
  • terraform plan: Tạo kế hoạch, xem trước thay đổi.
  • terraform apply: Áp dụng thay đổi, tạo hoặc sửa tài nguyên.
  • terraform destroy: Xóa toàn bộ tài nguyên được quản lý.

4. Các trường hợp sử dụng

4.1. Variables & Input Validation

Sử dụng biến để tùy chỉnh cấu hình và validation để kiểm tra dữ liệu đầu vào.

variable "environment" {
type = string
default = "dev"
description = "Môi trường triển khai (dev/prod)"

validation {
condition = contains(["dev", "prod"], var.environment)
error_message = "Environment phải là 'dev' hoặc 'prod'."
}
}

variable "db_password" {
type = string
sensitive = true # Ẩn giá trị trong log
}

output "connect_string" {
value = "Server=${var.environment};Database=mydb"
}

4.2. Locals & Data Sources

Sử dụng locals để xử lý logic nội bộ và data để lấy thông tin có sẵn.

# Locals: Biến cục bộ, giúp code gọn gàng hơn
locals {
project_name = "demo-app"
common_tags = {
Project = local.project_name
Env = var.environment
ManagedBy = "Terraform"
}
}

# Data Source: Lấy thông tin AMI mới nhất thay vì hardcode ID
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}

resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = local.common_tags
}

4.3. Remote State với S3 & DynamoDB

Quản lý state file tập trung cho team, hỗ trợ locking để tránh xung đột.

terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}

Lưu ý: Bucket S3 và DynamoDB table phải được tạo trước (có thể bằng tay hoặc một project Terraform riêng).

4.4. Loops (count vs for_each)

Tạo nhiều tài nguyên tương tự nhau mà không cần copy-paste code.

Sử dụng count:

resource "aws_instance" "server" {
count = 3
ami = "ami-12345678"
instance_type = "t2.micro"

tags = {
Name = "Server-${count.index}"
}
}

Sử dụng for_each:

variable "project_buckets" {
type = set(string)
default = ["assets", "logs", "backup"]
}

resource "aws_s3_bucket" "example" {
for_each = var.project_buckets
bucket = "my-app-${each.key}"
}

4.5. Dynamic Blocks

Tạo các block lặp lại bên trong một resource, ví dụ như ingress rules trong Security Group.

variable "ingress_ports" {
type = list(number)
default = [80, 443, 22, 8080]
}

resource "aws_security_group" "web" {
name = "allow_web_traffic"

dynamic "ingress" {
for_each = var.ingress_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}

5. Best Practices

5.1. Cấu trúc thư mục (Code Structure)

Sắp xếp code rõ ràng giúp dễ bảo trì.

.
├── main.tf # Chứa các resource chính
├── variables.tf # Khai báo biến
├── outputs.tf # Khai báo output
├── versions.tf # Cấu hình version terraform/provider
└── terraform.tfvars # Giá trị của biến (không commit file này nếu có secret)

5.2. Quản lý State (State Management)

  • Remote State: Luôn sử dụng Remote Backend (S3, Terraform Cloud) khi làm việc nhóm.
  • State Locking: Sử dụng DynamoDB để lock state, ngăn chặn 2 người chạy apply cùng lúc.
  • Isolation: Tách biệt state cho các môi trường (dev, prod) bằng cách dùng Workspaces hoặc thư mục riêng biệt.

5.3. Bảo mật (Security)

  • Không hardcode secrets: Không bao giờ viết Access Key/Secret Key trực tiếp trong file .tf. Sử dụng biến môi trường hoặc AWS Profile.
  • .gitignore: Luôn ignore các file nhạy cảm:
    .terraform/
    *.tfstate
    *.tfstate.backup
    *.tfvars

5.4. Quy trình làm việc (Workflow)

  • Version Pinning: Luôn chỉ định phiên bản cụ thể cho Provider và Terraform core để tránh lỗi khi có bản cập nhật mới (breaking changes).
    terraform {
    required_providers {
    aws = {
    source = "hashicorp/aws"
    version = "~> 5.0"
    }
    }
    }
  • Format & Validate:
    • Chạy terraform fmt để định dạng code chuẩn.
    • Chạy terraform validate để kiểm tra lỗi cú pháp trước khi plan.
  • Review Plan: Luôn kiểm tra kỹ output của terraform plan trước khi apply.

Sơ đồ Workflow minh họa:

5.5. Sử dụng module

Đóng gói code để tái sử dụng nhiều lần hoặc chia sẻ giữa các dự án.

Cấu trúc:

modules/
vpc/
main.tf
variables.tf
outputs.tf

Sử dụng Module:

module "my_vpc" {
source = "./modules/vpc"

vpc_cidr = "10.0.0.0/16"
environment = "production"
}

5.6. Quản lý phiên bản Terraform (Version Management)

Trong môi trường thực tế, việc quản lý phiên bản Terraform là cực kỳ quan trọng vì:

  • Tính tương thích: Terraform thường xuyên cập nhật và có thể chứa các thay đổi phá vỡ tương thích (breaking changes). Code chạy tốt ở version này có thể lỗi ở version khác.
  • State File: State file được tạo bởi phiên bản Terraform mới hơn thường không thể đọc được bởi phiên bản cũ hơn. Nếu team không đồng bộ version, rất dễ gây hỏng state.

Giải pháp: Sử dụng tfenv tfenv là một công cụ quản lý phiên bản (tương tự nvm cho Node.js hay rbenv cho Ruby), giúp giải quyết các vấn đề trên bằng cách:

  1. Đa phiên bản: Cho phép cài đặt và tồn tại song song nhiều phiên bản Terraform trên cùng một máy.
  2. Tự động chuyển đổi: Tự động chọn phiên bản Terraform phù hợp dựa trên file cấu hình .terraform-version trong thư mục dự án.
  3. Đồng bộ hóa: Đảm bảo tất cả thành viên trong team và hệ thống CI/CD đều sử dụng chính xác cùng một phiên bản, loại bỏ lỗi do sai lệch môi trường.

Cách hoạt động: Khi bạn chạy lệnh terraform, thực chất bạn đang gọi qua tfenv. Công cụ này sẽ kiểm tra file .terraform-version (chứa ví dụ: 1.5.7), sau đó chuyển hướng lệnh đến binary Terraform 1.5.7 đã được cài đặt. Điều này giúp việc chuyển đổi giữa các dự án cũ và mới trở nên trong suốt và an toàn.