CannotPullContainerError:拉取映像清单已重试 5 次:无法解析引用 public.ecr.aws/j0m1s7l0/la-client-api:latest

问题描述 投票:0回答:1

我正在尝试使用 terraform 部署一个用于 Rest api 的容器以及一个 postgres 容器。 ECS 任务定义指定可用的容器映像是公共 ECR 存储库。当我应用以下 terrafrom 时,我收到错误:

CannotPullContainerError: pull image manifest has been retried 5 time(s): failed to resolve ref public.ecr.aws/j0m1s7l0/la-client-api:latest: failed to do request: Head "https://public.ecr.aws/v2/j0m1s7l0/la-client-api/manifests/latest": dial tcp 99.83.145.10:443: i/o timeout

如果我访问 public.ecr.aws/j0m1s7l0/la-client-api:latest 我收到错误:

{"errors":[{"code":"DENIED","message":"Not Authorized"}]}
。现在我不确定这是否是存储库无法公开访问的问题,或者ECS服务是否由于网络设置而无法拉取镜像。我添加了NAT网关,因为ECS服务的子网是私有的。我现在花了两天时间,没有弄清楚 terraform 模板中的问题是什么。非常感谢任何帮助识别问题的帮助。非常感谢

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = "us-east-1"
}

resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"
}

# Create Subnets in the VPC
resource "aws_subnet" "subnet_az1" {
  vpc_id     = aws_vpc.my_vpc.id
  cidr_block = "10.0.0.0/24"
  availability_zone       = "us-east-1a"
  map_public_ip_on_launch = false # Disable auto-assigning public IPs
}

resource "aws_subnet" "subnet_az2" {
  vpc_id     = aws_vpc.my_vpc.id
  cidr_block = "10.0.1.0/24"
  availability_zone       = "us-east-1b"
  map_public_ip_on_launch = false # Disable auto-assigning public IPs
}


resource "aws_internet_gateway" "my_igw" {
  vpc_id = aws_vpc.my_vpc.id
}

# Create a new Elastic IP for the NAT Gateway
resource "aws_eip" "nat_eip" {}

# Create a public subnet in your VPC
resource "aws_subnet" "subnet_public" {
  vpc_id     = aws_vpc.my_vpc.id
  cidr_block = "10.0.2.0/24"
  availability_zone       = "us-east-1c"  # Choose a different AZ for the public subnet
  map_public_ip_on_launch = true # Auto-assign public IPs
}

# Create a private route table for your private subnet(s)
resource "aws_route_table" "private_route_table" {
  vpc_id = aws_vpc.my_vpc.id
}

# Create the NAT Gateway in a public subnet
resource "aws_nat_gateway" "my_nat_gateway" {
  allocation_id = aws_eip.nat_eip.id
  subnet_id     = aws_subnet.subnet_public.id
}

# Update the private subnet route table to route traffic through the NAT Gateway
resource "aws_route" "private_subnet_nat_gateway_route" {
  route_table_id         = aws_route_table.private_route_table.id
  destination_cidr_block = "0.0.0.0/0"  # Route all traffic
  nat_gateway_id         = aws_nat_gateway.my_nat_gateway.id
}

# Associate the private route table with your private subnet(s)
resource "aws_route_table_association" "private_subnet1_association" {
  subnet_id      = aws_subnet.subnet_az1.id  # Replace with your private subnet ID
  route_table_id = aws_route_table.private_route_table.id
}

resource "aws_route_table_association" "private_subnet2_association" {
  subnet_id      = aws_subnet.subnet_az2.id  # Replace with your private subnet ID
  route_table_id = aws_route_table.private_route_table.id
}

# resource "aws_eip" "my_eip" {}

# Create a Security Group for the Database
resource "aws_security_group" "db_security_group" {
  name        = "db-security-group"
  description = "Security group for the database"
  vpc_id        = aws_vpc.my_vpc.id

  # Define security group rules here
  # For example, allow incoming traffic on the database port
  ingress {
    from_port   = 5432
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # TODO allow only traffic from ...
  }
}

# Security Group for ECS tasks
resource "aws_security_group" "my_ecs_security_group" {
  name        = "ecs-security-group"
  description = "Security group for ECS tasks"
  vpc_id        = aws_vpc.my_vpc.id

  # Allow incoming traffic from the ALB on port 8000 (or your application port)
  ingress {
    from_port   = 8000
    to_port     = 8000
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # TODO
    # security_groups = [aws_security_group.my_alb_security_group.id]
  }

  # Allow outbound traffic 
  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"] # Allow all outbound traffic to all IP addresses
    # security_groups = [aws_security_group.my_alb_security_group.id]
  }
}

