我一直在尝试使用 CloudFormation 模板部署 React 前端应用程序,并且部署本身似乎可以正常工作,在本地 docker 中构建和运行的 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: 8080
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: 8080
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref FrontendLogGroup
awslogs-region: !Ref "AWS::Region"
awslogs-stream-prefix: frontend
FrontendService:
Type: AWS::ECS::Service
DependsOn: ALB
Properties:
Cluster: !Ref ECSCluster
ServiceName: !Ref ServiceName
TaskDefinition: !Ref FrontendTaskDefinition
DesiredCount: !Ref DesiredCount
LaunchType: FARGATE
LoadBalancers:
- ContainerName: frontend
ContainerPort: 8080
TargetGroupArn: !Ref TargetGroup
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
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: "*"
以及 nginx 配置:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
listen 8080;
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 8080
CMD ["nginx", "-g", "daemon off;"]
任何建议表示赞赏。
我尝试更改 nginx 配置和模板上的端口号,以防发生冲突。但我无法弄清楚出了什么问题。
由于它在本地构建并工作,我希望它在从 ECR 构建时能够正常工作。
我也尝试过让日志流正常工作,但没有成功,但这不是主要问题。
我立即看到两个问题:
您似乎没有为 ECS 服务分配安全组。这意味着该服务上的所有端口都将被阻止,并且 ALB/TargetGroup 将永远无法连接到该服务。您需要向 ECS 服务添加一个安全组,以允许来自负载均衡器的端口
8080
上的入站连接。
目标组为运行状况检查配置了正确的端口
8080
,但也为所有其他流量配置了端口 80
。目标组上的两个端口配置都应为端口 8080
。这是您告诉目标组连接到您的 ECS 服务的端口,因此他们必须与您的 ECS 服务正在侦听的端口相匹配。
我尝试更改 nginx 配置和模板上的端口号,以防发生冲突。
这是在 Fargate 上运行的单个容器,因此这里不存在端口冲突的可能性。