【QCon】精华内容上线92%,全面覆盖“人工智能+”的典型案例!>>> 了解详情
写点什么

如何在 Amazon EC2 Container Service 上实现服务高可用的自动伸缩

  • 2019-11-13
  • 本文字数:6955 字

    阅读完需:约 23 分钟

如何在Amazon EC2 Container Service上实现服务高可用的自动伸缩

1. 简介

Amazon EC2 Container Service (ECS)是 Amazon 提供的一项 Docker 容器管理服务,可以让您轻松构建、运行、管理您的 Docker 容器服务。ECS Service 是 ECS 的重要组件,它可以在集群中运行指定数量的任务,当某个任务不可用时,它会重新启动新的任务,维持住任务的指定数量。这个特性从一定程度上保证了服务的可用性,但当面对突发流量,ECS 本身并不能动态地进行任务数量的扩展,当流量较少时,ECS 也无法动态地进行任务数量的缩减。为解决此问题,可以使用 Auto Scaling 和 Amazon CloudWatch 等实现服务的自动伸缩,保证服务高可用。本文将介绍一个运用 Auto Scaling 在 ECS 中实现服务高可用的方案,并通过对方案构建过程的剖析,让您对高可用、自动伸缩的服务架构有更进一步的了解。

2. 方案构建

2.1 整体结构图

本方案使用 AWS CloudFormation 模板声明整个资源堆栈所需资源和相关配置,并实现自动化构建。关于 AWS CloudFormation 相关知识,请通过以下链接了解:CloudFormation入门



从上面这个结构图可以看出以下主要组成部分:


  1. 初始由两个跨 AZ 的 Container Instance 组成的 ESC Cluster。

  2. 初始 ESC Service 中有两个 task。

  3. Service Auto Scaling 与 CloudWatch 结合,当 Service 维度的 CPUUtilization 与 Threshold 满足设定的触发条件时,触发 CloudWatch Alarm,CloudWatch Alarm 根据设定的 Scaling Policy 进行 Task 数量伸缩。

  4. Auto Scaling Group 与 CloudWatch 结合,当 Cluster 维度的 CPUReservation 与 Threshold 满足设定的触发条件时,触发 CloudWatch Alarm,CloudWatch Alarm 根据设定的 Scaling Policy 进行实例数量的伸缩。

2.2 准备工作

(1)准备好 CloudFormation 模板脚本


请从这里(点我)下载用于构建整个资源堆栈的模板脚本文件。


或者通过点击下面按钮来运行堆栈:



(2)准备好 Lambda Function


本方案在 Auto Scaling Group 中的实例关闭时,会调用一个实现自动切换 Draining 状态的 Lambda Function。那么在构建整个堆栈之前,需要先准备好这个 Lambda Function。这里我们使用 Python 编写了这个脚本 auto_drain.py 来实现此功能。您可以了载包含了这个脚本的压缩包(点我)


下载完成后,请将它上传到您账户上同个 Region 的 S3 Bucket 中。记住这个 S3 Bucket Name,在构建堆栈时,将它传给 Lambda Function S3 Bucket 这个参数。


2.3 构建过程


(1)构建堆栈


打开您的 CloudFormation 控制面板,使用模板脚本创建堆栈,创建成功后,打开堆栈“输出”栏,可以看到 ALBDNS 输出值如下所示:



将这个 URL 复制到浏览器访问,看到 It Works 字样,表示堆栈构建成功。


(2)测试 service auto scaling


我们在模板中定义了基于 ECS Service 维度的 CPU 使用率(CPU Utilization)指标进行自动伸缩 service 的 task 个数。初始状态下,我们设置 Service 的 Desired Count(参见模板中的 ServiceDesiredCount 参数)为 2,查看 ECS Service Management Console,选中左边栏“集群”,再选中 as-demo-cluster,点击“ECS Service 实例”。可以看到现在有两个容器实例,每个容器实例运行一个任务,总共有两个任务。



