Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 36 — Infrastructure as Code (IaC): Terraform Basics

Operations Management for Tech Bài 36/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn là kỹ sư vận hành tại một công ty fintech ở TP.HCM. Sếp yêu cầu dựng một môi trường staging giống hệt production để test tính năng thanh toán mới. Bạn mở console AWS, click chuột tạo VPC, tạo subnet, tạo mấy con EC2, cấu hình security group, gắn load balancer... Mất nửa ngày. Ba tuần sau, khách hàng lớn cần thêm một môi trường demo riêng. Bạn lại làm lại từ đầu — và lần này quên gắn một security group, dẫn đến sự cố. Đến khi cần audit "ai đã mở port 22 ra Internet công khai?" thì không ai trả lời được, vì mọi thay đổi đều nằm trong đầu các kỹ sư và trong lịch sử click chuột không ai lưu lại.

Đây chính xác là bài toán mà Infrastructure as Code (IaC) giải quyết. Thay vì dựng hạ tầng bằng tay qua giao diện web (cái gọi là "ClickOps"), bạn mô tả toàn bộ hạ tầng — máy chủ, mạng, database, quyền truy cập — bằng các file văn bản có thể đọc, review và lưu vào Git. Chạy một lệnh, hạ tầng tự dựng lên. Muốn thêm môi trường mới? Copy cấu hình, đổi vài tham số, chạy lại. Muốn biết ai thay đổi gì? Xem lịch sử Git.

Trong bài này chúng ta tập trung vào Terraform — công cụ IaC phổ biến nhất hiện nay (do HashiCorp phát triển), mã nguồn mở, hoạt động đa nền tảng (AWS, GCP, Azure, và cả các nhà cung cấp Việt Nam như VNG Cloud, Viettel Cloud qua provider tương thích). Đây là kỹ năng nền tảng mà bất kỳ người làm Ops nào cũng cần, và nó là bệ phóng cho các chủ đề nâng cao hơn (GitOps, quản lý cấu hình) ở các bài sau. Lưu ý: bài này chỉ dạy nền tảng Terraform — không đi sâu vào Ansible/Puppet (đó là Bài 37) hay ArgoCD/Flux (Bài 40).

Khái niệm cốt lõi

Ba lý do khiến IaC trở thành chuẩn mực

Reproducible (Tái lập được). Cùng một file cấu hình, chạy ở đâu cũng cho kết quả giống nhau. Dev, staging, production được dựng từ cùng một khuôn mẫu, chỉ khác vài biến (kích thước máy, số lượng instance). Điều này xoá bỏ câu nói kinh điển "trên máy tôi chạy được mà". Sự khác biệt giữa các môi trường không còn là bí ẩn — nó nằm rõ ràng trong các file biến.

Versionable (Quản lý phiên bản được). Mọi thay đổi hạ tầng đi qua Git. Bạn tạo Pull Request, đồng đội review "tại sao lại mở thêm port này?", rồi mới merge. Hạ tầng được đối xử y hệt như code ứng dụng: có review, có lịch sử, có khả năng rollback về commit trước.

Auditable (Kiểm toán được). Câu hỏi "ai thay đổi gì, khi nào, vì sao" luôn có câu trả lời trong Git log. Với các công ty phải tuân thủ chuẩn như PCI-DSS (bắt buộc với fintech), ISO 27001, hay Nghị định 13/2023 về bảo vệ dữ liệu cá nhân tại Việt Nam, khả năng audit này là yêu cầu bắt buộc chứ không phải tuỳ chọn.

Declarative — mô tả "cái gì" thay vì "làm thế nào"

Terraform theo phong cách declarative (khai báo). Bạn không viết "tạo máy A, rồi tạo mạng B, rồi gắn A vào B". Bạn khai báo trạng thái mong muốn: "tôi muốn có 3 máy chủ, một VPC, một database". Terraform tự so sánh với thực tế và tính ra cần làm gì để đạt trạng thái đó — tạo mới cái thiếu, sửa cái sai, xoá cái thừa. Đây gọi là idempotent: chạy 1 lần hay 10 lần, kết quả cuối vẫn giống nhau.

