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
相关产品推荐
相关产品推荐

