Sau bài này bạn làm được: viết Terraform module cơ bản cho Security Group; review PR thay đổi mạng và biết câu hỏi nào cần hỏi; hiểu tại sao terraform plan phải chạy trong CI trước khi merge.
Click-ops và hậu quả
“Click-ops” là cấu hình hạ tầng bằng console cloud (tay chuột):
Problem:
Dev A click thêm SG rule "mở 0.0.0.0/0:5432" vì debug tối qua
→ quên không đóng lại
→ tồn tại 3 tháng
→ không ai biết rule đó từ đâu ra
→ không audit trail
IaC (Infrastructure as Code) giải quyết: mọi thay đổi là code, có review, có version history, có thể rollback.
Terraform vs OpenTofu (2025): sau khi HashiCorp đổi license Terraform sang BSL (8/2023), cộng đồng fork thành OpenTofu (CNCF sandbox, Linux Foundation). Cú pháp HCL tương thích gần như 100%, state file cùng format — nhiều tổ chức đã migrate bằng cách đổi binary
terraform→tofu. Ví dụ trong bài dùngterraformlàm lệnh nhưng hoàn toàn áp dụng được vớitofu plan/tofu apply.
Terraform cơ bản cho mạng
VPC và subnet
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "prod-vpc"
Environment = "production"
}
}
resource "aws_subnet" "public" {
for_each = { "a" = "10.0.1.0/24", "b" = "10.0.101.0/24" }
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = "us-east-1${each.key}"
tags = { Name = "public-${each.key}" }
}
resource "aws_subnet" "private" {
for_each = { "a" = "10.0.2.0/24", "b" = "10.0.102.0/24" }
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = "us-east-1${each.key}"
tags = { Name = "private-${each.key}" }
}
Security Group
resource "aws_security_group" "app" {
name = "app-server"
description = "App server SG"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTPS from internet"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH from bastion only"
from_port = 22
to_port = 22
protocol = "tcp"
security_groups = [aws_security_group.bastion.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { Name = "app-server" }
}
terraform plan — đọc output trước khi apply
terraform plan -out=tfplan
# Output mẫu:
# + aws_security_group_rule.allow_5432 ← sẽ THÊM rule mới
# ~ aws_lb_listener.https ← sẽ THAY ĐỔI (in-place)
# - aws_subnet.old_private ← sẽ XÓA
# -/+ aws_instance.app ← sẽ DESTROY và TẠO LẠI (!)
Dấu hiệu cần cẩn thận trong plan:
-/+destroy và recreate → downtime nếu không có replica.-delete subnet/SG → có thể ảnh hưởng resource khác tham chiếu.~thay đổi SG rule → có thể cắt traffic đang chạy.
GitOps workflow cho thay đổi mạng
[Developer] ──PR: "Thêm rule mở cổng 8443 cho service X"──▶ [GitHub/GitLab]
│
▼
[CI Pipeline chạy:]
1. terraform fmt (format check)
2. terraform validate
3. terraform plan → post output lên PR
│
▼
[Reviewer đọc plan output]
[Approve hoặc request changes]
│
▼
[Merge → CI apply]
Checklist review PR thay đổi mạng:
□ Rule mở cổng nào? Từ source nào? Đã minimize privilege chưa?
□ Có rule nào bị XÓA không? Ai đang dùng rule đó?
□ Có subnet/SG nào bị thay đổi gây destroy resource không?
□ Thay đổi có ảnh hưởng production ngay lập tức không?
□ Nếu rollback, làm thế nào?
□ Có thay đổi nào tương tự ở staging/test chưa?
Quản lý Kubernetes manifest bằng GitOps
Kubernetes resource (Ingress, NetworkPolicy, Service) cũng là YAML — áp dụng GitOps:
[Git repo]
└── k8s/
├── network-policy.yaml
├── ingress.yaml
└── service.yaml
[ArgoCD / Flux]
└── watch repo → sync vào cluster khi có thay đổi
Review flow:
PR thay đổi NetworkPolicy → CI validate YAML
→ dry-run apply (--dry-run=server)
→ reviewer approve
→ merge → ArgoCD sync tự động
Kiểm tra manifest trước khi apply:
# Dry run trên cluster (validate với API server)
kubectl apply --dry-run=server -f network-policy.yaml
# Diff so với trạng thái hiện tại
kubectl diff -f network-policy.yaml
Giữ IaC và reality đồng bộ
Nguy cơ lớn nhất của IaC: drift — ai đó vào console sửa tay, IaC không biết.
Phát hiện drift:
# Terraform tự detect drift khi plan
terraform plan
# "Note: Objects have changed outside of Terraform"
Prevent drift:
- Khóa console access bằng IAM policy (chỉ cho read, không cho write resource mạng).
- Chạy
terraform plantheo schedule (ví dụ mỗi ngày) và alert nếu có diff. - Dùng Sentinel / OPA để enforce policy “resource phải có tag Environment”.
Tóm tắt
- IaC = audit trail + reproducibility + review — click-ops không có điều này.
terraform planoutput phải được review trước khi apply vào production.- GitOps: PR → CI plan → review → merge → auto-apply.
- Drift là rủi ro thường trực — chạy plan thường xuyên và alert khi có drift.
Câu hỏi hay gặp
plan hiện -/+ EC2 khi đổi ami — nguy hiểm gì nếu không có replica?
Trả lời: -/+ là destroy + create — downtime, đổi IP private (nếu không giữ), mất ephemeral disk. Cần blue/green, ASG rolling, hoặc replace one-by-one.
Apply tay trên máy + sửa console — drift nhiều resource: vì sao, hệ quả?
Trả lời: State Terraform không khớp thực tế; ai đó đã đổi ngoài code. Hệ quả: plan/apply có thể xóa nhầm hoặc không ai tin được infra.
Bắt buộc tag Owner trên mọi SG — dùng gì trong Terraform ecosystem?
Trả lời: Default tags (provider), validation trong module, hoặc policy-as-code (Sentinel, OPA/Terraform Cloud) để chặn apply nếu thiếu tag.
Bài tiếp theo (Giai đoạn IV): Release và incident mạng — thực hành deploy không gây 502, và checklist khi incident xảy ra.