Bốn khái niệm bạn phải nắm

Provider — plugin kết nối Terraform với một nền tảng cụ thể (AWS, GCP, Cloudflare...). Nó "biết cách" gọi API của nền tảng đó.

Resource — đơn vị hạ tầng cơ bản: một EC2 instance, một S3 bucket, một bản ghi DNS. Đây là thứ bạn khai báo nhiều nhất.

State (trạng thái) — đây là khái niệm quan trọng nhất và cũng gây nhầm lẫn nhất. Terraform lưu một file terraform.tfstate ghi lại "tôi đang quản lý những tài nguyên nào, ID của chúng là gì". Đây là bản đồ nối giữa cấu hình của bạn và tài nguyên thật ngoài đời. Mất state = Terraform không còn biết nó đang quản lý cái gì.

Plan & Applyterraform plan cho bạn xem trước những gì sẽ thay đổi (giống bản nháp trước khi làm thật). terraform apply thực thi thay đổi đó. Thói quen luôn xem plan trước khi apply là điều phân biệt kỹ sư cẩn thận với kỹ sư gây sự cố.

Vòng đời làm việc cơ bản

Quy trình chuẩn gồm bốn lệnh: terraform init (tải provider, khởi tạo backend) → terraform plan (xem trước) → terraform apply (thực thi) → terraform destroy (xoá sạch khi không cần nữa, ví dụ môi trường test tạm).

Tình huống thực tế

Ví dụ 1 — Startup giao hàng ở Hà Nội thoát khỏi "ClickOps"

Một startup logistics khoảng 25 người tại Hà Nội chạy toàn bộ trên AWS Singapore. Ban đầu mọi thứ được dựng thủ công qua console. Vấn đề bùng nổ khi họ cần tách môi trường: dev, staging, production. Mỗi lần dựng môi trường mới mất 1-2 ngày và luôn có sai lệch — staging dùng instance t3.medium còn production dùng t3.large, nhưng chẳng ai chắc chắn, vì thông tin nằm rải rác trong đầu ba kỹ sư.

Họ chuyển sang Terraform. Toàn bộ hạ tầng được viết thành module: một module network (VPC, subnet), một module web (EC2, load balancer), một module db (RDS). Ba môi trường dùng chung code, chỉ khác file biến: dev.tfvars khai instance_type = "t3.small", prod.tfvars khai t3.large. Kết quả: dựng một môi trường mới từ 2 ngày rút còn 15 phút. Khi một kỹ sư nghỉ việc, kiến thức hạ tầng không đi theo anh ta — nó nằm trong Git.

Bài học: Giá trị lớn nhất của IaC với công ty nhỏ không phải là "tự động hoá cho ngầu", mà là biến tri thức hạ tầng từ ngầm định trong đầu người thành tường minh trong code.

Ví dụ 2 — Sự cố mất state của một team thương mại điện tử

Một team e-commerce lưu file terraform.tfstate ngay trong repo Git và commit lên. Nghe có vẻ hợp lý, nhưng hai kỹ sư cùng chạy apply trên hai nhánh khác nhau, mỗi người có một bản state riêng. Khi merge, state bị xung đột. Tệ hơn, state chứa mật khẩu database dạng plaintext — giờ nó nằm vĩnh viễn trong lịch sử Git, một lỗ hổng bảo mật nghiêm trọng.

Họ khắc phục bằng cách chuyển state sang remote backend: lưu file state trên S3, kèm DynamoDB để khoá (state locking) — đảm bảo tại một thời điểm chỉ một người apply được, người khác phải chờ. State được mã hoá, không còn nằm trong Git. Xung đột biến mất, và mật khẩu không còn rò rỉ.