接下来我们利用 Apache ab 压测工具进行测试,向我们的 ALB 发送 30000 条请求,并发为每秒 1000。相应的 ab 命令如下:

col 1

$ ab -n 300000 -c 1000 http://as-in-publi-xxxxxxxxxxxxxx/


等待 ab 请求运行完成后,查看此时 ECS Service 中实例的状态,如下图所示,可以发现在某台实例上新增加了一个 task,总的 task 数量变成了 3 个,也就是成功触发了 Service Scale out。



等待几分钟,再次查看 ECS Service 中实例的状态,可以看到 task 总数量又变成了 2 个,即当 Service CPUUtilization 小于我们设的的 Threshold 一定时间(默认设置的是 5 分钟)后,自动触发了 Service Scale in。



(3)测试 cluster auto scaling


同样使用 Apache ab 进行压测,不过为了达到效果,需要加大请求数量和并发数量。


使用以下 ab 命令(当然您可以根据自己实际情况进行调整)

col 1

$ ab -n 1000000 -c 2000 http://as-in-publi-xxxxxxxxxxxxxx/


如果遇到 socket:Too many open files 异常。可以使用以下命令修改 linux 系统最大打开文件数

col 1

$ ulimit -n 2048


等待压测结束后,查看 ECS 中实例的状态(因为实例的启动与容器的启动不同,耗费时间比较长,可能需要等待几分钟才能看到效果),如下图所示,Cluster 自动增加了一台实例。



等待一段时间后,再次查看 ECS Service 中实例的状态,如下图所示,Cluster 自动减少了一台实例。



(4)测试自动切换实例 Draining 状态


我们使用 Lambda Function 实现在 Scale in 时自动切换实例到 Draining 状态(具体讲解见后面章节)。


当前集群中总的实例数量为 2,我们通过修改 Auto Scaling Group 的 desired count(在 EC2 管理窗中界面选中我们模板创建的 Auto Scaling Group,修改 desired count),将它设为 1,这样 Auto Scaling 会自动关闭一台实例,看看运行的任务数量有什么变化。


可以看到被停掉的实例状态自动切换成了 DRAINING,任务也被移到了另一台实例上。

3. 基于两个维度的自动伸缩

CloudWatch 监听 ECS 中的四个指标,分别是 CPUUtilization、MemoryUtilization、CPUReservation 和 MemoryReservation。这四个指标可以分为两大类,前两者是指资源使用率,后两者是指资源占有率。本方案通过集群和服务这两个维度,分别对占用率和使用率进行监控,定制不同的伸缩策略,从而实现自动伸缩。

3.1 什么是资源使用率和资源占有率

资源使用率(CPUUtilization 和 MemoryUtilization)是指 ECS 集群(或服务)中所有运行着的任务实际使用资源的总和除以 EC2 Container Service 集群(或服务)总的资源得到的数值,表现的是集群或服务中资源的使用情况


资源占有率(CPUReservation 和 MemoryReservation)是指 ECS 集群(或服务)中所有运行着的任务申请占有的资源的总和除以 EC2 Container Service 集群(或服务)总的资源得到的数值,表现的是集群或服务中资源的剩余情况

3.2 基于服务中 CPU 使用率实现服务自动伸缩

为了简单起见,本方案只监控 CPU 的使用率和占有率。


当服务面对越来越大的流量时,服务中 CPU 使用率快速上升,这时可以根据这个 CPU 使用率来动态地增加任务数量,以达到分担负载的作用。而不是根据 CPU 占有率来进行扩展,因为无论流量如何变大,CPU 占用率是与服务中任务数量相关的,任务数量不同,CPU 占用率也不会改变。


在本方案中,具体的构建过如下:


(1)首先,我们声明了对服务中 task desired count 作为 Auto Scaling 调整的对象 ScalableTarget,ResourceId 指向我们创建的 ECS Service 代码如下:

col 1