# Security Group for ALB
resource "aws_security_group" "my_alb_security_group" {
  name        = "alb-security-group"
  description = "Security group for ALB"
  vpc_id        = aws_vpc.my_vpc.id

  # Allow incoming traffic from anywhere (0.0.0.0/0) on port 80 (HTTP)
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Create an RDS PostgreSQL instance for the database
resource "aws_db_instance" "la_client_api_database" {
  allocated_storage   = 20
  storage_type        = "gp2"
  engine              = "postgres"
  engine_version      = "15.3"
  instance_class      = "db.m5.large"
  db_name                = "la_client_api_db"
  username            = "postgres"
  password            = "postgres" # Replace with a secure password
  skip_final_snapshot = true
  publicly_accessible = true # TODO

#   vpc_security_group_ids = [aws_security_group.db_security_group.id]

  tags = {
    Name = "laclientapi"
  }
}

resource "aws_security_group" "my_security_group" {
  name_prefix = "my-security-group-"
}

resource "aws_ecs_cluster" "my_cluster" {
  name = "my-cluster"
}

resource "aws_lb" "my_alb" {
  name               = "my-alb"
  internal           = false
  load_balancer_type = "application"
  
  subnets = [
    aws_subnet.subnet_az1.id, # Subnet in Availability Zone 1
    aws_subnet.subnet_az2.id  # Subnet in Availability Zone 2
  ]
  security_groups = [aws_security_group.my_alb_security_group.id]

  enable_cross_zone_load_balancing = "true"
  enable_deletion_protection = false
}

resource "aws_lb_target_group" "fargate_target_group" {
  # Target group configuration here
  name        = "fargate-target-group"
  port        = 8000 # Replace with the desired target port
  protocol    = "HTTP"
  vpc_id      = aws_vpc.my_vpc.id
  target_type = "ip" # Use "ip" for Fargate tasks

  health_check {
    path                = "/"
    protocol            = "HTTP"
    port                = "8000"
    interval            = 30
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
  
  # Ensure this target group is associated with the ALB
  depends_on = [aws_lb.my_alb]
}

# Create an ECS task definition for the API service
resource "aws_ecs_task_definition" "api_task" {
  family                   = "api-task"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  execution_role_arn       = aws_iam_role.ecs_execution_role.arn

  cpu     = "256"  # Specify the CPU value for the task in CPU units (e.g., 256)
  memory  = 512 # Specify the memory limit for the container in MiB
  
  container_definitions = jsonencode([{
    name  = "client-api-container"
    image = "public.ecr.aws/j0m1s7l0/la-client-api:latest" 
    
    portMappings = [{
      containerPort = 8000,
      hostPort      = 8000
    }]
    
    environment = [
      { name = "DATABASE_URL", value = "postgresql://postgres:postgres@${aws_db_instance.la_client_api_database.address}:5432/la_client_api_db" },
      { name = "ROCKET_ADDRESS", value = "0.0.0.0" },
      { name = "ROCKET_ENV", value = "release" },
    ]

    # Add an authentication block for Docker Hub with the access token
    authentication = [
      {
        credentials_parameter = "DOCKERHUB_ACCESS_TOKEN"
        authentication = "DOCKER"
        auth = "dckr_pat_e-doKHRHR8CKuRw4eWvirzzNYNk"
      }
    ]

    # volume {
    #   name      = "service-storage"
    #   host_path = "/ecs/service-storage"
    # }

    placement_constraints {
      type       = "memberOf"
      expression = "attribute:ecs.availability-zone in [us-east-1a, us-east-1b]"
    }

  }])
}

resource "aws_lb_listener" "my_alb_listener" {
  load_balancer_arn = aws_lb.my_alb.arn
  port              = 80
  protocol          = "HTTP"
  
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.fargate_target_group.arn
  }

  depends_on = [aws_lb.my_alb, aws_lb_target_group.fargate_target_group]
}

resource "aws_lb_listener_rule" "my_listener_rule" {
  listener_arn = aws_lb_listener.my_alb_listener.arn
  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.fargate_target_group.arn
  }

  priority = 1 # Set a priority lower than other rules

  condition {
    path_pattern {
      values = ["/"]  # This path pattern will match all paths
    }
  }
}

# Create an ECS service for the API task
resource "aws_ecs_service" "api_service" {
  name            = "api-service"
  cluster         = aws_ecs_cluster.my_cluster.id
  task_definition = aws_ecs_task_definition.api_task.arn
  desired_count = 1
  launch_type     = "FARGATE" # or EC2 
  # TODO: maybe specify LB to associate with the service
  network_configuration {
    subnets         =  [aws_subnet.subnet_az1.id, aws_subnet.subnet_az2.id]
    security_groups = [aws_security_group.my_ecs_security_group.id]
    assign_public_ip = true  # Add this line to assign a public IP
  }
  
  load_balancer {
    target_group_arn = aws_lb_target_group.fargate_target_group.arn
    container_name   = "client-api-container"
    container_port   = 8000
  }

  depends_on = [aws_lb_target_group.fargate_target_group, aws_ecs_cluster.my_cluster, aws_db_instance.la_client_api_database]
}

# Define an IAM role for ECS task execution
resource "aws_iam_role" "ecs_execution_role" {
  name = "ecs_execution_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Action = "sts:AssumeRole",
      Effect = "Allow",
      Principal = {
        Service = "ecs-tasks.amazonaws.com"
      }
    }]
  })
}

# Attach the AmazonECSTaskExecutionRolePolicy policy to the execution role
resource "aws_iam_policy_attachment" "ecs_execution_role_attachment" {
  name       = "ecs_execution_role_attachment"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
  roles      = [aws_iam_role.ecs_execution_role.name]
}

output "alb_dns_name" {
  value = aws_lb.my_alb.dns_name
}

# output "alb_public_ip" {
#   value = aws_lb.my_alb.load_balancer_type == "network" ? aws_lb.my_alb.ip_address : null
# }


我多次检查了 terraform 模板。在 docker hub 中发布镜像时出现同样的问题 (

image = "outsidethecode/la:0.1"
)

amazon-web-services terraform amazon-ecs docker-registry amazon-ecr
1个回答
0
投票

带有“允许所有出站流量”注释的安全组规则被定义为

ingress
规则,因此它实际上允许所有 入站 流量。您的安全组完全缺乏
egress
规则,因此目前所有出站流量都被阻止。

© www.soinside.com 2019 - 2024. All rights reserved.