You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform部署EKS节点组遇节点注册失败、循环依赖报错

Terraform部署私有EKS节点组循环依赖报错排查

问题现象

部署过程先后触发两个报错:

  • 初始部署报错:
NodeCreationFailure: Instances failed to 
join the kubernetes cluster

排查确认是私有子网标签配置有误,参考公开配置示例调整VPC和子网标签规则后,触发第二个报错:

Error: Cycle: aws_eks_cluster.eks, aws_subnet.private_subnet

部署目标为在私有子网内部署完整EKS集群与工作节点组,问题相关的Terraform配置如下。

相关配置片段

VPC网络配置

resource "aws_vpc" "vpc" {
  cidr_block = "10.1.0.0/16"

  tags = {
    "kubernetes.io/cluster/${var.cluster_name}" = "shared"
  }

}
resource "aws_subnet" "public_subnet" {
  count                   = length(var.azs)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = var.public_cidrs[count.index]
  availability_zone       = var.azs[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.name}-public-subnet-${count.index + 1}"
  }
}

resource "aws_subnet" "private_subnet" {
  count                   = length(var.azs)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = var.private_cidrs[count.index]
  availability_zone       = var.azs[count.index]
  map_public_ip_on_launch = false

  tags = {
    "kubernetes.io/cluster/${aws_eks_cluster.eks.name}" = "shared"
    "kubernetes.io/role/internal-elb"           = "1"
  }
}

resource "aws_internet_gateway" "internet_gateway" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${var.name}-internet-gateway"
  }
}

resource "aws_route_table" "public_rt" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${var.name}-public-rt"
  }
}

resource "aws_route" "default_route" {
  route_table_id         = aws_route_table.public_rt.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.internet_gateway.id
}

resource "aws_route_table_association" "public_assoc" {
  count          = length(var.public_cidrs)
  subnet_id      = aws_subnet.public_subnet[count.index].id
  route_table_id = aws_route_table.public_rt.id
}

resource "aws_eip" "nat_eip" {
  count      = length(var.public_cidrs)
  vpc        = true
  depends_on = [aws_internet_gateway.internet_gateway]

  tags = {
    Name = "${var.name}-nat-eip-${count.index + 1}"
  }
}

resource "aws_nat_gateway" "nat_gateway" {
  count         = length(var.public_cidrs)
  allocation_id = aws_eip.nat_eip[count.index].id
  subnet_id     = aws_subnet.public_subnet[count.index].id
  depends_on    = [aws_internet_gateway.internet_gateway]

  tags = {
    Name = "${var.name}-NAT-gateway-${count.index + 1}"
  }
}

EKS集群与节点组配置

resource "aws_iam_role" "eks_cluster" {
  name = "${var.name}-eks-cluster-role"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "eks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "amazon_eks_cluster_policy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
  role       = aws_iam_role.eks_cluster.name
}

resource "aws_eks_cluster" "eks" {
  name     = var.cluster_name
  role_arn = aws_iam_role.eks_cluster.arn

  version = var.k8s_version

  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = false
    subnet_ids              = [
        aws_subnet.private_subnet[0].id,
        aws_subnet.private_subnet[1].id,
        aws_subnet.private_subnet[2].id,
    ]
  }
  depends_on = [
    aws_iam_role_policy_attachment.amazon_eks_cluster_policy
  ]
}

resource "aws_iam_role" "nodes_eks" {
  name               = "role-node-group-eks"
  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      }, 
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "amazon_eks_worker_node_policy_eks" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
  role       = aws_iam_role.nodes_eks.name
}

resource "aws_iam_role_policy_attachment" "amazon_eks_cni_policy_eks" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
  role       = aws_iam_role.nodes_eks.name
}

resource "aws_iam_role_policy_attachment" "amazon_ec2_container_registry_read_only" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
  role       = aws_iam_role.nodes_eks.name
}


resource "aws_eks_node_group" "nodes_eks" {
  cluster_name    = aws_eks_cluster.eks.name
  node_group_name = "${var.name}-node-group"
  node_role_arn   = aws_iam_role.nodes_eks.arn
  subnet_ids      = [
        aws_subnet.private_subnet[0].id,
        aws_subnet.private_subnet[1].id,
        aws_subnet.private_subnet[2].id,
  ]
  remote_access {
    ec2_ssh_key = aws_key_pair.bastion_auth.id

  }

  scaling_config {
    desired_size = 3
    max_size     = 6
    min_size     = 3
  }

  ami_type       = "AL2_x86_64"
  capacity_type  = "ON_DEMAND"
  disk_size      = 20
  instance_types = [var.instance_type]
  labels = {
    role = "nodes-group-1"
  }

  version = var.k8s_version

  depends_on = [
    aws_iam_role_policy_attachment.amazon_eks_worker_node_policy_eks,
    aws_iam_role_policy_attachment.amazon_eks_cni_policy_eks,
    aws_iam_role_policy_attachment.amazon_ec2_container_registry_read_only,
  ]
}

根因分析

循环依赖报错由资源双向引用形成闭环导致:

  • aws_eks_cluster.eks的vpc_config.subnet_ids字段引用了私有子网的资源ID,Terraform必须先完成私有子网创建,才能开始创建EKS集群
  • 但私有子网资源的标签键中引用了aws_eks_cluster.eks.name,Terraform必须先完成EKS集群创建拿到集群名称,才能完成私有子网的标签配置
    两个资源互相等待对方先创建,形成死锁,直接触发Cycle类型错误。

修复方法

私有子网的集群标签不需要引用EKS集群资源的属性,直接使用预先定义的var.cluster_name变量即可,和VPC资源上的集群标签写法保持一致。
将私有子网资源的tags块修改为:

tags = {
  "kubernetes.io/cluster/${var.cluster_name}" = "shared"
  "kubernetes.io/role/internal-elb"           = "1"
}

修改后资源依赖链路变为:创建VPC→创建私有子网→创建EKS集群→创建节点组,循环依赖自动解除。

补充说明:最初的节点无法加入集群报错,核心原因就是子网缺少正确的EKS集群归属标签,标签修复后只要保证私有子网关联的路由表配置了指向NAT网关的默认路由,节点可以正常访问EKS服务端点,节点即可顺利完成集群注册。

内容的提问来源于stack exchange,提问作者MP32

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:54:19