ServiceScalingTarget:


Type: AWS::ApplicationAutoScaling::ScalableTarget


DependsOn: DemoService


Properties:


MaxCapacity: !Ref ServiceMaxSize


MinCapacity: !Ref ServiceMinSize


ResourceId: !Join [”, [service/, !Ref ‘ECSCluster’, /, !GetAtt [DemoService, Name]]]


RoleARN: !GetAtt [ServiceAutoscalingRole, Arn]


ScalableDimension: ecs:service:DesiredCount


ServiceNamespace: ecs


(2)我们声明了两个 Scaling Policy,分别针对 Scale Out 和 Scale In 两种情况,当 Scale Out 时修改 ServiceScalingTarget 中的 Service Desired Count,增加 1 个 task;当 Scale In 时修改 ServiceScalingTarget 中的 Service Desired Count,减少 1 个 task。主要代码如下:

col 1

ServiceScaleOutPolicy:


Type: AWS::ApplicationAutoScaling::ScalingPolicy


Properties:


PolicyType: StepScaling


PolicyName: StepOutPolicy


ScalingTargetId: !Ref ‘ServiceScalingTarget’


StepScalingPolicyConfiguration:


AdjustmentType: ChangeInCapacity


MetricAggregationType: Average


StepAdjustments:


– MetricIntervalLowerBound: “0”


ScalingAdjustment: “1”


ServiceScaleInPolicy:


Type: AWS::ApplicationAutoScaling::ScalingPolicy


Properties:


PolicyType: StepScaling


PolicyName: StepInPolicy


ScalingTargetId: !Ref ‘ServiceScalingTarget’


StepScalingPolicyConfiguration:


AdjustmentType: ChangeInCapacity


MetricAggregationType: Average


(3)最后,我们声明两个 CloudWatch Alarm,分别在 Service 的 CPUUtilization 满足 Threshold 关系时,触发 Scale Out 和 Scale In 的 Alarm。

col 1

ServiceCPUUtilizationScaleOutAlarm:


Type: AWS::CloudWatch::Alarm


Properties:


EvaluationPeriods: !Ref ServiceCPUUtilizationScaleOutMinutes


Statistic: Average


Threshold: !Ref ServiceCPUUtilizationScaleOutThreshold


AlarmDescription: Alarm if Service CPUUtilization greater then threshold.


Period: ’60’


AlarmActions: [!Ref ‘ServiceScaleOutPolicy’]


Namespace: AWS/ECS


Dimensions:


– Name: ClusterName


Value: !Ref ECSCluster


– Name: ServiceName


Value: !GetAtt [DemoService, Name]


ComparisonOperator: GreaterThanThreshold


MetricName: CPUUtilization


ServiceCPUUtilizationScaleInAlarm:


Type: AWS::CloudWatch::Alarm


Properties:


EvaluationPeriods: !Ref ServiceCPUUtilizationScaleInMinutes


Statistic: Average


Threshold: !Ref ServiceCPUUtilizationScaleInThreshold


AlarmDescription: Alarm if Service CPUUtilization less then threshold.


Period: ’60’


AlarmActions: [!Ref ‘ServiceScaleInPolicy’]


Namespace: AWS/ECS


Dimensions:


– Name: ClusterName


Value: !Ref ECSCluster


– Name: ServiceName


Value: !GetAtt [DemoService, Name]


ComparisonOperator: LessThanThreshold


MetricName: CPUUtilization

3.3 基于集群中 CPU 占有率实现集群自动伸缩

当服务中任务的数量随着流量增大也不断增加时,集群中 CPU 的占有率也会上升,当 CPU 占有率不断上升,表示可用的资源已不多了,此时需要扩展新的实例,增加整个集群总的资源。


在本方案中,具体的构建过如下:


(1)首先声明两个 Scaling Policy,当集群 Scale Out 时修改集群中的实例数量,增加 1 个实例;当 Scale In 时修改集群中的实例数量,减少 1 个 task。主要代码如下:

