AppSpec 的“hooks”部分 - AWS CodeDeploy

AppSpec 的“hooks”部分

AppSpec 文件的 'hooks' 部分内容会随着部署的计算平台而改变。EC2/本地部署的 'hooks' 部分包含将部署生命周期事件挂钩链接到一个或多个脚本的映射。Lambda 或 Amazon ECS 部署的 'hooks' 部分指定在部署生命周期事件期间运行的 Lambda 验证函数。如果某个事件的挂钩不存在,则不会对该事件执行任何操作。仅当您将在部署过程中运行脚本或 Lambda 验证函数时,才需要此部分。

用于 Amazon ECS 部署的 AppSpec 的“hooks”部分

用于 Amazon ECS 部署的生命周期事件挂钩的列表

AWS Lambda 挂钩是一个 Lambda 函数,该函数在生命周期事件名称之后的新行中使用字符串指定。对于每次部署,每个挂钩将执行一次。以下是在 Amazon ECS 部署期间可以运行挂钩的生命周期事件的描述。

  • BeforeInstall – 用于在创建替换任务集之前运行任务。一个目标组与原始任务集相关联。如果指定了可选的测试侦听器,则它与原始任务集相关联。此时,无法执行回滚。

  • AfterInstall – 用于在创建替换任务集并且其中一个目标组与之关联后运行任务。如果指定了可选的测试侦听器,则它与原始任务集相关联。在此生命周期事件时挂钩函数的运行结果可能会触发回滚。

  • AfterAllowTestTraffic – 用于在测试侦听器为替换任务集提供流量后运行任务。此时挂钩函数的运行结果可能会触发回滚。

  • BeforeAllowTraffic – 用于在第二个目标组与替换任务集关联之后但在流量转移到替换任务集之前运行任务。在此生命周期事件时挂钩函数的运行结果可能会触发回滚。

  • AfterAllowTraffic – 用于在第二个目标组为替换任务集提供流量后运行任务。在此生命周期事件时挂钩函数的运行结果可能会触发回滚。

有关更多信息,请参阅在 Amazon ECS 部署过程中发生的事件教程:部署具有验证测试的 Amazon ECS 服务

钩子在 Amazon ECS 部署中的运行顺序。

在 Amazon ECS 部署中,事件挂钩按以下顺序运行:

事件钩子在 Amazon ECS 部署中的顺序。
注意

部署中的 StartInstallTestTrafficAllowTrafficEnd 事件无法编写脚本,这就是它们在此图中以灰色显示的原因。

“hooks”部分的结构

以下示例说明了 'hooks' 部分的结构。

使用 YAML:

Hooks: - BeforeInstall: "BeforeInstallHookFunctionName" - AfterInstall: "AfterInstallHookFunctionName" - AfterAllowTestTraffic: "AfterAllowTestTrafficHookFunctionName" - BeforeAllowTraffic: "BeforeAllowTrafficHookFunctionName" - AfterAllowTraffic: "AfterAllowTrafficHookFunctionName"

使用 JSON:

"Hooks": [ { "BeforeInstall": "BeforeInstallHookFunctionName" }, { "AfterInstall": "AfterInstallHookFunctionName" }, { "AfterAllowTestTraffic": "AfterAllowTestTrafficHookFunctionName" }, { "BeforeAllowTraffic": "BeforeAllowTrafficHookFunctionName" }, { "AfterAllowTraffic": "AfterAllowTrafficHookFunctionName" } ] }

Lambda“hooks”函数示例

使用 'hooks' 部分可以指定 CodeDeploy 可调用以验证 Amazon ECS 部署的 Lambda 函数。对于 BeforeInstallAfterInstallAfterAllowTestTrafficBeforeAllowTrafficAfterAllowTraffic 部署生命周期事件,您可以使用相同函数或不同的函数。验证测试完成后,Lambda AfterAllowTraffic 函数会回调 CodeDeploy 并提供 SucceededFailed 结果。

重要

如果 Lambda 验证函数未在一个小时内通知 CodeDeploy,则部署将被视为已失败。

