ST
ShowTech VN
🏠 Trang Chủ
📖 Về ShowTech 📬 Liên Hệ

[Bài 04] Làm Chủ Dependency Graph (DAG): Quản Lý Phụ Thuộc Tường Minh vs Ngầm Định & Data Sources

[Bài 04] Làm Chủ Dependency Graph (DAG): Quản Lý Phụ Thuộc Tường Minh vs Ngầm Định & Data Sources

Trong một hệ thống hạ tầng đám mây phức tạp, việc các tài nguyên phụ thuộc lẫn nhau là điều tất yếu: EC2 Instance cần đặt trong Subnet, Subnet cần thuộc về VPC, Security Group Rule cần ID của Security Group, và Kubernetes Cluster cần IAM Role trước khi khởi tạo worker nodes.

Terraform giải quyết bài toán này một cách kỳ diệu thông qua Đồ thị không tuần hoàn có hướng (Directed Acyclic Graph - DAG). Tuy nhiên, việc lạm dụng hoặc hiểu sai cơ chế phụ thuộc (đặc biệt là việc lạm dụng từ khóa depends_on hoặc sử dụng sai data source) có thể phá hủy khả năng thực thi song song, gây ra thắt cổ chai hiệu năng hoặc tạo ra lỗi Cyclic Dependency chết người.

Bài viết này sẽ giúp bạn hiểu tường tận cách Terraform dựng DAG, phân biệt chính xác giữa Phụ thuộc ngầm định (Implicit)Phụ thuộc tường minh (Explicit), cùng chiến lược truy vấn tài nguyên thực tế bằng Data Sources.


1. Phân Biệt Implicit vs Explicit Dependencies

1.1. Phụ Thuộc Ngầm Định (Implicit Dependency) — Lựa Chọn Khuyến Nghị Số 1

Xảy ra khi một tài nguyên này tham chiếu trực tiếp đến thuộc tính đầu ra (Exported Attribute) của một tài nguyên khác:

HCL
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "web" {
  # Tham chiếu trực tiếp ID của VPC -> Tạo Implicit Dependency
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"
}

Lợi ích:
- Terraform Core tự động phân tích cây phụ thuộc và biết chính xác aws_vpc.main phải được tạo trước aws_subnet.web.
- Khi hủy hạ tầng (destroy), Terraform tự động đảo ngược thứ tự: Xóa Subnet trước, sau đó mới xóa VPC.


1.2. Phụ Thuộc Tường Minh (Explicit Dependency với depends_on)

Được sử dụng khi hai tài nguyên có quan hệ phụ thuộc logic trong thực tế nhưng không có tham chiếu thuộc tính trực tiếp trong code:

HCL
resource "aws_iam_role_policy_attachment" "eks_node_policy" {
  role       = aws_iam_role.node_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}

resource "aws_eks_node_group" "workers" {
  cluster_name    = aws_eks_cluster.main.name
  node_group_name = "showtech-prod-workers"
  node_role_arn   = aws_iam_role.node_role.arn
  subnet_ids      = [aws_subnet.private_a.id, aws_subnet.private_b.id]

  # Bắt buộc EKS Node Group chỉ được tạo sau khi Policy đã gắn xong vào Role
  depends_on = [
    aws_iam_role_policy_attachment.eks_node_policy
  ]
}

2. Làm Chủ Data Sources: Cầu Nối Giữa Code và Thế Giới Thực

Data Sources cho phép Terraform truy vấn thông tin của các tài nguyên đã tồn tại sẵn (được tạo bởi hệ thống khác hoặc bởi một Terraform Workspace khác) mà không cần quản lý vòng đời của tài nguyên đó.

2.1. Tra Cứu Dynamic AMI Chuẩn Bảo Mật

Thay vì hardcode AMI ID (vốn thay đổi theo từng Region và thường xuyên lỗi thời), hãy dùng Data Source để tự động lấy Ubuntu AMI mới nhất đã được vá bảo mật:

HCL
data "aws_ami" "ubuntu_lts" {
  most_recent = true
  owners      = ["099720109477"] # Canonical Official Owner ID

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

3. Kiến Trúc Hoàn Chỉnh: Multi-Tier VPC & Bastion Host Triển Khai Thực Chiến

Dưới đây là mã nguồn kết hợp chuẩn xác giữa Data Sources, Implicit Dependencies và Resource References:

HCL
# main-infra.tf - Kiến trúc phân tầng mạng và máy chủ nhảy (Bastion Host)
terraform {
  required_version = ">= 1.7.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.40.0"
    }
  }
}

# 1. Data Source lấy danh sách Availability Zones khả dụng trong Region
data "aws_availability_zones" "available" {
  state = "available"
}

# 2. Khởi tạo VPC gốc
resource "aws_vpc" "core_vpc" {
  cidr_block           = "172.20.0.0/16"
  enable_dns_hostnames = true

  tags = {
    Name = "showtech-core-vpc"
  }
}