col 1

ClusterScaleOutPolicy:


Type: “AWS::AutoScaling::ScalingPolicy”


Properties:


AdjustmentType: “ChangeInCapacity”


AutoScalingGroupName:


Ref: “ECSAutoScalingGroup”


PolicyType: “StepScaling”


MetricAggregationType: “Average”


StepAdjustments:



MetricIntervalLowerBound: “0”


ScalingAdjustment: “1”


ClusterScaleInPolicy:


Type: “AWS::AutoScaling::ScalingPolicy”


Properties:


AdjustmentType: “ChangeInCapacity”


AutoScalingGroupName:


Ref: “ECSAutoScalingGroup”


PolicyType: “StepScaling”


MetricAggregationType: “Average”


StepAdjustments:



MetricIntervalUpperBound: “0”


ScalingAdjustment: “-1”


(2)我们声明两个 CloudWatch Alarm,分别监听的指标是集群的 CPUReservation,当 CPUReservation 满足 Threshold 设定的关系时,触发 Scale Out 和 Scale In 的 Alarm。主要代码如下:

col 1

ClusterCPUReservationScaleOutAlarm:


Type: AWS::CloudWatch::Alarm


Properties:


EvaluationPeriods: !Ref ClusterCPUReservationScaleOutMinutes


Statistic: Average


Threshold: !Ref ClusterCPUReservationScaleOutThreshold


AlarmDescription: Alarm if Service CPUUtilization greater then threshold.


Period: ’60’


AlarmActions: [!Ref ‘ClusterScaleOutPolicy’]


Namespace: AWS/ECS


Dimensions:


– Name: ClusterName


Value: !Ref ECSCluster


ComparisonOperator: GreaterThanThreshold


MetricName: CPUReservation


ClusterCPUReservationScaleInAlarm:


Type: AWS::CloudWatch::Alarm


Properties:


EvaluationPeriods: !Ref ClusterCPUReservationScaleInMinutes


Statistic: Average


Threshold: !Ref ClusterCPUReservationScaleInThreshold


AlarmDescription: Alarm if Service CPUUtilization less then threshold.


Period: ’60’


AlarmActions: [!Ref ‘ClusterScaleInPolicy’]


Namespace: AWS/ECS


Dimensions:


– Name: ClusterName


Value: !Ref ECSCluster


ComparisonOperator: LessThanThreshold


MetricName: CPUReservation

4. Cluster scale in 时切换实例到 Draining 保证服务容量

我们的 ECS 集群并不是一创建好就一成不变的,特别是当集群空闲的时候,触发了 EC2 的 Auto Scaling Group 的缩减实例之后. 有些时候我们需要关闭其中某个实例,无论是由于系统升级、安装软件、还是为了节省运行成本。那么在关闭实例时,实例上正在运行的 task 怎么办呢?把它们全杀掉?那可不行,那样就等于减小了整个 ECS 服务的容量和负载能力,对性能有很大影响。另外就是当直接关闭 EC2 机器的时候杀死其中的 task, 可能造成运行在上面的 task 处理的请求异常终止,而影响到我们所提供服务的 SLA, 这种情况下,将实例切换到 Draining 状态就是一种解决方案。


当您要关闭集群中某个实例时,运行在这个实例上的 task 会被停止,然后 ECS 会在集群中另一个(或多个)实例上运行这些被停掉的 task,从而保证整个 ECS Service 的 task 数量保持不变,保证了整个服务容量和负载能力不受影响。同时,当实例被设置成为 Draining 状态之后,ECS 将会自动平滑关闭 EC2 宿主机中的 task 资源,利用定义 task 模版时候所定义的放置规则,将其移动到其他拥有剩余资源的集群中的宿主机上. 关于实例 Draining 的更多信息,请参见文档.


