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
flowchart TB
PR[Developer tạo PR<br/>Thêm rule mở cổng 8443 cho service X] --> CI[CI Pipeline chạy<br/>1. terraform fmt<br/>2. terraform validate<br/>3. terraform plan → post output lên PR]
CI --> Review[Reviewer đọc plan output]
Review --> Merge[Merge → CI apply]
Merge --> Drift[Hạ tầng khớp trạng thái đã merge<br/>theo dõi drift; quay lại bước 1 nếu cần chỉnh]
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”.
Checklist review thay đổi mạng trong PR
Khi review PR Terraform/OpenTofu cho network, mình không chỉ nhìn code đẹp hay không. Mình tìm mấy tín hiệu rủi ro này:
- Có mở
0.0.0.0/0hoặc::/0cho port không phải HTTP/HTTPS không? - Route table có đổi default route không?
- Security Group/NACL có xóa rule đang phục vụ traffic production không?
- NAT Gateway, Load Balancer, Transit Gateway có tăng chi phí cố định không?
- Có plan rollback nếu apply xong mất kết nối không?
Một đoạn plan đáng sợ thường không dài. Chỉ cần thấy -/+ aws_route_table hoặc destroy một security group đang attached là nên dừng lại đọc kỹ. Với network, “apply rồi sửa sau” là câu mở đầu của nhiều incident rất mất ngủ.
Điều cần giữ khi debug mạng
- 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.