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

私有子网ECS Fargate无法拉取ECR镜像问题求助

问题描述

我是Terraform IaC和AWS的新手,正尝试将JMeter的Docker镜像推送到ECR仓库,让ECS中的Fargate实例使用该镜像。出于安全策略要求,Fargate实例必须部署在私有子网,需通过NAT Gateway访问互联网。我已通过部署脚本将镜像推送到正确位置,且反复检查了ECR、执行角色的IAM权限和网络配置,但任务执行时总是启动后随即停止,持续出现如下错误:

CannotPullContainerError: Error response from daemon: Get "https://xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

部署用Terraform代码

provider "aws" {
  region = var.aws_region
}
# VPC
resource "aws_vpc" "jmeter_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_support = true
  enable_dns_hostnames = true
}

resource "aws_subnet" "jmeter_public_subnet" {
  vpc_id            = aws_vpc.jmeter_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "${var.aws_region}a"
  depends_on = [
    aws_vpc.jmeter_vpc
  ]
}

resource "aws_subnet" "jmeter_private_subnet" {
  vpc_id            = aws_vpc.jmeter_vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "${var.aws_region}a"
  map_public_ip_on_launch = false

  depends_on = [
    aws_vpc.jmeter_vpc
  ]
}

resource "aws_eip" "nat_gateway_eip" {
  vpc = true
  tags = {
    name = "jmeter-eip"
  }
}

# Internet Gateway for the public subnet
resource "aws_internet_gateway" "jmeter_ig" {
  vpc_id = aws_vpc.jmeter_vpc.id
  depends_on = [
    aws_vpc.jmeter_vpc
  ]
}

# NAT Gateway for access to API from JMeter Instance
resource "aws_nat_gateway" "jmeter_nat_gateway" {
  allocation_id = aws_eip.nat_gateway_eip.id
  subnet_id = aws_subnet.jmeter_public_subnet.id
  depends_on = [
    aws_internet_gateway.jmeter_ig,
    aws_eip.nat_gateway_eip

  ]
}

# Route Tables
resource "aws_route_table" "jmeter_public_rt" {
  vpc_id = aws_vpc.jmeter_vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.jmeter_ig.id
  }
  depends_on = [
    aws_vpc.jmeter_vpc
  ]

}

resource "aws_route_table" "jmeter_private_rt" {
  vpc_id = aws_vpc.jmeter_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.jmeter_nat_gateway.id
  }
  depends_on = [
    aws_nat_gateway.jmeter_nat_gateway
  ]
}


#Route Table Associations
resource "aws_route_table_association" "jmeter_public_rt_association" {
  subnet_id = aws_subnet.jmeter_public_subnet.id
  route_table_id = aws_route_table.jmeter_public_rt.id
  depends_on = [
    aws_route_table.jmeter_public_rt,
    aws_subnet.jmeter_public_subnet
  ]
}

resource "aws_route_table_association" "jmeter_private_rt_association" {
  subnet_id = aws_subnet.jmeter_private_subnet.id
  route_table_id = aws_route_table.jmeter_private_rt.id
  depends_on = [
    aws_route_table.jmeter_private_rt,
    aws_subnet.jmeter_private_subnet
  ]
}


#Keep this traffic unrestricted for now, cull later
resource "aws_security_group" "jmeter_security_group" {
  vpc_id      = aws_vpc.jmeter_vpc.id

  ingress {
    cidr_blocks = ["0.0.0.0/0"]
    from_port = 0
    to_port   = 0
    protocol = "-1"
  }
  #ingress {
  #  from_port   = 80
  #  to_port     = 80
  #  protocol    = "tcp"
  #  cidr_blocks = ["0.0.0.0/0"]
  #}

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

  depends_on = [
    aws_vpc.jmeter_vpc
  ]
}

# ECS Cluster for image
resource "aws_ecs_cluster" "jmeter_cluster" {
  name = "jmeter-cluster"

  setting {
    name = "containerInsights"
    value = "enabled"
  }

  configuration {
    execute_command_configuration {
      logging = "OVERRIDE"
      log_configuration {
        cloud_watch_log_group_name = aws_cloudwatch_log_group.jmeter_log_group.name
        s3_bucket_name             = aws_s3_bucket.jmeter_s3.bucket
      }
    }
  }
}


# Create ECR Repository for JMeter Image
resource "aws_ecr_repository" "jmeter_ecr" {
  name = var.ecr_repository_name
  force_delete = true
}

data "aws_ecr_repository" "jmeter_ecr_data" {
  name = aws_ecr_repository.jmeter_ecr.name
  depends_on = [
    aws_ecr_repository.jmeter_ecr
  ]
}