在本方案中,我们使用 AWS SNS 和 Lambda 实现了实例在集群的 Auto Scaling Group Scale in 时关联 Auto Scaling 的LifecycleHook,实现被关闭前调用 Lambda Function 自动切换 Draining 状态,将 task 转移到集群中其它实例上。


我们定义了一个 Lambda Function 运行 Python 脚本 auto_drain.py,这个脚本主要完成两个工作:


(1)判断传到 Lambda Function 中的 event 是否包含 autoscaling:EC2_INSTANCE_TERMINATING,是的话就将这个实例状态切换成 Draining,这样 ECS 就会自动停止此实例上所有运行中的 task,并在集群中别的实例启动同样数量的 task。


切换实例到 Draining 状态的代码:

col 1

containerStatus = containerInstances[‘status’]


if containerStatus == ‘DRAINING’:


tmpMsgAppend = {“containerInstanceId”: containerInstanceId}


else:


# Make ECS API call to set the container status to DRAINING


ecsResponse = ecsClient.update_container_instances_state(cluster=clusterName,


containerInstances=[containerInstanceId],status=’DRAINING’)


(2)判断实例上还有没有 task 正在运行中。如果仍然有运行中的 task,则过发送一条 SNS 通知重新触如这个 Lambda Function。如果实例上所有 task 都已经被停止,则解开 LifecyleHook,使这个实例被停掉。


根据 task 运行情况做不同处理的代码:

col 1

# If tasks are still running…


if tasksRunning == 1:


response = snsClient.list_subscriptions()


for key in response[‘Subscriptions’]:


if TopicArn == key[‘TopicArn’] and key[‘Protocol’] == ‘lambda’:


snsClient.publish(


TopicArn= key[‘TopicArn’],


Message=json.dumps(message),


Subject=’Publishing SNS message to invoke lambda again…’


)


# If tasks are NOT running…


elif tasksRunning == 0:


completeHook = 1


try:


response = asgClient.complete_lifecycle_action(


LifecycleHookName=lifecycleHookName,


AutoScalingGroupName=asgGroupName,


LifecycleActionResult=’CONTINUE’,


InstanceId=Ec2InstanceId)


logger.info(“Response received from complete_lifecycle_action %s”,response)


logger.info(“Completedlifecycle hook action”)


except Exception, e:


整个 Python 脚本代码,可以解压您在准备工作中下载的 auto_drain.zip,查看 auto_drain.py 脚本文件。


``

5. 总结

本文主要介绍了如何在 ECS 中使用 Auto Scaling、Amazon CloudWatch 和其他常用 AWS 服务实现高可用,可伸缩的服务架构,并结合 Lambda Function 和 AWS SNS 做到当集群中由于 Auto Scaling 缩减关闭某个实例时,自动切换实例状态到 Draining,将 task 转移到其他实例上,保证服务容量以及回收实例资源时候的 task 的平滑过渡。通过本套方案,可以让 ECS 服务在可用性和伸缩性上得到保证,从而为您解决实际业务场景中相类的问题提供一定的帮助和思路。您也可以在本方案基础上,结合实际业务需求,定制自己的 Scaling Policy、监控指标和监控维度,实现更符合您需求的解决方案。


作者介绍



李磊,AWS 解决方案架构师,负责基于 AWS 的云计算方案的架构设计,同时致力于 AWS 云服务在国内和全球的应用和推广。在大规模并发后台架构,电商系统,社交网络平台、互联网领域应用,DevOps 以及 Serverless 无服务器架构等领域有着广泛的设计与实践经验。在加入 AWS 之前超过十年的开发和架构设计经验, 带领团队攻克各种技术挑战,总是希望站在技术的最前沿。


本文转载自 AWS 技术博客。


原文链接:


https://amazonaws-china.com/cn/blogs/china/how-to-implement-high-availability-automatic-scaling-of-services-on-ecs/


2019-11-13 08:00557

评论

发布
暂无评论
发现更多内容

《鸿蒙生态应用开发白皮书》正式发布,携手更多开发者迈入鸿蒙世界

