私有子网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