# Task definition for JMeter instance, network configuration provided by script
resource "aws_ecs_task_definition" "jmeter_task_def" {
  family = "jmeter-task"
  cpu = 1024
  memory = 4096
  container_definitions = jsonencode([
    {
      name = "jmeter_task"
      image = "${aws_ecr_repository.jmeter_ecr.repository_url}:latest"
      log_configuration = {
        log_driver = "awslogs",
        options = {
          "awslogs-group" = aws_cloudwatch_log_group.jmeter_log_group.name
          "awslogs-region" = var.aws_region
        }
      }
    },
  ])
  requires_compatibilities = ["FARGATE"]
  network_mode = "awsvpc"

  execution_role_arn = aws_iam_role.fargate_execution.arn
  task_role_arn = aws_iam_role.fargate_task.arn
  depends_on = [
    aws_cloudwatch_log_group.jmeter_log_group,
    aws_iam_role.fargate_execution,
    data.aws_ecr_repository.jmeter_ecr_data
  ]
}

# ECS Service for this instance
resource "aws_ecs_service" "jmeter_service" {
  name            = "jmeter-service"
  cluster         = aws_ecs_cluster.jmeter_cluster.id
  task_definition = aws_ecs_task_definition.jmeter_task_def.arn
  launch_type = "FARGATE"
  platform_version = "1.3.0"
  desired_count   = 1

  network_configuration {
    security_groups = [aws_security_group.jmeter_security_group.id]
    subnets         = [aws_subnet.jmeter_private_subnet.id]
  }

  deployment_controller {
    type = "ECS"
  }

  depends_on = [
    aws_ecs_cluster.jmeter_cluster,
    aws_ecs_task_definition.jmeter_task_def,
    aws_security_group.jmeter_security_group,
    aws_subnet.jmeter_private_subnet
  ]
}


data "aws_iam_policy_document" "fargate-role-policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ecs.amazonaws.com", "ecs-tasks.amazonaws.com"]
    }
  }
}
resource "aws_iam_policy" "fargate_execution" {
  name   = "fargate_execution_policy"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage",
            "ecr:BatchCheckLayerAvailability"
        ],
        "Resource": "*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "ecr:GetAuthorizationToken"
        ],
        "Resource": "*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream"
        ],
        "Resource": "*"
    }
  ]
}
EOF
depends_on = [
  aws_ecr_repository.jmeter_ecr
]
}

resource "aws_iam_policy" "fargate_task" {
  name   = "fargate_task_policy"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
        ],
        "Resource": "*"
    }
  ]
}
EOF
}
resource "aws_iam_role" "fargate_execution" {
  name               = "fargate_execution_role"
  assume_role_policy = data.aws_iam_policy_document.fargate-role-policy.json
}
resource "aws_iam_role" "fargate_task" {
  name               = "fargate_task_role"
  assume_role_policy = data.aws_iam_policy_document.fargate-role-policy.json
}
resource "aws_iam_role_policy_attachment" "fargate-execution" {
  role       = aws_iam_role.fargate_execution.name
  policy_arn = aws_iam_policy.fargate_execution.arn
}
resource "aws_iam_role_policy_attachment" "fargate-task" {
  role       = aws_iam_role.fargate_task.name
  policy_arn = aws_iam_policy.fargate_task.arn
}
排查与修复方案

1. 验证NAT Gateway及路由关联有效性

错误核心是私有子网内Fargate任务无法通过NAT Gateway访问互联网,需确认:

  • 登录AWS控制台,检查NAT Gateway状态是否为available,若处于pending状态需等待资源就绪。
  • 确认私有子网的路由表已成功关联jmeter_private_rt,且路由表中0.0.0.0/0确实指向NAT Gateway ID。
  • 检查公有子网的路由表是否正确关联Internet Gateway,确保NAT Gateway自身能访问互联网。

2. 补充执行角色日志权限

当前执行角色策略缺少logs:PutLogEvents权限,会导致Fargate无法上传容器启动日志,也可能间接影响镜像拉取流程。修改aws_iam_policy.fargate_execution的policy内容:

{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage",
            "ecr:BatchCheckLayerAvailability"
        ],
        "Resource": "*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "ecr:GetAuthorizationToken"
        ],
        "Resource": "*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
        ],
        "Resource": "*"
    }
  ]
}

3. 完善ECS服务依赖关系

当前ECS服务启动时可能NAT Gateway或路由关联尚未就绪,需添加相关依赖:

resource "aws_ecs_service" "jmeter_service" {
  # 其他配置保持不变
  depends_on = [
    aws_ecs_cluster.jmeter_cluster,
    aws_ecs_task_definition.jmeter_task_def,
    aws_security_group.jmeter_security_group,
    aws_subnet.jmeter_private_subnet,
    aws_nat_gateway.jmeter_nat_gateway,
    aws_route_table_association.jmeter_private_rt_association
  ]
}

4. 验证ECR镜像及DNS解析

  • 确认ECR仓库中latest标签的镜像存在,可通过AWS CLI执行aws ecr list-images --repository-name <你的仓库名>验证。
  • 启动测试Fargate任务(使用amazonlinux镜像),执行nslookup <你的ECR域名>验证私有子网内DNS解析是否正常。

5. 检查VPC DNS配置

虽然VPC已开启enable_dns_support和enable_dns_hostnames,但需确认私有子网内的Fargate任务能获取到DNS服务器地址(AWS默认VPC DNS为VPC网段+2)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:07:01