Bài học: Với bất kỳ team nào từ 2 người trở lên, remote backend + state locking là bắt buộc. Không bao giờ commit file state vào Git. Đây là lỗi kinh điển mà gần như mọi team đều mắc một lần.

Ví dụ 3 — VNG Cloud và bài toán đa nền tảng

Một công ty game ở Việt Nam vận hành phần lớn trên VNG Cloud (để tối ưu độ trễ cho người chơi trong nước và tuân thủ yêu cầu lưu trữ dữ liệu nội địa), nhưng dùng Cloudflare cho CDN và AWS S3 cho backup. Trước đây mỗi nền tảng quản lý một kiểu, không đồng bộ. Với Terraform, họ dùng nhiều provider trong cùng một codebase: provider tương thích OpenStack cho VNG Cloud, provider Cloudflare cho DNS/CDN, provider AWS cho S3. Một terraform apply điều phối cả ba.

Bài học: Sức mạnh của Terraform là trừu tượng hoá đa nền tảng — bạn học một cú pháp, áp dụng cho nhiều nhà cung cấp. Điều này đặc biệt giá trị ở Việt Nam, nơi nhiều công ty phải kết hợp cloud nội địa (vì lý do pháp lý, chi phí, độ trễ) với cloud quốc tế.

Hướng dẫn từng bước

Ta sẽ dựng một S3 bucket đơn giản trên AWS để hiểu quy trình.

Bước 1 — Cài đặt và cấu trúc thư mục. Cài Terraform (tải binary hoặc dùng brew install terraform). Tạo một thư mục dự án, thường tách file theo mục đích: main.tf (tài nguyên), variables.tf (khai báo biến), outputs.tf (giá trị đầu ra).

Bước 2 — Khai báo provider. Trong main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" { region = "ap-southeast-1" # Singapore, gần Việt Nam nhất }

Bước 3 — Khai báo resource.

resource "aws_s3_bucket" "backup" {
  bucket = "vietnamcos-backup-2026"

tags = { Environment = "production" ManagedBy = "terraform" } }

Tag ManagedBy = "terraform" là thói quen tốt: sau này nhìn vào console AWS, bạn biết ngay tài nguyên nào do Terraform quản lý, tránh sửa tay gây "drift".

Bước 4 — Dùng biến để linh hoạt. Trong variables.tf:

variable "environment" {
  type    = string
  default = "dev"
}

Rồi tham chiếu bằng var.environment trong resource. Đây là cách bạn tái sử dụng code cho nhiều môi trường.

Bước 5 — Khởi tạo. Chạy terraform init. Terraform tải provider AWS về thư mục .terraform. Chạy một lần cho mỗi dự án (hoặc khi đổi provider).

Bước 6 — Xem trước. Chạy terraform plan. Terraform hiển thị: + aws_s3_bucket.backup will be created. Dấu + nghĩa là tạo mới, ~ là sửa, - là xoá. Luôn đọc kỹ output này. Nếu bạn thấy dấu - ở một database production mà không mong đợi, dừng lại ngay.

Bước 7 — Thực thi. Chạy terraform apply. Terraform hỏi xác nhận yes. Sau đó bucket được tạo thật trên AWS, và trạng thái được ghi vào state.

Bước 8 — Cấu hình remote backend. Ngay khi làm việc nhóm, thêm backend S3:

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

Bước 9 — Dọn dẹp. Với môi trường tạm, terraform destroy xoá sạch mọi tài nguyên, tránh lãng phí chi phí — điều này liên quan chặt đến tối ưu chi phí (chủ đề Bài 35).

Lỗi thường gặp & mẹo

Commit file state vào Git. Lỗi phổ biến nhất. State chứa thông tin nhạy cảm (mật khẩu, IP nội bộ) và gây xung đột khi làm nhóm. Luôn dùng remote backend, thêm .tfstate vào .gitignore.

