这个新闻通讯启发了我的第一本书Node.js on AWS: From Zero to Highly Available and Scalable Hero。它长120页,更具描述性,其中包括缓存,监视和跟踪等几种最佳实践。
注意:此内容最初发表在 Simple AWS newsletter 上。了解AWS解决方案背后的原因。免费订阅! 3000名工程师和技术专家已经拥有。
用例:将EC2上的应用程序转换为ECS上的可扩展应用程序
设想
您有一个您在node.js中写的很酷的应用程序。您是一位出色的开发人员,但您最初是从AWS的0个知识开始的。首先,您刚刚启动了一个EC2实例,SSH在那里并部署了该应用程序。然后,您订阅了简单的AWS,然后阅读the issue on a self-healing, single-instance environment,因此您应用了它。更好,但没有扩展。您找到了the old issue about ECS,您了解了基本概念,但是您不知道如何从EC2上的应用程序转到ECS群集上的应用。
。服务
-
ECS:一种容器编排服务,可帮助管理集群上的docker容器。
-
弹性容器注册表(ECR):用于存放,管理和部署Docker Images的托管容器注册表。
-
fargate:用于容器的无服务器计算引擎,消除了管理基础EC2实例的需求。
解决方案逐步
在您的本地机器上安装Docker
遵循官方Docker网站的说明:
Windows:https://docs.docker.com/desktop/windows/install/
macos:https://docs.docker.com/desktop/mac/install/
linux:https://docs.docker.com/engine/install/
创建一个Dockerfile
在您的应用程序的根目录中,创建一个名为“ Dockerfile”的文件(无文件扩展名)。将以下作为起点,根据需要进行调整。
# Use the official Node.js image as the base image
FROM node:latest
# Set the working directory for the app
WORKDIR /app
# Copy package.json and package-lock.json into the container
COPY package*.json ./
# Install the app's dependencies
RUN npm ci
# Copy the app's source code into the container
COPY . .
# Expose the port your app listens on
EXPOSE 3000
# Start the app
CMD ["npm", "start"]
构建Docker图像并在本地进行测试
在您应用程序的根目录中,构建Docker映像。构建完成后,使用新图像启动本地容器。在您的浏览器或Curl或Postman中测试该应用程序,以确保其正常工作。如果不是,请返回并修复Dockerfile。
docker build -t cool-nodejs-app .
docker run -p 3000:3000 cool-nodejs-app
创建ECR注册表
首先,使用AWS管理控制台或AWS CLI创建一个新的ECR存储库。
aws ecr create-repository --repository-name cool-nodejs-app
将Docker图像推到ECR
创建ECR注册表的命令将输出输出中的指令,以使用您的ECR存储库对Docker进行身份验证。跟着他们。然后,将图像标记为ECR,用适当的值代替{AWSAccountId}
和{AWSRegion}
:
docker tag cool-nodejs-app:latest {AWSAccountId}.dkr.ecr.{AWSRegion}.amazonaws.com/cool-nodejs-app:latest
docker push {AWSAccountId}.dkr.ecr.{AWSRegion}.amazonaws.com/cool-nodejs-app:latest
创建ECS任务定义
我们将使用CloudFormation进行此操作。使用以下内容创建一个名为“ ECS-Task-definition.yaml”的文件。然后,在AWS控制台中,使用“ ecs-task-definition.yaml”文件作为模板创建一个新的CloudFormation堆栈。
Resources:
ECSTaskRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: !Sub ${AWS::AccountId}
Service:
- ecs-tasks.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: ECRReadOnlyAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- Effect: Allow
Action:
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:GetRepositoryPolicy
- ecr:DescribeRepositories
- ecr:ListImages
- ecr:DescribeImages
- ecr:BatchGetImage
Resource: !Sub "arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/cool-nodejs-app"
CoolNodejsAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: cool-nodejs-app
TaskRoleArn: !Ref ECSTaskRole
ExecutionRoleArn: !Ref ECSTaskRole
RequiresCompatibilities:
- FARGATE
NetworkMode: awsvpc
Cpu: '256'
Memory: '512'
ExecutionRoleArn: !Ref TaskExecutionRole
ContainerDefinitions:
- Name: cool-nodejs-app
Image: !Sub "arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/cool-nodejs-app:latest"
PortMappings:
- ContainerPort: 3000
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroup
awslogs-region: !Sub ${AWS::Region}
awslogs-stream-prefix: ecs
创建ECS群集和服务
您需要现有的VPC(您可以使用默认的VPC)。更新您的云形式模板(“ ecs-task-definition.yaml”),以包括ECS群集,服务以及网络和负载平衡的必要资源。用VPC的ID替换{VPCID},然后用一个或多个子网ID替换{subnetids}。然后,在控制台上,转到CloudFormation并使用修改的模板更新现有的堆栈。
Resources:
# ... (Existing Task Definition and related resources)
CoolNodejsAppService:
Type: AWS::ECS::Service
Properties:
ServiceName: cool-nodejs-app-service
Cluster: !Ref CoolNodejsAppCluster
TaskDefinition: !Ref CoolNodejsAppTaskDefinition
DesiredCount: 2
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- {SubnetIDs}
LoadBalancers:
- TargetGroupArn: !Ref AppTargetGroup
ContainerName: cool-nodejs-app
ContainerPort: 3000
CoolNodejsAppCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: cool-nodejs-app-cluster
AppLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: app-load-balancer
Scheme: internet-facing
Type: application
IpAddressType: ipv4
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '60'
Subnets:
-
SecurityGroups:
- !Ref AppLoadBalancerSecurityGroup
AppLoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: app-load-balancer-security-group
VpcId: {VPCID}
GroupDescription: Security group for the app load balancer
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
AppTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: app-target-group
Port: 3000
Protocol: HTTP
TargetType: ip
VpcId: {VPCID}
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /healthcheck
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
UnhealthyThresholdCount: 2
为ECS服务设置自动尺度
将以下资源添加到CloudFormation模板中,以基于CPU利用率设置自动缩放策略,并使用修改的模板更新CloudFormation堆栈。
Resources:
# ... (Existing resources)
AppScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 10
MinCapacity: 2
ResourceId: !Sub "service/${CoolNodejsAppCluster}/${CoolNodejsAppService}"
RoleARN: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
AppScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: app-cpu-scaling-policy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref AppScalingTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 50
ScaleInCooldown: 300
ScaleOutCooldown: 300
测试新应用
完成云形式堆栈更新完成后,转到ECS控制台,单击Cool-Nodejs-App-Cluster群集,您会找到Cool-Nodejs-App-Service服务。该服务将根据您的任务定义和所需的计数启动任务,因此您应该看到2个任务。要测试该应用程序,请转到EC2控制台,在左侧查找负载平衡器选项,单击名为App-Load-Balancer的负载平衡器,然后查找DNS名称。将名称粘贴到您的浏览器中或使用卷发或邮递员。如果我们做到了,您应该看到与本地运行应用程序时相同的输出。恭喜!
解决方案解释
-
安装Docker: ECS运行容器。 Docker是我们用于容器化应用程序的工具。容器将应用程序及其依赖项包装在一起,确保在不同阶段和平台上保持一致的环境。对于ECS,我们都需要这种方式,但是我们获得了能够在本地或在EC上运行的额外好处,而无需任何额外的努力。
-
创建一个dockerfile: dockerfile是一个脚本,告诉docker如何构建docker映像。我们指定了基本映像,复制了应用程序的文件,安装依赖项,暴露了应用程序的端口,并定义了命令以启动应用程序。在启动开发时写这篇文章非常容易,但是为现有应用程序这样做更困难。
-
构建Docker映像并在本地进行测试:您知道软件的行为的程度是您对其进行了测试的程度。因此,我们编写了Dockerfile,构建了图像并对其进行了测试!
-
创建ECR注册表: Amazon弹性容器注册表(ECR)是存储Docker容器图像的托管容器注册表。这是一个文物的仓库,我们的文物是码头图像。 ECR与EC无缝集成,使我们能够直接从那里拉出图像而没有太多麻烦。
-
将Docker图像推向ECR:我们正在构建我们的Docker图像并将其推到注册表中,以便我们可以从那里拉出。
-
创建ECS任务定义:一个任务定义描述了任务应使用的docker映像,任务应具有多少CPU和内存以及其他配置。它们基本上是任务蓝图。我们使用CloudFormation构建了我们的,因为它使其更可维护(并且为您提供模板比显示主机屏幕截图更容易)。顺便说一句,我们还提供了任务定义从ECR注册表中取出图像的权限。
-
创建ECS群集和服务:集群包含所有服务和任务。任务是我们应用程序的实例,基本上是Docker容器以及某些ECS包装和配置。我们没有手动启动任务,我们将创建一个服务,该服务是相同任务的分组。服务公开了应用程序的单个端点(在这种情况下使用应用程序加载平衡器),并控制该端点背后的任务(包括启动必要任务)。
-
为ECS服务设置自动缩放:在此步骤之前,我们的ECS服务有2个任务,仅此而已,没有任何类型的自动缩放。在这里,我们制定了告诉ECS服务何时创建更多任务或破坏现有任务的政策。我们将基于CPU利用率来执行此操作,这在大多数情况下是最佳参数(也是最容易设置的)。
-
测试新应用程序:好吧,现在我们已经部署了所有内容了。是时候测试它了!查找与我们的服务相对应的负载平衡器的DNS名称,将其粘贴在您的浏览器,卷发或邮递员中,然后将其用于试驾。如果有效,请给自己轻拍!如果没有,请给我发送电子邮件。
讨论
我们做到了!采用了一个无法扩展的EC2实例,并使其可扩展,可靠(如果任务失败,ECS启动了一个新的)且高度可用(如果您在不同的AZS中至少选择了2个子网)。而且并不难。
现实生活可能会更难。该解决方案在假设该应用程序在同一实例中不存储状态的假设工作。任何会话数据(或任何需要在实例中共享的任何数据,无论其暂时或持久性)都应存储在单独的存储中,例如数据库。即使您在其余数据中使用RDS/Aurora,DynamoDB也非常适合会话数据。
我看到的大多数使用单个EC2实例的人和关系数据库都在同一实例中运行数据库。您应该将其移至RDS/Aurora。这是从将应用程序移动到ECS的单独步骤,我们将来可以在它上做一个问题。
本地环境很容易,直到您使用3个DEV使用旧Mac,2使用M1或M2 Mac,Windows上的2个和一个孤独的家伙运行一个模糊的Linux发行版(这是同一个人认为VI比VS更好代码)。 Docker修复了。
是的,我使用了Fargate。我作弊了吗?也许是我认为您已经知道自动扩展组。我和Fargate一起去了,所以我们可以专注于ECS,但是如果您想在EC2自动缩放组上看到这一点,请点击回复并让我知道!
为什么不是ECS而不是普通的EC2自动扩展组?对于一项服务,这几乎是相同的努力。对于多种服务,EC摘要许多复杂性。
最佳实践
卓越运营
-
使用CI/CD管道:我手动运行所有这些,但是您应该添加管道。创建基础架构后,所有管道都需要做的就是使用Docker Build构建Docker Image,用Docker Tag标记它,然后用Docker Push推动它。
-
在管道中使用IAM角色:当然,您不想让任何人写信给您的ECR注册表。 CI/CD管道将需要进行身份验证。您可以使用长期寿命的证书(不是很好,但有效),也可以让管道扮演IAM角色。详细信息取决于您使用的工具,但请尝试执行此操作。
-
健康检查:为您的ECS服务和应用负载平衡器配置健康检查,以确保只有健康的任务才能接收流量。
-
将基础结构用作代码:在此问题中,您已经一半了!您已经在CloudFormation中完成了ECS群集,任务定义和服务!
-
实现日志聚合:使用CloudWatch日志,Elasticsearch或您喜欢的任何工具为您的ECS任务设置日志聚合。所有任务都是相同的,跨任务的日志应汇总。
-
使用蓝色/绿色部署: blue/green是一种策略,它包括与旧版本与旧版本并行部署新版本,对其进行测试,将流量路由到它,对其进行监视,而且,当您确定它按预期工作时,然后关闭旧版本。我真的应该在此问题。
安全
-
在秘密经理中存储秘密:数据库凭据,API密钥,其他敏感数据?秘密经理。与上一期相同的推理。
-
任务IAM角色:为每个ECS任务分配IAM角色(在任务定义下执行),因此它具有与其他AWS服务互动的权限。我们实际上是在解决方案中做到的,因此任务可以访问ECR!
-
启用网络隔离:我现在告诉您使用默认VPC。对于真实用例,您应该使用专用的VPC(它们是免费的!),然后将任务放入私有子网中。
-
使用安全组和NACL:在我们的最后一期中,我经常提到防御。基本上,在包括网络在内的多个层面上保护自己。
-
定期旋转秘密: Secrets Manager降低了被妥协的秘密机会。但是,如果是的话,您找不到呢?定期旋转密码和所有秘密。
可靠性
-
多AZ部署:在不同的AZS和EC中选择多个子网将以高度可靠的方式部署您的任务。请记住,对于AWS高度可用意味着它可以快速自动从一个AZ的失败中恢复。
-
使用连接排水:在某个时候您需要杀死一个任务,但可能会服务请求。连接排水告诉LB不要向该任务发送新的请求,而要等待几秒钟(可配置,使用300)杀死它。这样,任务可以完成处理这些请求,并且用户不会受到影响。
-
设置自动任务重试:配置自动检索因瞬态错误而失败的任务。这样,您的任务可以自动从临时错误恢复。
性能效率
-
优化任务尺寸:调整CPU和内存分配以符合您的应用程序要求。通过扔钱来解决不良表现,听起来很糟糕,但是某些过程和语言本质上是资源密集的。
-
使用容器见解:启用CloudWatch中的容器见解以监视,故障排除和优化ECS任务。这为您提供了对容器性能的宝贵见解,并帮助您确定潜在的瓶颈或优化区域。
-
使用队列进行写入:之前,您的应用程序和数据库均未缩放。现在,您的应用程序的扩展非常好,但是您的数据库仍然无法扩展。突然的用户不再降低您的应用程序层,但是随之而来的写作请求激增可能会降低数据库。为了保护这一点,请将所有写入添加到队列中,并以最大速率从队列中消耗另一种服务。
-
使用缓存:如果您多次访问相同的数据,则可以缓存。这也将保护您的数据库免受读取的爆发。
-
优化自动缩放策略:定期审查和调整缩放策略,因此您的服务不会太晚缩放并过早地扩展。
成本优化
-
配置储蓄计划:获取Fargate的储蓄计划。
-
优化自动缩放策略:定期审查和调整缩放策略,因此您的服务不会过早扩展,并且在太晚时扩展。是的,这是性能效率的相反的。底线是您需要找到最佳位置。
-
右尺寸的ECS任务:您将钱花在绩效问题上,现在效果更好。在可能的情况下优化代码,然后再次右键(这次降低资源)以获取这笔钱。与自动缩放一样,找到最佳点。
-
定期清洁您的ECR注册表: ECR每GB存储的费用。我们通常会推出和推动图像,并且永远不会删除它们。您不需要3个月前的图像(如果您这样做,您总是可以重建它们!)
-
使用EC2自动缩放组:就像我在讨论部分中提到的那样,我选择了Fargate,因此我们可以专注于EC,而不会因自动缩放实例而分心。 Fargate非常便宜,易于使用,0维护和快速缩放。 EC2实例的自动缩放组更难使用(不是那么多),需要维护工作,尺度不那么快,但是便宜得多。选择正确的一个。好消息是,它不难迁移,所以我建议所有新应用程序Fargate。
了解AWS解决方案背后的原因。
加入3000多个开发人员,技术线索和专家,学习如何与Simple AWS newsletter一起构建云解决方案而不是通过考试。
- 现实世界情景
- 解决方案背后的原因
- 如何运用最佳实践
如果您想进一步了解我,可以找到我on LinkedIn或www.guilleojeda.com