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

CloudFormation部署Nginx镜像至ECS遇循环问题,服务无法完成部署

问题:CloudFormation部署ECS Fargate服务陷入准备/排空循环,本地Docker正常运行

我尝试通过CloudFormation模板部署React前端应用,本地Docker构建并运行的Nginx镜像可正常工作,但CloudFormation栈始终无法完成部署,陷入服务准备与排空的循环状态。已尝试修改Nginx配置及模板中的端口号排查冲突,但未能解决;日志流配置未成功,但非当前核心问题。

相关配置文件

CloudFormation模板

AWSTemplateFormatVersion: "2010-09-09"
Parameters:
    FrontendImageName:
        Type: String
        Default: frontend_client
        Description: The name of the frontend image in AWS ECR
    ClusterName:
        Type: String
        Default: frontend-ecs-cluster
        Description: The name of the ECS cluster
    ServiceName:
        Type: String
        Default: frontend-ecs-service
        Description: The name of the ECS service
    DesiredCount:
        Type: Number
        Default: 1
        Description: The number of tasks to run

Resources:
    VPC:
        Type: AWS::EC2::VPC
        Properties:
            CidrBlock: "10.0.0.0/16"
            EnableDnsSupport: true
            EnableDnsHostnames: true
            Tags:
                - Key: Name
                  Value: VPC
    PublicSubnet1:
        Type: AWS::EC2::Subnet
        Properties:
            VpcId: !Ref VPC
            CidrBlock: "10.0.1.0/24"
            MapPublicIpOnLaunch: true
            AvailabilityZone: !Select [0, !GetAZs ""]
    PublicSubnet2:
        Type: AWS::EC2::Subnet
        Properties:
            VpcId: !Ref VPC
            CidrBlock: "10.0.2.0/24"
            MapPublicIpOnLaunch: true
            AvailabilityZone: !Select [1, !GetAZs ""]
    InternetGateway:
        Type: AWS::EC2::InternetGateway
    AttachGateway:
        Type: AWS::EC2::VPCGatewayAttachment
        Properties:
            VpcId: !Ref VPC
            InternetGatewayId: !Ref InternetGateway
    PublicRouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
            VpcId: !Ref VPC
    PublicRoute:
        Type: AWS::EC2::Route
        DependsOn: AttachGateway
        Properties:
            RouteTableId: !Ref PublicRouteTable
            DestinationCidrBlock: "0.0.0.0/0"
            GatewayId: !Ref InternetGateway
    PublicSubnet1RouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
            SubnetId: !Ref PublicSubnet1
            RouteTableId: !Ref PublicRouteTable
    PublicSubnet2RouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
            SubnetId: !Ref PublicSubnet2
            RouteTableId: !Ref PublicRouteTable
    ALB:
        Type: AWS::ElasticLoadBalancingV2::LoadBalancer
        Properties:
            Subnets:
                - !Ref PublicSubnet1
                - !Ref PublicSubnet2
            SecurityGroups:
                - !Ref ALBSecurityGroup
            Scheme: internet-facing
            Type: application
    TargetGroup:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties:
            VpcId: !Ref VPC
            Protocol: HTTP
            Port: 80
            TargetType: ip
            HealthCheckPath: /health
            HealthCheckPort: 80
    ALBListenerRule:
        Type: AWS::ElasticLoadBalancingV2::ListenerRule
        Properties:
            Actions:
                - Type: forward
                  TargetGroupArn: !Ref TargetGroup
            Conditions:
                - Field: path-pattern
                  Values:
                      - /*
            ListenerArn: !Ref ALBListener
            Priority: 1
    ALBListener:
        Type: AWS::ElasticLoadBalancingV2::Listener
        Properties:
            DefaultActions:
                - Type: forward
                  TargetGroupArn: !Ref TargetGroup
            LoadBalancerArn: !Ref ALB
            Port: 80
            Protocol: HTTP
    ALBSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: Security group for ALB
            VpcId: !Ref VPC
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  CidrIp: 0.0.0.0/0
    FrontendLogGroup:
        Type: AWS::Logs::LogGroup
        Properties:
            LogGroupName: !Sub "/ecs/${ServiceName}"
    ECSCluster:
        Type: AWS::ECS::Cluster
        Properties:
            ClusterName: !Ref ClusterName
    FrontendTaskDefinition:
        Type: AWS::ECS::TaskDefinition
        Properties:
            Family: !Ref ServiceName
            Cpu: "256"
            Memory: "512"
            NetworkMode: awsvpc
            RequiresCompatibilities:
                - FARGATE
            ExecutionRoleArn: !GetAtt FrontendTaskExecutionRole.Arn
            ContainerDefinitions:
                - Name: frontend
                  Image: !Join
                      - ""
                      - - !Ref "AWS::AccountId"
                        - ".dkr.ecr."
                        - !Ref "AWS::Region"
                        - ".amazonaws.com/"
                        - !Ref FrontendImageName
                        - ":latest"
                  Essential: true
                  PortMappings:
                      - ContainerPort: 80
                  LogConfiguration:
                      LogDriver: awslogs
                      Options:
                          awslogs-group: !Ref FrontendLogGroup
                          awslogs-region: !Ref "AWS::Region"
                          awslogs-stream-prefix: frontend
    ECSServiceSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: Security group for ECS Service
            VpcId: !Ref VPC
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  SourceSecurityGroupId: !Ref ALBSecurityGroup
    FrontendService:
        Type: AWS::ECS::Service
        DependsOn:
            - ALB
            - ECSServiceSecurityGroup
        Properties:
            Cluster: !Ref ECSCluster
            ServiceName: !Ref ServiceName
            TaskDefinition: !Ref FrontendTaskDefinition
            DesiredCount: !Ref DesiredCount
            LaunchType: FARGATE
            LoadBalancers:
                - ContainerName: frontend
                  ContainerPort: 80
                  TargetGroupArn: !Ref TargetGroup
            NetworkConfiguration:
                AwsvpcConfiguration:
                    AssignPublicIp: ENABLED
                    SecurityGroups:
                        - !Ref ECSServiceSecurityGroup
                    Subnets:
                        - !Ref PublicSubnet1
                        - !Ref PublicSubnet2
    FrontendTaskExecutionRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    - Effect: Allow
                      Principal:
                          Service:
                              - ecs-tasks.amazonaws.com
                      Action: "sts:AssumeRole"
            Policies:
                - PolicyName: FrontendTaskExecutionRolePolicy
                  PolicyDocument:
                      Version: "2012-10-17"
                      Statement:
                          - Effect: Allow
                            Action:
                                - ecr:GetAuthorizationToken
                                - ecr:BatchCheckLayerAvailability
                                - ecr:GetDownloadUrlForLayer
                                - ecr:BatchGetImage
                            Resource: "*"
                          - Effect: Allow
                            Action:
                                - logs:CreateLogStream
                                - logs:PutLogEvents
                            Resource: !GetAtt FrontendLogGroup.Arn

Nginx配置

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

server {
    listen 80;
    server_name localhost;

    location /health {
        access_log off;
        return 200 "OK\n";
    }

    location / {
        limit_req zone=mylimit burst=20 nodelay;
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    location /latest {
        return 403;
    }

    location /api/latest {
        return 403;
    }
}

Dockerfile

# Stage 1: Build React App
FROM node:alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build

# Stage 2: Serve React App with Nginx
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
解决建议
  • 修复Nginx的server_name配置:
    当前Nginx配置的server_name localhost会导致ALB发起的健康检查请求(Host头为容器IP或内部主机名)无法匹配该server块,返回404触发健康检查失败。将server_name改为_(匹配任意主机名)或者直接删除该字段:

    server {
        listen 80;
        server_name _; # 替换原localhost
    
        location /health {
            access_log off;
            return 200 "OK\n";
        }
        # 其余配置保持不变
    }
    
  • 添加ECS服务安全组的出站规则:
    当前ECSServiceSecurityGroup仅配置了入站规则,没有允许出站流量,可能导致容器无法正常初始化(比如DNS解析、依赖资源拉取等)。添加全量出站规则:

    ECSServiceSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: Security group for ECS Service
            VpcId: !Ref VPC
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  SourceSecurityGroupId: !Ref ALBSecurityGroup
            SecurityGroupEgress:
                - IpProtocol: -1
                  CidrIp: 0.0.0.0/0
    
  • 调整CloudFormation服务的依赖关系:
    确保ECS服务在ALB监听器和目标组完全创建后再启动,避免因依赖未就绪导致注册失败。修改FrontendService的DependsOn:

    FrontendService:
        Type: AWS::ECS::Service
        DependsOn:
            - ALB
            - ALBListener
            - TargetGroup
            - ECSServiceSecurityGroup
    
  • 修复日志流配置(辅助排查):
    放宽日志权限以便查看容器启动日志,帮助定位问题。修改FrontendTaskExecutionRole的日志权限资源:

    - Effect: Allow
      Action:
          - logs:CreateLogStream
          - logs:PutLogEvents
      Resource: "arn:aws:logs:*:*:*"
    
  • 验证ECR镜像状态:
    确认ECR中存在指定名称的镜像,且latest标签正确,同时检查任务执行角色的ECR拉取权限是否正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 05:19:50