# 3. Subnet Public (Implicit Dependency vào Core VPC & Data Source AZ)
resource "aws_subnet" "public_zone_a" {
  vpc_id            = aws_vpc.core_vpc.id
  cidr_block        = "172.20.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "showtech-public-1a"
  }
}

# 4. Security Group cho Bastion Host
resource "aws_security_group" "bastion_sg" {
  name        = "showtech-bastion-sg"
  description = "Cho phep truy cap SSH bao mat tu IP quan tri"
  vpc_id      = aws_vpc.core_vpc.id

  ingress {
    description = "SSH tu Van phong ShowTech"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["1.55.120.88/32"] # IP Tinh co dinh
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# 5. Bastion Host kết hợp Data Source AMI + Subnet ID + SG ID
resource "aws_instance" "bastion" {
  ami                    = data.aws_ami.ubuntu_lts.id
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.public_zone_a.id
  vpc_security_group_ids = [aws_security_group.bastion_sg.id]

  root_block_device {
    volume_size           = 20
    volume_type           = "gp3"
    encrypted             = true
    delete_on_termination = true
  }

  tags = {
    Name = "showtech-bastion-gateway"
    Role = "Security"
  }
}

4. Production War Story: Tháo Gỡ Sự Cố "Cycle Dependencies" Giữa Security Groups

ℹ️ Lưu Ý / Tình Huống

Sự cố kinh điển: Nhóm phát triển cấu hình quan hệ tương hỗ: Security Group của Web App (sg-web) cần cho phép giao tiếp với Security Group của Database (sg-db), và ngược lại sg-db chỉ nhận kết nối từ sg-web. Khi chạy terraform apply, Terraform lập tức ném lỗi Crash:

TEXT
Error: Cycle: aws_security_group.sg_web, aws_security_group.sg_db

Cách Khắc Phục Chuẩn Enterprise:

Tuyệt đối không định nghĩa ingress/egress trực tiếp bên trong block aws_security_group. Thay vào đó, khởi tạo vỏ Security Group rỗng trước, sau đó dùng tài nguyên rời aws_security_group_rule để liên kết. Bằng cách này, đồ thị DAG hoàn toàn thông suốt mà không còn chu trình lặp.


5. Câu Hỏi Phỏng Vấn Kỹ Sư Cao Cấp (Senior Terraform Q&A)

Q1: Khi nào Data Source được đánh giá (evaluated) trong vòng đời Terraform? Có sự khác biệt nào giữa việc Data Source phụ thuộc vào Resource tĩnh vs Resource vừa mới tạo trong cùng 1 lần Apply?

Trả lời:
- Nếu Data Source có các đối số đầu vào (arguments) cố định đã biết trước (Static), Terraform sẽ truy vấn dữ liệu ngay trong giai đoạn terraform plan (Refresh phase).
- Nếu Data Source phụ thuộc vào thuộc tính của một Resource chưa được tạo (chỉ có sau khi Apply, ví dụ: ID của một VPC mới), Data Source sẽ bị hoãn lại (deferred evaluation) cho đến tận pha apply. Điều này có thể khiến các Resource phía sau phụ thuộc vào Data Source đó hiển thị (known after apply) trong bản Plan.

Q2: Làm thế nào để xuất dữ liệu đồ thị DAG của Terraform ra định dạng trực quan để kiểm toán kiến trúc (Architecture Audit)?

Trả lời:
Terraform hỗ trợ lệnh xuất đồ thị dạng chuẩn Graphviz DOT:

BASH
terraform graph | dot -Tpng > terraform-graph.png

Lệnh này giúp kỹ sư trực quan hóa toàn bộ các nút phụ thuộc, phát hiện các điểm nghẽn cổ chai hoặc quan hệ thừa thãi trong codebase.


6. Tổng Kết

Hiểu rõ đồ thị DAG và cách vận hành của Implicit/Explicit Dependencies giúp bạn thiết kế kiến trúc hạ tầng linh hoạt, tối ưu hóa tốc độ thực thi song song và tránh hoàn toàn các lỗi xung đột phụ thuộc.

Trong bài viết tiếp theo (Bài 05), chúng ta sẽ hoàn thiện giai đoạn 1 với chuyên đề Thiết Kế Variables, Locals và Outputs Chuẩn Enterprise Trong Terraform.


Bài viết thuộc Series đào tạo Terraform Thực Chiến độc quyền trên ShowTech.vn.

ShowTech Author

ShowTech Admin (ShowTech Team)

Cloud Architect & Senior DevOps Engineer

Đam mê xây dựng hệ thống phần mềm hiệu năng cao, phân tán quy mô lớn và chia sẻ tri thức công nghệ thực chiến chuẩn quốc tế cho cộng đồng kỹ sư Việt Nam.

Tìm kiếm Blog này

Bài đăng phổ biến từ blog này

Tối Ưu PostgreSQL Chịu Tải Hàng Triệu Queries: Indexing, Connection Pooling & Partitioning

Kiến Trúc Microservices Chịu Tải 1 Triệu CCU Thực Chiến

Tối Ưu Memory Footprint & Goroutine Pooling Trong High-Throughput Go Services

Đã sao chép liên kết vào clipboard!