AWS CodeDeploy 的 EC2/本地部署 - AppSpec 与生命周期钩子设计
通过 AppSpec 文件以声明方式定义部署目标路径和生命周期钩子。介绍基于标签的部署组和 Auto Scaling 集成的集群管理方法。
AppSpec 文件的结构
EC2/本地部署的 AppSpec 文件 (appspec.yml) 在 files 部分定义源文件的部署目标路径,在 permissions 部分设置文件的所有者和权限,在 hooks 部分指定在生命周期各阶段执行的脚本。files 部分定义 source(修订版中的路径)与 destination(实例上的路径)的映射关系。permissions 部分通过指定 owner、group、mode 来适当设置已部署文件的权限。version 字段固定为 0.0,由 AWS 保留用于将来扩展。AppSpec 文件必须放在修订版的根目录中,遗漏是导致部署失败的常见原因。当 files 部分的 destination 指定为目录时,source 的目录结构会原样复制。单文件部署时,在 source 和 destination 中都指定文件名可以实现重命名部署。
生命周期钩子的活用
生命周期钩子是在部署各阶段执行的脚本。BeforeInstall 在文件部署前执行,用于清理旧版本或安装依赖包。AfterInstall 在文件部署后执行,用于生成配置文件或执行数据库迁移。ApplicationStart 执行应用程序的启动脚本。ValidateService 在部署后执行健康检查,验证应用程序是否正常运行。如果 ValidateService 失败,部署将被记录为失败,若已配置自动回滚则会恢复到前一版本。每个钩子都可以配置超时时间(默认 3600 秒),对于长时间运行的迁移处理,通常会延长超时。ApplicationStop 用于停止前一版本,但需注意在首次部署时由于不存在前一版本而不会执行。钩子内可使用环境变量 DEPLOYMENT_ID 和 DEPLOYMENT_GROUP_NAME,能够实现按部署进行条件分支。
部署组与集群管理
部署组是将部署目标实例进行分组的单位。可以通过标签过滤器动态指定,例如选择 Environment:Production 且 Role:WebServer 的实例。与 Auto Scaling 组集成后,扩展时新启动的实例会自动部署最新的修订版。通过在部署配置中指定最小健康主机数,确保部署过程中始终有一定数量的实例继续处理请求。CodeDeployDefault.OneAtATime 逐台部署,CodeDeployDefault.HalfAtATime 每次部署一半。 要深入了解 EC2 部署实践,可参考专业书籍 (Amazon)。
设计最佳实践与陷阱
AppSpec 的 hooks 脚本应设计为幂等的。由于部署失败后的重试或回滚可能导致同一脚本多次执行,因此需要确保停止已停止的服务或创建已存在的目录时不会报错。在 ValidateService 钩子中,除 HTTP 健康检查外,还应加入等待应用预热完成的 sleep 或重试逻辑,防止因启动初期的暂时性失败导致部署失败。使用基于标签的部署组时,标签误操作可能导致部署到非预期的实例,建议在 CodePipeline 中加入审批步骤,在部署前确认目标实例列表。与 Auto Scaling 集成时,如果向新启动的实例部署失败,Auto Scaling 可能将其判定为不健康并终止,从而陷入无限循环。为防止这种情况,应配合 EC2 Auto Scaling 的生命周期钩子,等待部署完成后再转入 InService 状态。
与 CodePipeline 的集成和回滚策略
结合 CodePipeline 可以自动化从源代码变更检测到构建、测试、部署的整个工作流。在流水线的阶段之间插入手动审批操作,可以作为生产部署前的审查关卡。CodeDeploy 的回滚功能是保障部署安全的重要机制,当部署过程中 ValidateService 钩子或健康检查失败时会自动恢复到前一版本。与 CloudWatch 告警集成后,还可以在部署后错误率上升或延迟增加时触发回滚。Blue/Green 部署中通过设置保留旧环境一段时间,保证发现问题时能立即回滚。部署日志会自动发送到 CloudWatch Logs,可作为故障分析和性能改进的数据。
CodeDeploy 与其他部署方式的比较
EC2 的部署手段包括 CodeDeploy、Systems Manager Run Command、Ansible/Chef 等配置管理工具以及容器化 (ECS/EKS)。CodeDeploy 采用基于代理的拉取式部署,无需 SSH 连接和安全组开放。Systems Manager Run Command 适合单次命令执行,但回滚和阶段性部署控制需要自行实现。Ansible/Chef 的配置管理幂等性优秀,但缺乏 AWS 原生的部署进度管理(最小健康主机数、自动回滚)。容器化是不可变基础设施的理想形态,但遗留应用的迁移成本高。对于 EC2/本地的现有工作负载,CodeDeploy 是最低成本的渐进式引入方案。能够将本地服务器纳入部署目标是 CodeDeploy 独有的优势,是在混合环境中构建统一部署流水线的唯一 AWS 原生选项。
CodeDeploy 的费用
CodeDeploy 的 EC2/本地部署可免费使用。向 EC2 实例部署不产生额外费用。向本地服务器部署每个实例每次部署约 0.02 美元。ECS 和 Lambda 的部署也是免费的。CodeDeploy 本身的成本几乎为零,作为 CI/CD 流水线的部署阶段引入门槛很低,这是其一大优势。但存储修订版的 S3 费用、用于部署触发的 CodePipeline 费用(每条流水线月约 1 美元)以及通知用的 SNS 费用另行计算。对于大规模集群(数百台以上),本地部署的按量计费不可忽视,应根据月度部署次数与实例数的乘积提前进行成本估算。
总结
CodeDeploy 的 EC2/本地部署通过 AppSpec 文件和生命周期钩子以声明方式定义部署流程。基于标签的部署组可应对动态集群,最小健康主机数确保部署期间的可用性。由于本地服务器也可作为部署目标,因此对混合环境的统一部署管理非常有效。通过确保脚本幂等设计和 Auto Scaling 生命周期钩子的协调,可以实现稳定的零停机部署。