打造安全、稳定且经济高效的 ECS 集群 - 在 AWS 上创建集群的最佳实践
在 AWS 上运行 ECS 集群可能会遇到很多问题。经历了多次宕机和惨痛教训后,我总结了一系列经验教训,希望能帮助您在预算有限的情况下,打造一个安全可靠、抗脆弱的集群。
私有主机,通过 ALB/ELB 公开
负载均衡器应该是您访问集群的网关。运行 Web 服务时,请确保集群运行在私有子网中,并且您的容器无法直接从互联网访问。理想情况下,您的内部容器应该暴露一个随机的、临时的端口,并将其绑定到目标组。同时,请确保仅允许来自负载均衡器安全组的流量。
使用竞价型实例
竞价型实例是节省计算密集型/工作集群或开发/测试环境成本的绝佳方式。如果您不了解竞价型实例,以下是 AWS 文档中的一段摘录:
Spot实例是指未使用的EC2实例,其价格低于按需实例的价格。
实际上,使用竞价型实例可以节省高达80%的成本!但是,需要注意的是,竞价型实例采用竞价定价模式,AWS 可能会随时终止实例,因此您应该意识到,您的实例可能会在提前一分钟发出警告后立即被终止。
使用参数存储环境变量
在容器的任务定义中,您可以直接指定环境变量,但这并非将配置变量传递给容器的最佳方式。这种方式不安全,它将基础设施与环境变量耦合在一起,更改环境变量需要您修改任务定义并重新部署。更合适的做法是在启动时从 AWS SSM(简单系统管理器)参数存储注入环境变量。
它是一个安全、加密、可审计、可扩展且受管理的中央数据源,不仅可供 ECS 使用,还可供 Lambda、EC2、CloudFormation 等众多服务使用。此外,它与其他 AWS 服务完美集成,因此一旦某个变量发生变化,您就可以立即做出响应。更多信息请访问 AWS 官方博客。
在 EC2 实例启动时更新 ECS 代理
> service MY-SERVICE was unable to place a task because no container instance met all of its requirements.
那是我们在调查一次故障时发现的错误。“到底哪里出了问题?”——我们不断地问自己这个问题。经过一番深入调查,我们终于找到了根本原因。
由于我们的集群运行在 Spot 实例上,AWS 决定终止这些实例。幸运的是,我们的自动扩展组重新启动了这些实例,以维持所需的运行实例数量,但出现了一个问题。AMI 中安装的 ECS 代理版本过旧,无法从存储中提取 SSM 参数,因此任务反复无法启动。
解决方案?在 EC2 自动扩展组用户数据中添加以下两行简单的配置,确保 ECS 代理能够及时更新已启动的实例:
sudo yum update -y ecs-init
# Depending on AMI
### On ECS-optimized Amazon Linux 2 AMI
sudo systemctl restart docker
### On ECS-optimized Amazon Linux AMI
sudo service docker restart && sudo start ecs
根据预留实例数量而非使用量来扩展实例。
还记得service MY-SERVICE was unable to place a task...错误提示吗?另一种可能是集群资源不足,无法满足需求。这种情况很常见:CPU 预留率高达 80%,但实际使用率却很低,导致无法进行扩展操作。
典型的扩容操作问题。使用率很低,但预留容量却很高。
因此,您应该根据内存或 CPU预留量而不是使用量来扩展运行集群的 EC2 实例。您的服务应该根据使用量进行扩展,并且随着服务数量的增长,预留资源的数量也应该相应增加,这将有效地触发底层 EC2 实例上的自动扩展组操作。
调整生命值检查宽限期和冷却时间
了解容器启动所需的大致时间对于正确调整健康检查宽限期至关重要。如果配置得过于激进,仍在启动中的实例可能会被调度程序过早地标记为不健康并终止,甚至在它们真正启动之前就被终止。这将触发实例不断被创建和终止的恶性循环。在大多数情况下,增加此宽限期应该可以解决您的问题。
另一方面,宽限期过长可能会导致部署速度缓慢,并影响集群的扩展性。我的建议是从 60 秒开始,看看这个时间是否足够。
使用目标轨道缩放而不是步进缩放
与需要设置两个阈值来控制扩容或缩容的步进式扩容不同,目标跟踪式扩容服务会自动计算维持指标在指定值或接近指定值所需的服务/实例数量。根据我的经验,这种方式不仅设置更简单(所需的配置更少),而且效果也更好。
或者,您甚至可以尝试在 Lambda 函数中使用事件来制定自定义扩展策略。
如果 AWS 提供的自动扩展选项无法满足您的需求,您可以随时在 Lambda 函数中添加自定义扩展逻辑,该函数可以响应 ECS 事件、指标和警报。您可以在此处了解更多信息。
跨多个AZ
AZ 代表可用区,您可以将 AZ 理解为一个独立的数据中心。历史上,AWS 可用区曾发生过多次宕机事件,因此未来仍有可能发生。不过,保护集群免受类似故障影响非常简单——只需将机器分布在多个可用区即可。
自动扩展组管理集群在多个可用区中的可用性,从而提供弹性和可靠性,而 ECS 调度器则管理任务在不同可用区中的底层机器之间的分配,有效地使集群具有高可用性,并且不受单个可用区故障的影响。
配置蓝绿部署
实现工程领域全面敏捷性的一大挑战在于,如何让工程师轻松部署系统,而不是让部署成为一项需要停机维护、整个团队时刻关注的任务。蓝绿部署正是解决这一难题的关键所在。
部署时,会创建一个与当前环境/服务完全相同的副本,流量会逐步路由到新目标,每分钟增加几个百分点,最终在新单元上达到 100%。一旦所有流量都转移到新目标且运行稳定,绿色单元就会被停用,新版本(即“蓝色”)将成为新的标准(即“绿色”)。如果新版本出现故障,例如响应过多400,则部署过程会停止,流量将完全回滚到绿色单元。
这种方法允许您的开发团队每天多次推送新版本代码,而无需担心停机或将错误代码推送到生产环境。您可以在 AWS 官方博客上了解更多信息。
使用 IaaC
Terraform、CloudFormation 或云开发工具包 (CDK) 等工具是实现集群可复现部署的理想方式。当客户要求提供额外的环境,或者您需要运行动态的、基于分支的预发布环境时,这些工具尤其有用。此外,您还可以找到许多符合这些最佳实践的即插即用、设计精良的模块,只需单击一下即可创建此类集群。
使用 CloudWatch 日志
为了更轻松地排查应用程序故障,请始终将容器STDOUT日志推送到 CloudWatch。您只需在配置文件中添加以下几行即可containerDefinitions:
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "awslogs",
"awslogs-region": "us-west-2",
"awslogs-stream-prefix": "awslogs-example"
}
}
别忘了附加必要的 IAM 权限:logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents, logs:DescribeLogStreams
一旦它们进入 CloudWatch,您就可以使用Insights非常轻松地找到相关信息。
另外,在应用程序代码中,请记得将日志行尾符从 `\n` 改为 `\n`,\n以便\r将多行字符串保存为一个条目。这不仅可以节省成本,还能让它们更容易被 grep 命令抓取。
如果你喜欢这篇文章,不妨看看我的云创业项目——Dynobase,一款专业的DynamoDB图形用户界面客户端。


