EC2 自动扩展和代码部署

EC2 自动扩展和代码部署

我刚刚开始使用 EC2 负载均衡器,并尝试实现自动调用和容错。数据库托管在 RDS 中,会话共享,文件使用 S3 共享。

我似乎找不到代码部署的最佳解决方案。我不想使用 fabric 在所有实例上重新运行部署命令。

在 1 个实例上更新代码、从中创建 AMI、然后使用该 AMI 重新启动实例对我来说似乎有点过度,因为我们每天部署多次,整个过程有点麻烦。

理想情况下,我希望将应用程序代码存储在所有实例中的共享(NFS?)卷上,并在部署期间更新该卷上的代码。所有实例都可以监视特定文件的更改,并在文件被触碰时重新启动应用程序工作器。

有没有办法使用 NFS 并在所有实例上自动挂载共享 EBS?

或者有更好的方法来做到这一点?

期望摘要:

我更新了 1 个 EC2 实例/NFS 卷,其余的则继续使用,无需经历创建新 AMI、销毁实例和创建新实例的整个过程。当应用程序代码和数据库模式不同步时,我不希望某些实例陷入困境。我知道最佳做法是编写支持 2 次连续数据库模式更改的代码,但目前我们真的负担不起。

答案1

我建议避免使用 NFS 共享,因为它们会为您的系统创建单点故障,并且 NFS 客户端有时可能无法从 NFS 服务器停机中恢复。NFS 并不是真正的“云方式”。AWS 在虚拟机之间进行数据共享的方式涉及使用各种分散的 AWS 服务。

对于这个特定目的,我建议使用 AWS S3 服务。为您的软件版本创建一个 S3 存储桶,将您的版本上传到那里,并授予虚拟机访问权限,以便它们可以通过轮询存储桶来下载版本。我还建议使用 IAM 角色为生产虚拟服务器提供对 S3 存储桶的只读访问权限,而无需任何硬编码的 API 凭据。此方案可在攻击者设法访问您的服务器时保护您的版本和存储桶。由于没有硬编码的 API 凭据,因此它们无法被逆向工程或在其他地方滥用。

构建和开发环境可以以类似的方式设置 - 如果构建服务器或 CI 服务器位于 AWS 基础设施上,则可以拥有对存储桶的读写访问权限。如果您的构建/CI 服务器位于其他地方,您可以设置一个具有适当 S3 访问权限的 IAM 组,并创建一个具有 API 凭证的 IAM 用户,以便在外部站点使用。

您仍需要准备一个 AMI,以便它能够按照您的意愿下载和配置版本并启动应用程序,但至少您不必在每次发布新版本时都重复此过程。使用 Chef、Puppet 或 Ansible 等配置管理工具可能可以满足您的所有需求,但您需要花一些时间来熟悉这些工具并用它们为您的环境建模。

可以使用 AWS CloudFormation 建模、创建和维护基础设施(负载均衡器、ASG、安全组、角色等)。如果您的环境比单个 ELB 和单个 ASG 更复杂,我建议您看一下。通过使用 CloudFormation 建模您的基础设施,您可以非常轻松地创建和维护整个环境的精确副本 - 例如一个用于测试/QA,一个用于生产。基础设施描述是一个 json 文档,可以保存在您的 VCS 存储库中。

我希望这有帮助。

答案2

理想情况下,您的自动配置系统应该挂接到您的部署环境中,以便为新节点提取初始代码。自动扩展组非常适合启动实例,但代码更新问题留给用户作为练习。在 AMI 中烘焙代码效果很好,除非您每天多次更新代码;在这种情况下,最好构建一个单独的代码部署机制。

这确实取决于你的代码。你可以通过几种方式来实现。

拉取部署

这是您描述的方法:每个节点都监视某些东西,一旦发生更改事件,它就会知道提取新代码。有很多方法可以实现这一点。

  • 每 n 分钟检查一次文件或 URL 的 Cron 作业。
  • Ansible 或类似程序发布更新的内容,代理轮询并执行 UpdateCode 操作。
  • 代码本身具有钩子来轮询队列服务并在更新作业到达时运行更新作业。

这样做的好处是,您可以很好地扩展代码部署基础架构。拉取方法意味着代码层运行两个版本的间隔更长,从而降低了部署基础架构的强度。

但是,您说您不能容忍多个代码版本。这将是一个问题。Pull 可能不是您现在想要的方法(尽管最终会是)。

推送部署

此方法将代码推送到节点。与 pull 类似,有很多方法可以做到这一点:

  • 类似于 capistrano 的东西,其中扩展组被轮询以获取实例列表,并且代码同时并行推送到所有节点。
  • Ansible 的不同模式或类似模式,其中每个节点上的代理都会收到主节点的通知以立即更新代码。
  • 您的数据库中已更新架构键,并且您的代码足够智能,可以注意到这一点,并在进行任何其他工作之前启动自我更新过程。

推送的优点是更容易保持代码库的紧密同步。如果您确实需要严格的单版本层,那么这样做更容易。根据您配置的扩展间隔,您可以关闭部分实例,更新子集上的代码,然后将其打开。数据库中的架构键告诉代码不要处理任何工作,并将请求转发给拥有正确密钥的代码。更新数据库中的密钥,几毫秒内,新代码节点开始执行所有工作,旧代码节点开始转发。然后更新另一层上的代码。

如果您的扩展组不能容忍代码中断……您可能必须使用 A/B 系统来处理扩展组。更新 B 组的代码,翻转数据库中的位,将 ELB 更改为指向 B 组。下次更新时反向操作。


您使用哪种方法完全取决于您的整体应用程序对同时运行的多个代码版本的容忍度。像这样的分布式系统是一个复杂的系统,无论您多么努力,系统某一部分的状态都会与系统另一部分的状态略有不同。您的容忍度越低,每次更新代码时您不得不承受的中断就越大。中断可能不到一秒钟,但它仍然是中断。

相关内容