Tối ưu hóa chi phí và cấu hình Terraform cho AWS EC2 trong môi trường DevOps
Trong kỷ nguyên điện toán đám mây, việc quản lý hạ tầng như một mã nguồn (Infrastructure as Code) đã trở thành tiêu chuẩn vàng của ngành DevOps. Terraform là công cụ hàng đầu giúp các kỹ sư tự động hóa việc triển khai tài nguyên trên nhiều nhà cung cấp đám mây khác nhau, trong đó AWS là nền tảng phổ biến nhất. Một trong những thách thức lớn nhất mà các tổ chức gặp phải không chỉ là triển khai nhanh chóng mà còn là kiểm soát chi phí và đảm bảo tính nhất quán của các cấu hình. Bài viết này sẽ hướng dẫn chi tiết cách sử dụng Terraform để khởi tạo một máy ảo EC2 (Elastic Compute Cloud) với các thiết lập tối ưu về bảo mật, hiệu suất và chi phí, đồng thời giới thiệu các kỹ thuật quản lý trạng thái chuyên nghiệp.
Tại sao cần sử dụng Terraform cho AWS EC2
Thay vì thao tác thủ công qua bảng điều khiển AWS Console, việc sử dụng Terraform giúp loại bỏ sai sót do con người, cho phép bạn theo dõi lịch sử thay đổi của hạ tầng và dễ dàng sao chép cấu hình qua nhiều môi trường khác nhau như phát triển (dev), thử nghiệm (staging) và sản xuất (prod). Khi cấu hình được lưu dưới dạng mã, bạn có thể kiểm tra lại quy trình này thông qua hệ thống version control như Git, tạo nên một quy trình audit chặt chẽ. Hơn nữa, Terraform cho phép bạn mô tả trạng thái mong muốn của hạ tầng, và công cụ này sẽ tự động tính toán những thay đổi cần thiết để đưa hệ thống hiện tại về trạng thái đó, giúp việc nâng cấp hay sửa lỗi trở nên an toàn và minh bạch hơn rất nhiều.
Cấu trúc cơ bản của bài viết
Hướng dẫn này sẽ đi sâu vào việc xây dựng một cấu trúc dự án Terraform chuẩn, bắt đầu từ việc khai báo nhà cung cấp (provider) AWS, định hình các biến số (variables) linh hoạt, tạo ra máy chủ EC2 với các cấu hình bảo mật tối ưu, và cuối cùng là triển khai thực tế thông qua dòng lệnh. Chúng ta sẽ không chỉ dừng lại ở việc tạo máy chủ mà còn chú trọng đến việc cấu hình nhóm an ninh (Security Group) để hạn chế truy cập không cần thiết, sử dụng các biến để phân biệt môi trường, và áp dụng các tập lệnh (scripts) khởi động để cài đặt phần mềm tự động ngay khi máy chủ được bật.
Chuẩn bị môi trường và cấu trúc dự án
Trước khi bắt đầu viết mã, bạn cần đảm bảo môi trường làm việc của mình đã được thiết lập đúng cách. Điều tiên quyết là cài đặt Terraform trên máy trạm của bạn, đồng thời cấu hình quyền truy cập AWS thông qua biến môi trường hoặc file cấu hình ~/.aws/credentials. Việc sử dụng các biến môi trường cho thông tin đăng nhập AWS là một phương pháp tốt để bảo mật, tránh việc mã hóa trực tiếp key và secret vào file mã nguồn của bạn. Ngoài ra, việc sử dụng phiên bản Terraform cố định (pinning) thông qua file terraform_version trong .terraform-version hoặc trong file configuration là một thực hành tốt để đảm bảo tính ổn định khi cộng tác với đồng nghiệp.
Chúng ta sẽ bắt đầu bằng việc tạo một thư mục dự án mới để chứa tất cả các file cấu hình. Trong cấu trúc tiêu chuẩn của Terraform, bạn nên có một file main.tf để khai báo provider và các tài nguyên, một file variables.tf để định nghĩa các tham số đầu vào, và một file outputs.tf để trả về các thông tin quan trọng sau khi triển khai như địa chỉ IP hay tên miền của máy chủ. Việc phân chia rõ ràng các file này giúp mã nguồn dễ đọc, dễ bảo trì và dễ mở rộng trong tương lai. Bạn cũng nên tạo một file .gitignore để loại trừ các thư mục nhạy cảm như .terraform và terraform.tfstate khỏi việc commit lên repository, đảm bảo an toàn cho dữ liệu trạng thái của bạn.
Hãy thực hiện các lệnh sau để tạo cấu trúc thư mục cơ bản và khởi tạo dự án:
mkdir aws-ec2-terraform
cd aws-ec2-terraform
touch main.tf variables.tf outputs.tf .gitignore terraform.tfvars
Sau khi tạo xong các file, bạn cần khai báo phiên bản Terraform và nhà cung cấp AWS trong file main.tf. Điều này giúp Terraform biết chính xác công cụ nào và phiên bản nào sẽ được sử dụng để quản lý tài nguyên. Việc khai báo version cho AWS provider là rất quan trọng để tránh các thay đổi không mong muốn trong API của AWS khi họ nâng cấp hệ thống. Chúng ta sẽ khai báo sử dụng phiên bản stable nhất của AWS provider hiện có.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.6.0"
}
provider "aws" {
region = var.aws_region
}
Cấu hình biến số và nhóm an ninh linh hoạt
Điểm mạnh lớn nhất của Terraform là khả năng tham số hóa thông qua variables. Thay vì cứng nhắc viết chết tên vùng (region), loại instance hay hệ điều hành trong code, chúng ta sẽ khai báo chúng trong file variables.tf. Điều này cho phép bạn tái sử dụng cùng một file cấu hình cho nhiều môi trường khác nhau chỉ bằng cách thay đổi file terraform.tfvars. Ví dụ, trong môi trường phát triển bạn có thể dùng instance loại t3.micro để tiết kiệm chi phí, trong khi môi trường sản xuất sẽ sử dụng t3.medium hoặc cao hơn để đảm bảo hiệu năng. Việc này cũng giúp quy trình kiểm duyệt code (Code Review) trở nên hiệu quả hơn vì người xem chỉ cần tập trung vào logic thay vì các giá trị cụ thể của từng môi trường.
Bên cạnh các biến cơ bản, việc quản lý nhóm an ninh (Security Group) là yếu tố then chốt để bảo vệ máy chủ của bạn khỏi các cuộc tấn công từ bên ngoài. Thay vì tạo Security Group thủ công trên Console, chúng ta sẽ mô tả nó trong Terraform. Nguyên tắc bảo mật quan trọng nhất là chỉ mở các cổng thực sự cần thiết. Đối với một máy chủ web hoặc ứng dụng thông thường, bạn chỉ cần mở cổng SSH (22) để quản trị từ địa chỉ IP của bạn và cổng HTTP/HTTPS (80/444) cho traffic công cộng. Việc mở toàn bộ traffic từ 0.0.0.0/0 là một rủi ro lớn và nên tránh hoàn toàn trừ khi có biện pháp bảo vệ bổ sung như Web Application Firewall (WAF).
Chúng ta sẽ định nghĩa các biến cần thiết trong file variables.tf, bao gồm vùng triển khai, loại instance, tên hệ điều hành, và danh sách các biến môi trường khác. Đồng thời, trong file main.tf, chúng ta sẽ tạo tài nguyên aws_security_group và aws_instance. Hãy lưu ý rằng trong thực tế sản xuất, bạn có thể sử dụng module từ registry của Terraform để tạo các cấu hình phức tạp hơn, nhưng ở đây chúng ta sẽ viết ra tường minh để bạn hiểu rõ cơ chế hoạt động.
variable "aws_region" {
description = "AWS region to deploy resources"
type = string
default = "ap-southeast-1"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "ami_id" {
description = "Amazon Machine Image ID for the instance"
type = string
}
variable "environment" {
description = "Deployment environment (dev, staging, prod)"
type = string
default = "dev"
}
Tiếp theo là phần tạo nhóm an ninh trong main.tf. Chúng ta sẽ cấu hình để chỉ cho phép truy cập SSH từ địa chỉ mạng hiện tại của bạn (có thể thay đổi hoặc sử dụng biến cho phép IP cụ thể) và mở cổng 80 cho tất cả lưu lượng truy cập web. Đây là cấu hình cơ bản nhưng an toàn cho một máy chủ web đơn giản.
resource "aws_security_group" "web_sg" {
name = "web-sg-${var.environment}"
description = "Security group for web server in ${var.environment} environment"
vpc_id = aws_vpc.main.id
ingress {
description = "SSH access from specific IP"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Note: Replace with your specific IP in production
}
ingress {
description = "HTTP access from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-sg-${var.environment}"
Environment = var.environment
}
}
Để tạo Security Group, bạn cần một VPC (Virtual Private Cloud). Trong ví dụ này, chúng ta sẽ giả định rằng bạn đã có một VPC hoặc tạo một VPC mặc định đơn giản để bài hướng dẫn hoàn chỉnh. Dưới đây là đoạn code tạo VPC đơn giản nếu bạn chưa có, hoặc bạn có thể bỏ qua và thay thế vpc_id bằng ID VPC của bạn trong production.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "main-vpc-${var.environment}"
}
}
Triển khai máy chủ EC2 và kịch bản khởi động
Bây giờ là phần quan trọng nhất: tạo máy chủ EC2. Khi khai báo tài nguyên aws_instance, chúng ta sẽ liên kết nó với Security Group đã tạo ở trên. Điểm đặc biệt cần lưu ý là việc sử dụng user_data. Đây là một tính năng mạnh mẽ cho phép bạn chạy các script shell hoặc cloud-init ngay khi máy chủ vừa được khởi động lần đầu. Script này có thể được dùng để cài đặt Apache/Nginx, Docker, hoặc các ứng dụng của bạn mà không cần phải vào SSH thủ công sau đó. Điều này mang lại tính tự động hóa cao và đảm bảo mọi máy chủ được tạo ra đều có cùng một cấu hình phần mềm.
Trong ví dụ này, chúng ta sẽ viết một script đơn giản để cài đặt Nginx và tạo một trang web chào mừng đơn giản. Điều này giúp bạn kiểm tra ngay lập tức xem máy chủ có đang chạy và cấu hình network có thông suốt hay không. Ngoài ra, chúng ta sẽ thêm các tag (nhãn) vào instance để quản lý chi phí và theo dõi dễ dàng hơn. AWS tính phí dựa trên nhiều yếu tố, và việc gắn tag môi trường giúp bạn lọc và phân tích hóa đơn một cách hiệu quả qua AWS Cost Explorer.
resource "aws_instance" "web_server" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y nginx
systemctl start nginx
systemctl enable nginx
echo "<h1>Welcome to ${var.environment} environment!</h1>" > /usr/share/nginx/html/index.html
EOF
tags = {
Name = "web-server-${var.environment}"
Environment = var.environment
Project = "terraform-demo"
}
}
Để tạo instance, bạn cũng cần một subnet để máy chủ đặt trong đó. Chúng ta sẽ tạo một subnet public với gateway để có thể truy cập internet. Dưới đây là code tạo subnet, lưu ý rằng trong môi trường production phức tạp, bạn nên chia mạng thành public và private subnet với nhiều Availability Zone để đảm bảo độ tin cậy (High Availability).
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-${var.environment}"
}
}
Triển khai và quản lý trạng thái
Sau khi đã viết xong tất cả các file cấu hình, bước tiếp theo là khởi tạo và áp dụng chúng lên AWS. Quy trình này bao gồm ba bước chính: init, plan, và apply. Bước init tải các provider cần thiết và thiết lập state file ban đầu. Bước plan là bước quan trọng nhất để kiểm tra, nó sẽ so sánh trạng thái hiện tại của AWS với file cấu hình và liệt kê những gì sẽ được tạo, sửa hoặc xóa. Hãy luôn đọc kỹ output của lệnh plan trước khi chạy apply để tránh các thao tác gây mất dữ liệu không mong muốn.
Để thực hiện, bạn cần mở terminal, di chuyển vào thư mục dự án và chạy các lệnh sau. Lưu ý rằng lệnh apply sẽ hỏi bạn xác nhận trước khi thực thi. Nếu bạn muốn tự động xác nhận (không khuyến khích trong môi trường sản xuất nếu chưa kiểm tra kỹ), bạn có thể thêm cờ -auto-approve, nhưng tốt nhất là nên đọc kỹ plan.
terraform init
terraform plan
terraform apply
Trong quá trình chạy, nếu bạn gặp lỗi về AMI ID, hãy nhớ rằng AMI thay đổi theo từng vùng (region). Bạn cần tìm ID của AMI Ubuntu hoặc Amazon Linux phù hợp với vùng bạn đang dùng. Có thể sử dụng data source "aws_ami" để tự động tìm AMI mới nhất mà không cần hardcode ID, giúp code của bạn bền vững hơn theo thời gian.
Để lấy lại địa chỉ IP công cộng của máy chủ vừa tạo, hãy chạy lệnh terraform output hoặc xem trong file outputs.tf nếu bạn đã khai báo. Điều này giúp bạn truy cập trang web vừa tạo.
terraform output public_ip
Trong file outputs.tf, bạn có thể khai báo:
output "public_ip" {
description = "Public IP of the web server"
value = aws_instance.web_server.public_ip
}
Lưu ý quan trọng về bảo mật và chi phí
Một khi hệ thống đã chạy, trách nhiệm của bạn là đảm bảo nó không gây tốn kém chi phí không cần thiết hoặc bị tấn công. Hãy nhớ tắt máy chủ (terraform destroy) sau khi hoàn thành bài thực hành nếu đây là môi trường demo, vì AWS tính phí cho mỗi giờ máy chủ hoạt động. Trong sản xuất, hãy sử dụng các tính năng như Instance Scheduler hoặc Lambda để tự động tắt các resource không cần thiết vào cuối ngày hoặc cuối tuần. Đặc biệt, hãy luôn tránh việc hardcode credentials (Access Key/Secret Key) vào file code. Luôn sử dụng biến môi trường hoặc IAM Role nếu chạy trên CI/CD pipeline.
Về bảo mật, hãy xem xét việc thay thế CIDR 0.0.0.0/0 trong Security Group bằng địa chỉ IP thực tế của bạn hoặc sử dụng VPC Endpoints nếu có thể. Ngoài ra, hãy cân nhắc sử dụng các bản AMI đã được quét bảo mật và cập nhật thường xuyên. Terraform chỉ quản lý cấu hình, việc bảo mật hệ điều hành và ứng dụng bên trong vẫn là trách nhiệm của bạn. Hãy kết hợp Terraform với các công cụ như Checkov hoặc tfsec để quét code tìm các lỗ hổng bảo mật trước khi triển khai.
Kết luận
Với những gì đã hướng dẫn trên, bạn đã có thể tự mình xây dựng một quy trình quản lý hạ tầng AWS EC2 chuyên nghiệp bằng Terraform. Từ việc chuẩn bị môi trường, cấu hình biến số linh hoạt, thiết lập nhóm an ninh chặt chẽ đến việc triển khai máy chủ với kịch bản khởi động tự động. Đây mới chỉ là bước đầu của hành trình DevOps, nhưng nó đặt nền móng vững chắc cho việc quản lý hạ tầng phức tạp hơn. Khi bạn làm chủ được các khái niệm này, việc mở rộng sang các tài nguyên khác như database (RDS), load balancer (ALB), hay container (ECS/EKS) sẽ trở nên dễ dàng hơn rất nhiều.
Hy vọng bài viết này giúp bạn hiểu sâu hơn về cách Terraform hoạt động và cách áp dụng nó vào thực tế để tối ưu hóa cả chi phí và hiệu suất. Hãy luôn thực hành, đọc tài liệu chính thức và không ngừng cải thiện quy trình làm việc của mình. Chúc bạn thành công trong việc xây dựng các hệ thống hạ tầng đám mây vững chắc và an toàn.