最新动态

面试处处碰壁?不慌,Java核心面试文档.PDF助你披荆斩棘

钟奕礼

Java java面试 java编程 程序员‘

EMQ宣布推出LF Edge eKuiper全新Logo标识

EMQ映云科技

物联网 IoT emq 11月月更 eKuiper

华为开发者联盟学堂上线超1800门课程,为开发者提供一站式赋能

最新动态

【实用工具】解决PCB设计难题,痛击风险漏洞!

华秋PCB

工具 PCB PCB设计

目前看过最全的一线大厂面试题(题+详解),你所不知道的都在这

钟奕礼

Java java程序员 java面试 java编程

太全了!神仙级SpringCloud Alibaba笔记,从入门到实战,全方位讲解微服务技术栈!

程序知音

Java 微服务 Spring Cloud 后端技术

JVM 核心知识体系

钟奕礼

Java java面试 java编程 程序员‘

东莞理工学院-网安学院举办第二届“火焰杯”软件测试高校就业选拔赛颁奖典礼

测吧(北京)科技有限公司

软件测试

瓴羊DAAS体系结构,助力乔丹体育高质量增长

瓴羊企业智能服务

《鸿蒙生态应用开发白皮书》正式发布,携手更多开发者迈入鸿蒙世界

最新动态

一次MTU问题导致的RDS访问故障

京东科技开发者

数据库 Linux MTU RDS vpn

单体分层应用架构剖析

京东科技开发者

架构 模块 应用架构 服务层 业务层

DHorse系列文章之操作手册

tiandizhiguai

微服务 云原生 k8s管理 微服务治理 Serverless Kubernetes

集群部署看过来,低代码@AWS智能集群的架构与搭建方案

葡萄城技术团队

负载均衡 部署 集群 亚马逊

【Java经典面试800题】面试必备,查漏补缺;多线程+spring+JVM调优+分布式+redis+算法

程序知音

Java java面试 java架构 后端技术 Java面试八股文

“鸿蒙开发套件”焕新升级 端到端赋能助力开发运维事半功倍

最新动态

A/B测试有哪些常见应用场景?——火山引擎DataTester科普

字节跳动数据平台

大数据 AB testing实战

高管对上新老板的 Twitter

HoneyMoose

咱也不知道这份牛P哄哄的【Nginx实战】资料是不是你们想要的

钟奕礼

Java 程序员 java面试 java编程

金九银十已过,总结了阿里面试官常问九大项面试题!

钟奕礼

Java java面试 java编程 程序员‘

阿里资深架构师谈Java进阶攻略:7大技能+12份进阶笔记+面试150题

钟奕礼

Java 程序员 java面试 java编程

Nacos 中的配置文件如何实现加密传输

江南一点雨

nacos SpringCloud

Spring6正式发布!重磅更新,是否可以拯救Java

宋小生

端云一体开发,助力鸿蒙应用开发效率全面提升

最新动态

PGL图学习之项目实践(UniMP算法实现论文节点分类、新冠疫苗项目实战,助力疫情)[系列九]

汀丶人工智能

图神经网络 图学习 11月月更 论文节点分类任务 新冠疫苗任务

即时通讯技术文集(第7期):长连接网关、P2P等 [共10篇]

JackJiang

网络编程 IM 即时通讯IM

观察者模式在spring中的应用

京东科技开发者

Java spring 编程 观察者模式

东莞理工学院-网安学院举办第二届“火焰杯”软件测试高校就业选拔赛颁奖典礼

测试人

软件测试 自动化测试 测试发开 测试比赛

四项发明专利布局国际市场 申威睿思层级化数据库操作加速系统和方法获肯定

科技热闻

即时通讯赛道开打信创牌,WorkPlus为何独树一帜?

WorkPlus

如何在Amazon EC2 Container Service上实现服务高可用的自动伸缩_语言 & 开发_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章