在调用 Lambda 挂钩函数之前,必须使用 putLifecycleEventHookExecutionStatus 命令向服务器通知部署 ID 和生命周期事件挂钩执行 ID。

下面是一个使用 Node.js 编写的 Lambda 挂钩函数示例。

'use strict'; const aws = require('aws-sdk'); const codedeploy = new aws.CodeDeploy({apiVersion: '2014-10-06'}); exports.handler = (event, context, callback) => { //Read the DeploymentId from the event payload. var deploymentId = event.DeploymentId; //Read the LifecycleEventHookExecutionId from the event payload var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId; /* Enter validation tests here. */ // Prepare the validation test results with the deploymentId and // the lifecycleEventHookExecutionId for CodeDeploy. var params = { deploymentId: deploymentId, lifecycleEventHookExecutionId: lifecycleEventHookExecutionId, status: 'Succeeded' // status can be 'Succeeded' or 'Failed' }; // Pass CodeDeploy the prepared validation test results. codedeploy.putLifecycleEventHookExecutionStatus(params, function(err, data) { if (err) { // Validation failed. callback('Validation test failed'); } else { // Validation succeeded. callback(null, 'Validation test succeeded'); } }); };

用于 AWS Lambda 部署的 AppSpec 的“hooks”部分

用于 AWS Lambda 部署的生命周期事件挂钩的列表

AWS Lambda 挂钩是一个 Lambda 函数,该函数在生命周期事件名称之后的新行中使用字符串指定。对于每次部署,每个挂钩将执行一次。以下是对可用于您的 AppSpec 文件中的挂钩的描述。

  • BeforeAllowTraffic – 用于在将流量转移到部署的 Lambda 函数版本之前运行任务。

  • AfterAllowTraffic – 用于在将流量转移到部署的 Lambda 函数版本之后运行任务。

挂钩在 Lambda 函数版本部署中的运行顺序

在无服务器 Lambda 函数版本部署中,事件挂钩按以下顺序运行:

事件钩子在 Lambda 部署中的顺序。
注意

部署中的 StartAllowTrafficEnd 事件无法脚本化,这就是为什么它们在此图中灰显。

“hooks”部分的结构

以下示例说明了“hooks”部分的结构。

使用 YAML:

hooks: - BeforeAllowTraffic: BeforeAllowTrafficHookFunctionName - AfterAllowTraffic: AfterAllowTrafficHookFunctionName

使用 JSON:

"hooks": [{ "BeforeAllowTraffic": "BeforeAllowTrafficHookFunctionName" }, { "AfterAllowTraffic": "AfterAllowTrafficHookFunctionName" }]

Lambda“hooks”函数示例

使用“hooks”部分可以指定 CodeDeploy 可调用以验证 Lambda 部署的 Lambda 函数。对于 BeforeAllowTrafficAfterAllowTraffic 部署生命周期事件,您可以使用相同函数或不同的函数。验证测试完成后,Lambda 验证函数会回调 CodeDeploy 并提供 SucceededFailed 结果。

重要

如果 Lambda 验证函数未在一个小时内通知 CodeDeploy,则部署将被视为已失败。

在调用 Lambda 挂钩函数之前,必须使用 putLifecycleEventHookExecutionStatus 命令向服务器通知部署 ID 和生命周期事件挂钩执行 ID。

下面是一个使用 Node.js 编写的 Lambda 挂钩函数示例。

'use strict'; const aws = require('aws-sdk'); const codedeploy = new aws.CodeDeploy({apiVersion: '2014-10-06'}); exports.handler = (event, context, callback) => { //Read the DeploymentId from the event payload. var deploymentId = event.DeploymentId; //Read the LifecycleEventHookExecutionId from the event payload var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId; /* Enter validation tests here. */ // Prepare the validation test results with the deploymentId and // the lifecycleEventHookExecutionId for CodeDeploy. var params = { deploymentId: deploymentId, lifecycleEventHookExecutionId: lifecycleEventHookExecutionId, status: 'Succeeded' // status can be 'Succeeded' or 'Failed' }; // Pass CodeDeploy the prepared validation test results. codedeploy.putLifecycleEventHookExecutionStatus(params, function(err, data) { if (err) { // Validation failed. callback('Validation test failed'); } else { // Validation succeeded. callback(null, 'Validation test succeeded'); } }); };

用于 EC2/本地部署的 AppSpec 的“hooks”部分

生命周期事件挂钩的列表

对于实例的每次部署,EC2/本地部署挂钩执行一次。在一个挂钩中,可以指定运行一个或多个脚本。生命周期事件的每个挂钩在单独的行中使用字符串指定。以下是对可用于您的 AppSpec 文件中的挂钩的描述。

有关哪些生命周期事件挂钩对哪些部署和回滚类型有效的信息,请参阅生命周期事件挂钩可用性

  • ApplicationStop – 此部署生命周期事件发生在下载应用程序修订之前。您可以为此事件指定脚本,以便从容地停止应用程序或在部署准备过程中删除当前已安装的软件包。用于此部署生命周期事件的 AppSpec 文件和脚本来自于上一个成功部署的应用程序修订。

    注意

    在您部署实例之前,实例上不存在 AppSpec 文件。因此,ApplicationStop 挂钩在您首次部署到实例时不会运行。您可以在第二次部署到实例时使用 ApplicationStop 挂钩。

    为确定上次成功部署的应用程序修订的位置,CodeDeploy 代理将查看 deployment-group-id_last_successful_install 文件中列出的位置。此文件位于:

    Amazon Linux、Ubuntu Server 和 RHEL Amazon EC2 实例上的 /opt/codedeploy-agent/deployment-root/deployment-instructions 文件夹。

    Windows Server Amazon EC2 实例上的 C:\ProgramData\Amazon\CodeDeploy\deployment-instructions 文件夹。

    要对在 ApplicationStop 部署生命周期事件期间失败的部署进行故障排除,请参阅 对失败的 ApplicationStop、BeforeBlockTraffic 或 AfterBlockTraffic 部署生命周期事件进行故障排除

  • DownloadBundle – 在此部署生命周期事件期间,CodeDeploy 代理会将应用程序修订文件复制到临时位置:

    Amazon Linux、Ubuntu Server 和 RHEL Amazon EC2 实例上的 /opt/codedeploy-agent/deployment-root/deployment-group-id/deployment-id/deployment-archive 文件夹。

    Windows Server Amazon EC2 实例上的 C:\ProgramData\Amazon\CodeDeploy\deployment-group-id\deployment-id\deployment-archive 文件夹。

    此事件是为 CodeDeploy 代理预留的,不能用于运行脚本。

    要对在 DownloadBundle 部署生命周期事件期间失败的部署进行故障排除,请参阅 排查失败的 DownloadBundle 部署生命周期事件的问题,错误为“UnknownError: not opened for reading”

  • BeforeInstall – 您可以使用此部署生命周期事件执行预安装任务,例如解密文件和创建当前版本的备份。

  • Install – 在此部署生命周期事件期间,CodeDeploy 代理会将修订文件从临时位置复制到最终目标文件夹中。此事件是为 CodeDeploy 代理预留的,不能用于运行脚本。

  • AfterInstall – 您可以使用此部署生命周期事件执行配置应用程序或更改文件权限等任务。

  • ApplicationStart – 此部署生命周期事件通常用于重新启动在 ApplicationStop 期间停止的服务。

  • ValidateService – 这是最后一个部署生命周期事件。它用于验证部署已成功完成。

  • BeforeBlockTraffic – 在从负载均衡器取消注册实例之前,您可以使用此部署生命周期事件在这些实例上运行任务。

    要对在 BeforeBlockTraffic 部署生命周期事件期间失败的部署进行故障排除,请参阅 对失败的 ApplicationStop、BeforeBlockTraffic 或 AfterBlockTraffic 部署生命周期事件进行故障排除

  • BlockTraffic – 在此部署生命周期事件期间,阻止互联网流量访问当前正在处理流量的实例。此事件是为 CodeDeploy 代理预留的,不能用于运行脚本。

  • AfterBlockTraffic – 在从相应的负载均衡器取消注册实例之后,您可以使用此部署生命周期事件在这些实例上运行任务。

    要对在 AfterBlockTraffic 部署生命周期事件期间失败的部署进行故障排除,请参阅 对失败的 ApplicationStop、BeforeBlockTraffic 或 AfterBlockTraffic 部署生命周期事件进行故障排除

  • BeforeAllowTraffic – 在将实例注册到负载均衡器之前,您可以使用此部署生命周期事件在这些实例上运行任务。

  • AllowTraffic – 在此部署生命周期事件期间,允许互联网流量在部署后访问实例。此事件是为 CodeDeploy 代理预留的,不能用于运行脚本。

  • AfterAllowTraffic – 在将实例注册到负载均衡器之后,您可以使用此部署生命周期事件在这些实例上运行任务。

生命周期事件挂钩可用性

下表列出了适用于每个部署和回滚方案的生命周期事件挂钩。

生命周期事件名称 Auto Scaling 启动部署¹ Auto Scaling 终止部署¹ 就地部署² 蓝/绿部署:原始实例 蓝/绿部署:替换实例 蓝/绿部署回滚:原始实例 蓝/绿部署回滚:替换实例
ApplicationStop
DownloadBundle³
BeforeInstall
Install³
AfterInstall
ApplicationStart
ValidateService
BeforeBlockTraffic
BlockTraffic³
AfterBlockTraffic
BeforeAllowTraffic
AllowTraffic³
AfterAllowTraffic

¹ 有关 Amazon EC2 Auto Scaling 部署的信息,请参阅 Amazon EC2 Auto Scaling 如何与 CodeDeploy 结合使用

² 也适用于就地部署的回滚。

³ 保留用于 CodeDeploy 操作。不能用于运行脚本。

挂钩在部署中的运行顺序

Auto Scaling 启动部署

在 Auto Scaling 启动部署期间,CodeDeploy 按以下顺序运行事件挂钩。

有关 Auto Scaling 启动部署的更多信息,请参阅 Amazon EC2 Auto Scaling 如何与 CodeDeploy 结合使用

自动扩缩启动部署期间事件钩子的顺序。
注意

部署中的 StartDownloadBundleInstallAllowTrafficEnd 事件无法脚本化,这就是它们在此图中以灰色显示的原因。不过,您可以编辑 AppSpec 文件的 'files' 部分,以指定在 Install 事件期间安装的内容。

Auto Scaling 终止部署

在 Auto Scaling 终止部署期间,CodeDeploy 按以下顺序运行事件挂钩。

有关 Auto Scaling 终止部署的更多信息,请参阅 在 Auto Scaling 横向缩减事件期间启用终止部署

自动扩缩终止部署期间事件钩子的顺序。
注意

部署中的 StartBlockTrafficEnd 事件无法脚本化,这就是它们在此图中以灰色显示的原因。

就地部署

在就地部署中(包括就地部署的回滚),事件挂钩按以下顺序运行:

注意

对于就地部署,与阻止和允许流量相关的六个挂钩仅当您在部署组中指定 Elastic Load Balancing 中的经典负载均衡器、应用程序负载均衡器或网络负载均衡器时适用。

就地部署回滚期间事件钩子的顺序。
注意

部署中的 StartDownloadBundleInstallEnd 事件无法编写脚本,这就是它们在此图中以灰色显示的原因。不过,您可以编辑 AppSpec 文件的 'files' 部分,以指定在 Install 事件期间安装的内容。

蓝绿部署

在蓝/绿部署中,事件挂钩按以下顺序运行:

事件钩子在蓝绿部署中的顺序。
注意

部署中的 StartDownloadBundleInstallBlockTrafficAllowTrafficEnd 事件无法编写脚本,这就是它们在此图中以灰色显示的原因。不过,您可以编辑 AppSpec 文件的“files”部分,以指定在 Install 事件期间安装的内容。

“hooks”部分的结构

'hooks' 部分具有以下结构:

hooks: deployment-lifecycle-event-name: - location: script-location timeout: timeout-in-seconds runas: user-name

可以在 hook 条目中的部署生命周期事件名称后包括以下元素:

location

必需。修订的脚本文件包的位置。您在 hooks 部分中指定的脚本的位置是应用程序修订包根目录的相对路径。有关更多信息,请参阅 为 CodeDeploy 规划修订

timeout

可选。在脚本被视为失败之前允许其执行的秒数。默认值为 3600 秒(1 小时)。

注意

3600 秒(1 小时)是允许每个部署生命周期事件脚本执行的最长时间。如果脚本超过此限制,则部署将停止,并且部署到实例将失败。确保在 timeout 中为每个部署生命周期事件的所有脚本指定的总秒数不超过此限制。

runas

可选。运行脚本时要模拟的用户。默认情况下,这是在实例上运行的 CodeDeploy 代理。CodeDeploy 不存储密码,因此,如果 runas 用户需要密码,则无法模拟该用户。此元素仅适用于 Amazon Linux 和 Ubuntu Server 实例。

在挂钩脚本中引用文件

如果您要按 AppSpec 的“hooks”部分 中所述,将脚本挂接到 CodeDeploy 生命周期事件,并且想要在脚本中引用文件(如 helper.sh),则需要使用以下内容指定 helper.sh

使用绝对路径

要使用文件的绝对路径来引用文件,可以执行以下任一操作:

部署存档位置

DownloadBundle 生命周期事件期间,CodeDeploy 代理会将部署修订提取到以下格式的目录中:

root-directory/deployment-group-id/deployment-id/deployment-archive

路径的 root-directory 部分始终设置为下表所示的默认值,或者由 :root_dir 配置设置控制。有关配置设置的更多信息,请参阅CodeDeploy 代理配置参考

代理平台 默认根目录
Linux – 所有 rpm 发行版 /opt/codedeploy-agent/deployment-root
Ubuntu Server – 所有 deb 发行版 /opt/codedeploy-agent/deployment-root
Windows Server %ProgramData%\Amazon\CodeDeploy

从挂钩脚本中,您可以使用根目录路径以及 DEPLOYMENT_IDDEPLOYMENT_GROUP_ID 环境变量访问当前的部署存档。有关您能使用的变量的更多信息,请参阅挂钩的环境变量可用性

下面的示例介绍了如何在 Linux 上访问位于修订根目录下的 data.json 文件:

#!/bin/bash rootDirectory="/opt/codedeploy-agent/deployment-root" # note: this will be different if you # customize the :root_dir configuration dataFile="$rootDirectory/$DEPLOYMENT_GROUP_ID/$DEPLOYMENT_ID/deployment-archive/data.json" data=$(cat dataFile)

再举一个例子,介绍了如何在 Windows 上使用 Powershell 访问位于修订根目录下的 data.json 文件:

$rootDirectory="$env:ProgramData\Amazon\CodeDeploy" # note: this will be different if you # customize the :root_dir configuration $dataFile="$rootDirectory\$env:DEPLOYMENT_GROUP_ID\$env:DEPLOYMENT_ID\deployment-archive\data.json" $data=(Get-Content $dataFile)

使用相对路径

要使用文件的相对路径引用该文件,您需要知道 CodeDeploy 代理的工作目录。文件路径是相对于该目录的。

下表显示了 CodeDeploy 代理的每个受支持平台的工作目录。

代理平台 流程管理方法 生命周期事件脚本的工作目录
Linux – 所有 rpm 发行版 systemd(默认) /
init.d – 了解更多 /opt/codedeploy-agent
Ubuntu Server – 所有 debian 发行版 全部 /opt/codedeploy-agent
Windows Server 不适用 C:\Windows\System32

挂钩的环境变量可用性

在每个部署生命周期事件期间,挂钩脚本可以访问以下环境变量:

APPLICATION_NAME

CodeDeploy 中属于当前部署的应用程序的名称(例如 WordPress_App)。

DEPLOYMENT_ID

CodeDeploy 已分配给当前部署的 ID(例如 d-AB1CDEF23)。

DEPLOYMENT_GROUP_NAME

CodeDeploy 中属于当前部署的部署组的名称(例如 WordPress_DepGroup)。

DEPLOYMENT_GROUP_ID

CodeDeploy 中属于当前部署的部署组的 ID(例如 b1a2189b-dd90-4ef5-8f40-4c1c5EXAMPLE)。

LIFECYCLE_EVENT

当前部署生命周期事件的名称(例如 AfterInstall)。

这些是每个部署生命周期事件的本地环境变量。

根据部署包的来源,还有其他环境变量可用于挂接脚本:

来自 Amazon S3 的捆绑包

  • BUNDLE_BUCKET

    从中下载部署包的 Amazon S3 存储桶的名称(例如 my-s3-bucket)。

  • BUNDLE_KEY

    Amazon S3 存储桶中下载的捆绑包的对象键(例如 WordPress_App.zip)。

  • BUNDLE_VERSION

    捆绑包的对象版本(例如 3sL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY+MTRCxf3vjVBH40Nr8X8gdRQBpUMLUo)。只有在 Amazon S3 存储桶启用了对象版本控制时,才会设置此变量。

  • BUNDLE_ETAG

    捆绑包的对象 etag(例如,b10a8db164e0754105b7a99be72e3fe5-4)。

GitHub 上的捆绑包

  • BUNDLE_COMMIT

    Git 生成的捆绑包的 SHA256 提交哈希值(例如 d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26)。

如果 DEPLOYMENT_GROUP_NAME 的值等于 Staging,则以下脚本会将 Apache HTTP 服务器上的侦听端口更改为 9090 而非 80。必须在 BeforeInstall 部署生命周期事件期间调用此脚本:

if [ "$DEPLOYMENT_GROUP_NAME" == "Staging" ] then sed -i -e 's/Listen 80/Listen 9090/g' /etc/httpd/conf/httpd.conf fi

如果 DEPLOYMENT_GROUP_NAME 环境变量的值等于 Staging,则以下脚本示例会将其错误日志中记录的消息的详细级别从警告更改为调试。必须在 BeforeInstall 部署生命周期事件期间调用此脚本:

if [ "$DEPLOYMENT_GROUP_NAME" == "Staging" ] then sed -i -e 's/LogLevel warn/LogLevel debug/g' /etc/httpd/conf/httpd.conf fi

以下脚本示例将指定网页中的文本替换为显示这些环境变量值的文本。必须在 AfterInstall 部署生命周期事件期间调用此脚本:

#!/usr/bin/python import os strToSearch="<h2>This application was deployed using CodeDeploy.</h2>" strToReplace="<h2>This page for "+os.environ['APPLICATION_NAME']+" application and "+os.environ['DEPLOYMENT_GROUP_NAME']+" deployment group with "+os.environ['DEPLOYMENT_GROUP_ID']+" deployment group ID was generated by a "+os.environ['LIFECYCLE_EVENT']+" script during "+os.environ['DEPLOYMENT_ID']+" deployment.</h2>" fp=open("/var/www/html/index.html","r") buffer=fp.read() fp.close() fp=open("/var/www/html/index.html","w") fp.write(buffer.replace(strToSearch,strToReplace)) fp.close()

挂钩示例

以下是 hooks 条目的示例,该条目为 AfterInstall 生命周期事件指定两个挂钩:

hooks: AfterInstall: - location: Scripts/RunResourceTests.sh timeout: 180 - location: Scripts/PostDeploy.sh timeout: 180

Scripts/RunResourceTests.sh 脚本在部署过程的 AfterInstall 阶段运行。如果该脚本的运行时间超过 180 秒(3 分钟),则部署将失败。

您在“hooks”部分中指定的脚本的位置是应用程序修订包根目录的相对路径。在上述示例中,名为 RunResourceTests.sh 的文件位于名为 Scripts 的目录中。该 Scripts 目录位于包的根级别。有关更多信息,请参阅 为 CodeDeploy 规划修订