Sửa tay sau khi đã dùng Terraform (Configuration Drift). Bạn dùng Terraform tạo một server, rồi vài tuần sau vào console sửa tay một security group. Giờ thực tế lệch với code. Lần apply sau, Terraform sẽ "sửa lại" theo code và xoá thay đổi tay của bạn — có thể gây sự cố. Quy tắc vàng: tài nguyên đã do Terraform quản lý thì chỉ sửa qua Terraform.

Không ghim phiên bản provider. Nếu không khai version = "~> 5.0", một ngày đẹp trời provider tự cập nhật lên phiên bản mới có breaking change, plan báo lỗi hàng loạt. Luôn ghim phiên bản.

Áp dụng thẳng lên production không qua plan. Luôn chạy plan, đọc kỹ, tốt hơn nữa là lưu plan (terraform plan -out=tfplan) rồi apply tfplan để đảm bảo cái được duyệt đúng là cái được chạy.

Mẹo hardcode giá trị nhạy cảm. Đừng viết mật khẩu thẳng trong file .tf. Dùng biến, hoặc tốt hơn là lấy từ hệ thống quản lý bí mật như Vault/KMS (chủ đề Bài 33).

Mẹo terraform fmtvalidate. Chạy terraform fmt để format code đồng nhất, terraform validate để bắt lỗi cú pháp trước khi plan. Đưa cả hai vào CI để tự động kiểm tra mỗi Pull Request.

Mẹo module hoá sớm. Ngay khi bạn thấy mình copy-paste một khối cấu hình cho môi trường thứ hai, hãy tách nó thành module. Đừng để codebase phình ra rồi mới refactor.

Bài tập thực hành

  • Dựng và huỷ. Cài Terraform, viết cấu hình tạo một S3 bucket (hoặc một tài nguyên miễn phí trên cloud bạn có tài khoản). Chạy đủ chu trình init → plan → apply → destroy. Quan sát file terraform.tfstate sinh ra và biến mất.
  • Đọc plan như một thám tử. Sau khi apply, vào console sửa tay một tag của tài nguyên. Chạy lại terraform plan. Đọc xem Terraform phát hiện drift thế nào và định làm gì để "sửa lại".
  • Biến hoá đa môi trường. Viết một variable "environment" và dùng nó để đặt tên tài nguyên. Chạy apply với -var="environment=dev" rồi -var="environment=staging". Xác nhận bạn tạo được hai bộ tài nguyên độc lập từ cùng một code.
  • Thiết lập remote backend. Cấu hình backend S3 + DynamoDB lock. Thử chạy apply đồng thời từ hai terminal và quan sát cơ chế khoá hoạt động.
  • Suy ngẫm audit. Viết một đoạn ngắn: nếu công ty bạn bị hỏi "ai đã mở port 3306 (MySQL) ra Internet ba tháng trước", với IaC bạn sẽ trả lời thế nào? So với cách làm ClickOps thì khác biệt ở đâu?

Tóm tắt

Infrastructure as Code biến hạ tầng từ những cú click chuột không lưu vết thành code có thể tái lập, quản lý phiên bản và kiểm toán. Terraform — công cụ IaC phổ biến nhất — dùng phong cách declarative: bạn khai báo trạng thái mong muốn, nó tự tính cách đạt được. Bốn khái niệm cốt lõi cần nắm chắc: provider (kết nối nền tảng), resource (đơn vị hạ tầng), state (bản đồ tài nguyên đang quản lý — quan trọng nhất), và cặp plan/apply (xem trước rồi thực thi).

Quy trình chuẩn: init → plan → apply, và destroy khi dọn dẹp. Ba nguyên tắc sống còn rút ra từ các tình huống thực tế: (1) không bao giờ commit state vào Git — dùng remote backend với state locking khi làm nhóm; (2) đã quản lý bằng Terraform thì đừng sửa tay để tránh drift; (3) luôn đọc kỹ plan trước khi apply. Nắm vững nền tảng này, bạn đã sẵn sàng cho các chủ đề nâng cao như quản lý cấu hình (Bài 37) và GitOps (Bài 40).