Sự cố môi trường Production lúc 2 giờ sáng
Bạn đang gỡ bỏ (decommission) một môi trường staging, mong đợi một kết quả nhanh chóng. Thay vào đó, Terraform bị treo trong 180 giây trước khi gặp lỗi DependencyViolation. Đầu ra của terminal xác nhận cơn ác mộng của bạn: một Security Group hoặc Subnet bị kẹt vì có thứ gì đó khác vẫn đang sử dụng nó.
Error: Error deleting Security Group: DependencyViolation: The security group 'sg-0a1b2c3d4e5f6g7h8' has dependencies and cannot be deleted.
status code: 400, request id: 12345678-90ab-cdef-1234-567890abcdef
Vấn đề bắt nguồn từ sự sai lệch giữa state file của Terraform và thực tế trên cloud console của bạn. Terraform tin rằng nó quản lý vòng đời (lifecycle) của tài nguyên. Tuy nhiên, một thực thể bên ngoài—chẳng hạn như một Network Interface (ENI) được gắn thủ công, một Lambda function còn sót lại, hoặc một Load Balancer—vẫn đang bám chặt lấy tài nguyên đó. Cloud API từ chối yêu cầu xóa để ngăn chặn việc gián đoạn mạng.
Bước 1: Xác định phụ thuộc ẩn (Ghost Dependency)
Terraform cho bạn biết nó không thể xóa cái gì, nhưng lại cực kỳ kín tiếng về lý do "tại sao". Khi một AWS Security Group bị kẹt, một Network Interface (ENI) ẩn thường là thủ phạm. Điều này thường xuyên xảy ra với Elastic Load Balancers (ELB) hoặc các node EKS không được tắt ngay lập tức.
Sử dụng AWS CLI để tìm liên kết cụ thể đang giữ tài nguyên của bạn làm con tin:
# Tìm các ENI được liên kết với Security Group bị kẹt
aws ec2 describe-network-interfaces \
--filters Name=group-id,Values=sg-0a1b2c3d4e5f6g7h8 \
--query 'NetworkInterfaces[*].{ID:NetworkInterfaceId,Description:Description,Status:Status}'
Nếu lệnh này trả về một ID đang hoạt động, bạn đã tìm thấy "bóng ma" của mình. Các ENI của AWS Lambda nổi tiếng với điều này. Ngay cả sau khi một function đã bị xóa, network interface có thể tồn tại trong vài phút trước khi VPC thu hồi nó. Terraform không thể thấy các liên kết thủ công hoặc do hệ thống quản lý này vì chúng không nằm trong file .tfstate của bạn.
Bước 2: Các giải pháp thực tế
Phương pháp A: Khắc phục bằng Lifecycle (Chủ động)
Trong quá trình apply, Terraform thường cố gắng xóa một tài nguyên cũ trước khi tạo tài nguyên thay thế. Nếu các tài nguyên khác vẫn trỏ đến ID cũ, quá trình này sẽ thất bại. Bạn có thể đảo ngược logic này bằng một khối lifecycle.
resource "aws_security_group" "web_server" {
name = "web-sg-v2"
lifecycle {
create_before_destroy = true
}
}
Khối này buộc Terraform phải khởi tạo Security Group mới trước. Sau đó, nó cập nhật tất cả các tài nguyên phụ thuộc để trỏ sang ID mới. Chỉ sau khi tài nguyên cũ thực sự "đứng một mình", Terraform mới cố gắng xóa nó. Đây là cách hiệu quả nhất để ngăn chặn các vòng lặp phụ thuộc (dependency loops) trong code hạ tầng mạng.
Phương pháp B: Can thiệp thủ công (Phản ứng)
Đôi khi bạn chỉ cần dọn đường một cách thủ công. Điều này thường thấy khi một cụm Kubernetes (EKS) tạo ra các Load Balancer mà Terraform không theo dõi.
- Xác định vị trí và ngắt kết nối: Sử dụng ID từ Bước 1 để tìm tài nguyên trong AWS Console. Xóa ENI hoặc Load Balancer một cách thủ công.
- Đồng bộ hóa State: Sau khi chướng ngại vật đã biến mất, hãy chạy
terraform plan -refresh-onlyđể cập nhật state cục bộ của bạn. - Hoàn tất công việc: Chạy lại
terraform destroy. Bây giờ nó sẽ hoàn thành ngay lập tức.
Phương pháp C: Buộc State phải "quên" tài nguyên
Nếu bạn đã xóa tài nguyên thông qua cloud console nhưng Terraform vẫn tiếp tục cố gắng hủy nó, bạn cần thực hiện một thủ thuật "cắt bỏ" trên state file của mình. Hãy sử dụng lệnh state rm.
terraform state rm aws_security_group.web_server
Lưu ý: Việc này chỉ loại bỏ bản ghi theo dõi. Nó không xóa hạ tầng thực tế. Chỉ sử dụng lệnh này khi tài nguyên vật lý đã biến mất hoặc nếu bạn có ý định quản lý nó một cách thủ công.
Bước 3: Xác minh
Xác nhận việc khắc phục bằng cách chạy plan. Nếu bạn đã triển khai create_before_destroy, đầu ra sẽ trông như thế này:
# aws_security_group.web_server must be replaced
# +/- create replacement and then destroy
Một lần chạy thành công sẽ kết thúc với thông báo đầy thỏa mãn: Destroy complete! Resources: 1 destroyed.
Những bài học cho tương lai
- Tránh "ClickOps": Việc gắn thủ công một Security Group vào một instance EC2 trong console là cách chắc chắn nhất để làm hỏng lần chạy Terraform tiếp theo của bạn.
- Tính đến độ trễ của Lambda: Các Lambda hỗ trợ VPC có thể mất tới 20 phút để giải phóng hoàn toàn ENI trong một số trường hợp hiếm hoi. Hãy xây dựng logic thử lại (retry logic) vào các pipeline CI/CD của bạn nếu điều này xảy ra thường xuyên.
- Tiêu chuẩn hóa các quy tắc Lifecycle: Áp dụng
create_before_destroycho các thành phần nền tảng như Subnets và VPC Peering connections theo mặc định. - Cảnh giác với các "sát thủ thầm lặng": NAT Gateways và VPC Endpoints thường có thời gian dọn dẹp dài, gây cản trở việc xóa VPC cha.

