

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 教程：使用无 AWS 服务器应用程序模型部署更新的 Lambda 函数 CodeDeploy
<a name="tutorial-lambda-sam"></a>

AWS SAM 是一个用于构建无服务器应用程序的开源框架。它将 AWS SAM 模板中的 YAML 语法转换并扩展为用于构建无服务器应用程序（例如 Lambda 函数）的 CloudFormation 语法。有关更多信息，请参阅[什么是 AWS Serverless Application Model？](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) 

 在本教程中，您将使用 AWS SAM 创建可执行以下操作的解决方案：
+  创建 Lambda 函数。
+  创建您的 CodeDeploy 应用程序和部署组。
+  创建两个 Lambda 函数，用于在 CodeDeploy生命周期挂钩期间执行部署验证测试。
+  检测 Lambda 函数的更新时间。Lambda 函数的更新会触发部署 CodeDeploy ，从而逐步将生产流量从您的 Lambda 函数的原始版本转移到更新的版本。

**注意**  
本教程要求您创建的资源可能会导致您的 AWS 账户产生相关费用。这些费用包括 CodeDeploy、Amazon 和（亚马逊 CloudWatch）可能收取的费用 AWS Lambda。有关更多信息，请参阅[CodeDeploy 定价](https://aws.amazon.com/codedeploy/pricing/)、[Amazon CloudWatch 定价](https://aws.amazon.com/cloudwatch/pricing/)和[AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/)。

**Topics**
+ [先决条件](tutorial-lambda-sam-prereqs.md)
+ [步骤 1：设置基础设施](tutorial-lambda-sam-setup-infrastructure.md)
+ [步骤 2：更新 Lambda 函数](tutorial-lambda-sam-update-function.md)
+ [步骤 3：部署更新的 Lambda 函数](tutorial-lambda-sam-deploy-update.md)
+ [步骤 4：查看部署结果](tutorial-lambda-sam-deploy-view-results.md)
+ [第 5 步：清理](tutorial-lambda-clean-up.md)

# 先决条件
<a name="tutorial-lambda-sam-prereqs"></a>

要完成本教程，您首先必须：
+  完成 [入门 CodeDeploy](getting-started-codedeploy.md) 中的步骤。
+  安装 C AWS Serverless Application Model LI。有关信息，请参阅[安装 AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)。
+  创建 S3 存储桶。 AWS SAM 会将 SA [AWS M 模板](https://docs.aws.amazon.com/en_us/codedeploy/latest/userguide/tutorial-lambda-sam-template.html)中引用的项目上传到此存储桶中。

# 步骤 1：设置基础设施
<a name="tutorial-lambda-sam-setup-infrastructure"></a>

 本主题向您展示 AWS SAM 如何使用为您的 AWS SAM 模板和 Lambda 函数创建文件。然后，使用 AWS SAM `package`和`deploy`命令在基础架构中生成组件。基础设施准备就绪后，您将拥有一个 CodeDeploy 应用程序和部署组、一个要更新和部署的 Lambda 函数，以及两个 Lambda 函数，其中包含在您部署 Lambda 函数时运行的验证测试。完成后，您可以使用 CloudFormation 在 Lambda 控制台中查看您的组件，或者使用 AWS CLI 来测试您的 Lambda 函数。

**Topics**
+ [创建文件](tutorial-lambda-create-files.md)
+ [Package 打包 AWS SAM 应用程序](tutorial-lambda-sam-package.md)
+ [部署 S AWS AM 应用程序](tutorial-lambda-sam-deploy.md)
+ [（可选）检查并测试基础设施](tutorial-lambda-sam-confirm-components.md)

# 创建文件
<a name="tutorial-lambda-create-files"></a>

 要创建基础设施，必须创建以下文件：
+ `template.yml`
+ `myDateTimeFunction.js`
+ `beforeAllowTraffic.js`
+ `afterAllowTraffic.js`

**Topics**
+ [创建你的 AWS SAM 模板](tutorial-lambda-sam-template.md)
+ [为 Lambda 函数创建文件](tutorial-lambda-sam-create-lambda-function.md)
+ [为您的 BeforeAllowTraffic Lambda 函数创建一个文件](tutorial-lambda-sam-create-lambda-before-traffic.md)
+ [为您的 AfterAllowTraffic Lambda 函数创建一个文件](tutorial-lambda-sam-create-lambda-after-traffic.md)

# 创建你的 AWS SAM 模板
<a name="tutorial-lambda-sam-template"></a>

创建一个 AWS SAM 模板文件来指定基础架构中的组件。

**创建 AWS SAM 模板**

1.  创建一个名为 `SAM-Tutorial` 的目录。

1.  在 `SAM-Tutorial` 目录中创建名为 `template.yml` 的文件。

1.  将以下 YAML 代码复制到 `template.yml` 中。这是 AWS SAM 模板。

   ```
   AWSTemplateFormatVersion : '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   Description: A sample SAM template for deploying Lambda functions.
   
   Resources:
   # Details about the myDateTimeFunction Lambda function
     myDateTimeFunction:
       Type: AWS::Serverless::Function
       Properties:
         Handler: myDateTimeFunction.handler
         Runtime: nodejs18.x
   # Instructs your myDateTimeFunction is published to an alias named "live".      
         AutoPublishAlias: live
   # Grants this function permission to call lambda:InvokeFunction
         Policies:
           - Version: "2012-10-17"		 	 	 
             Statement: 
             - Effect: "Allow"
               Action: 
                 - "lambda:InvokeFunction"
               Resource: '*'
         DeploymentPreference:
   # Specifies the deployment configuration      
             Type: Linear10PercentEvery1Minute
   # Specifies Lambda functions for deployment lifecycle hooks
             Hooks:
               PreTraffic: !Ref beforeAllowTraffic
               PostTraffic: !Ref afterAllowTraffic
               
   # Specifies the BeforeAllowTraffic lifecycle hook Lambda function
     beforeAllowTraffic:
       Type: AWS::Serverless::Function
       Properties:
         Handler: beforeAllowTraffic.handler
         Policies:
           - Version: "2012-10-17"		 	 	 
   # Grants this function permission to call codedeploy:PutLifecycleEventHookExecutionStatus        
             Statement: 
             - Effect: "Allow"
               Action: 
                 - "codedeploy:PutLifecycleEventHookExecutionStatus"
               Resource:
                 !Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
           - Version: "2012-10-17"		 	 	 
   # Grants this function permission to call lambda:InvokeFunction        
             Statement: 
             - Effect: "Allow"
               Action: 
                 - "lambda:InvokeFunction"
               Resource: !Ref myDateTimeFunction.Version
         Runtime: nodejs18.x
   # Specifies the name of the Lambda hook function      
         FunctionName: 'CodeDeployHook_beforeAllowTraffic'
         DeploymentPreference:
           Enabled: false
         Timeout: 5
         Environment:
           Variables:
             NewVersion: !Ref myDateTimeFunction.Version
             
   # Specifies the AfterAllowTraffic lifecycle hook Lambda function
     afterAllowTraffic:
       Type: AWS::Serverless::Function
       Properties:
         Handler: afterAllowTraffic.handler
         Policies:
           - Version: "2012-10-17"		 	 	 
             Statement: 
   # Grants this function permission to call codedeploy:PutLifecycleEventHookExecutionStatus         
             - Effect: "Allow"
               Action: 
                 - "codedeploy:PutLifecycleEventHookExecutionStatus"
               Resource:
                 !Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
           - Version: "2012-10-17"		 	 	 
             Statement: 
   # Grants this function permission to call lambda:InvokeFunction          
             - Effect: "Allow"
               Action: 
                 - "lambda:InvokeFunction"
               Resource: !Ref myDateTimeFunction.Version
         Runtime: nodejs18.x
   # Specifies the name of the Lambda hook function      
         FunctionName: 'CodeDeployHook_afterAllowTraffic'
         DeploymentPreference:
           Enabled: false
         Timeout: 5
         Environment:
           Variables:
             NewVersion: !Ref myDateTimeFunction.Version
   ```

此模板指定以下内容。有关更多信息，请参阅 [AWS SAM 模板概念](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html)。

**一个名为 `myDateTimeFunction` 的 Lambda 函数**  
 发布此 Lambda 函数时，模板中的 `AutoPublishAlias` 行将其链接到名为 `live` 的别名。在本教程的后面部分，此函数的更新会触发部署 AWS CodeDeploy ，从而逐步将生产流量从原始版本转移到更新的版本。

**两个 Lambda 部署验证函数**  
 以下 Lambda 函数是在 CodeDeploy 生命周期挂钩期间执行的。该函数包含代码，用于验证更新的 `myDateTimeFunction` 的部署。验证测试的结果通过 `PutLifecycleEventHookExecutionStatus` API 方法传递给 CodeDeploy 。如果验证测试失败，则部署失败并回滚。  
+  `CodeDeployHook_beforeAllowTraffic` 在 `BeforeAllowTraffic` 挂钩期间运行。
+  `CodeDeployHook_afterAllowTraffic` 在 `AfterAllowTraffic` 挂钩期间运行。
这两个函数的名称以 `CodeDeployHook_` 开头。`CodeDeployRoleForLambda` 角色仅允许在 Lambda 函数中，采用以此前缀开头的名称调用 Lambda `invoke` 方法。有关更多信息，请参阅《*CodeDeploy API 参考*》中的[AppSpec AWS Lambda 部署的 “挂钩” 部分](reference-appspec-file-structure-hooks.md#appspec-hooks-lambda)和[PutLifecycleEventHookExecutionStatus](https://docs.aws.amazon.com/codedeploy/latest/APIReference/API_PutLifecycleEventHookExecutionStatus.html)。

**自动检测更新的 Lambda 函数**  
 `AutoPublishAlias` 术语指示框架检测 `myDateTimeFunction` 函数何时发生了变化，然后使用 `live` 别名进行部署。

**部署配置**  
 部署配置决定了您的 CodeDeploy应用程序将流量从原始版本的 Lambda 函数转移到新版本的速率。此模板指定预定义的部署配置 `Linear10PercentEvery1Minute`。  
 您无法在 SA AWS M 模板中指定自定义部署配置。有关更多信息，请参阅 [使用创建部署配置 CodeDeploy](deployment-configurations-create.md)。

**部署生命周期挂钩函数**  
 `Hooks` 部分指定在生命周期事件挂钩期间运行的函数。`PreTraffic` 指定在 `BeforeAllowTraffic` 挂钩期间运行的函数。`PostTraffic` 指定在 `AfterAllowTraffic` 挂钩期间运行的函数。

**Lambda 调用另一个 Lambda 函数的权限**  
 指定的`lambda:InvokeFunction`权限授予 AWS SAM 应用程序使用的角色调用 Lambda 函数的权限。当 `CodeDeployHook_beforeAllowTraffic` 和 `CodeDeployHook_afterAllowTraffic` 函数在验证测试期间调用部署的 Lambda 函数时，必须具备该权限。

# 为 Lambda 函数创建文件
<a name="tutorial-lambda-sam-create-lambda-function"></a>

本教程稍后将为更新和部署的函数创建文件。

**注意**  
 Lambda 函数可以使用 AWS Lambda支持的任何运行时。有关更多信息，请参阅 [AWS Lambda 运行时](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)。

**创建 Lambda 函数**

1.  创建文本文件，并以 `myDateTimeFunction.js` 文件形式保存到 `SAM-Tutorial` 目录中。

1.  将以下 Node.js 代码复制到 `myDateTimeFunction.js` 中。

   

   ```
   'use strict';
       
       exports.handler = function(event, context, callback) {
       
         if (event.body) {
           event = JSON.parse(event.body);
         }
       
         var sc; // Status code
         var result = ""; // Response payload
       
         switch(event.option) {
           case "date": 
             switch(event.period) {
               case "yesterday":
                 result = setDateResult("yesterday");
                 sc = 200;
                 break;
               case "today":
                 result = setDateResult();
                 sc = 200;
                 break;
               case "tomorrow":
                 result = setDateResult("tomorrow");
                 sc = 200;
                 break;
               default:
                 result = {
                   "error": "Must specify 'yesterday', 'today', or 'tomorrow'."
                 };
                 sc = 400;
                 break;
             }
             break;
             
       /*      Later in this tutorial, you update this function by uncommenting 
               this section. The framework created by AWS SAM detects the update 
               and triggers a deployment by CodeDeploy. The deployment shifts 
               production traffic to the updated version of this function.
               
               case "time":
               var d = new Date();
               var h = d.getHours();
               var mi = d.getMinutes();
               var s = d.getSeconds();
       
               result = {
                 "hour": h,
                 "minute": mi,
                 "second": s
               };
               sc = 200;
               break;
       */
             default:
               result = {
                 "error": "Must specify 'date' or 'time'."
               };
               sc = 400;
             break;
         }
       
         const response = {
           statusCode: sc,
           headers: { "Content-type": "application/json" },
           body: JSON.stringify( result )
         };
       
         callback(null, response);
       
         function setDateResult(option) {
       
           var d = new Date(); // Today
           var mo; // Month
           var da; // Day
           var y; // Year
       
           switch(option) {
             case "yesterday":
               d.setDate(d.getDate() - 1);
               break;
             case "tomorrow":
               d.setDate(d.getDate() + 1);
             default:
              break;
           }
       
           mo = d.getMonth() + 1; // Months are zero offset (0-11)
           da = d.getDate();
           y = d.getFullYear();
       
           result = {
             "month": mo,
             "day": da,
             "year": y
           };
       
           return result;
         }
       };
   ```

Lambda 函数返回昨天、今天或明天的日期、月份和年份。在本教程后面的部分中，您将取消注释更新函数的代码，以返回有关您指定的日期或时间的信息（例如，日期、月份和年份，或当前小时、分钟和秒）。创建的框架 AWS SAM 会检测并部署该函数的更新版本。

**注意**  
 教程中也使用了此 Lambda 函数。 AWS Cloud9 AWS Cloud9 是一个基于云的集成开发环境。有关如何在中创建、执行、更新和调试此函数的信息 AWS Cloud9，请参阅[的AWS Lambda 教程 AWS Cloud9](https://docs.aws.amazon.com/cloud9/latest/user-guide/tutorial-lambda.html)。

# 为您的 BeforeAllowTraffic Lambda 函数创建一个文件
<a name="tutorial-lambda-sam-create-lambda-before-traffic"></a>

为 `beforeAllowTraffic` 挂钩 Lambda 函数创建文件。

1.  创建文本文件，并以 `beforeAllowTraffic.js` 文件形式保存到 `SAM-Tutorial` 目录中。

1.  将以下 Node.js 代码复制到 `beforeAllowTraffic.js` 中。该函数在部署的 `BeforeAllowTraffic` 挂钩期间执行。

   ```
   'use strict';
       
       const AWS = require('aws-sdk'); 
       const codedeploy = new AWS.CodeDeploy({apiVersion: '2014-10-06'});
       var lambda = new AWS.Lambda();
       
       exports.handler = (event, context, callback) => {
       
       	console.log("Entering PreTraffic Hook!");
       	
       	// Read the DeploymentId and LifecycleEventHookExecutionId from the event payload
         var deploymentId = event.DeploymentId;
       	var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId;
       
       	var functionToTest = process.env.NewVersion;
       	console.log("BeforeAllowTraffic hook tests started");
       	console.log("Testing new function version: " + functionToTest);
       
       	// Create parameters to pass to the updated Lambda function that
       	// include the newly added "time" option. If the function did not
       	// update, then the "time" option is invalid and function returns
       	// a statusCode of 400 indicating it failed.
       	var lambdaParams = {
       		FunctionName: functionToTest,    
       		Payload: "{\"option\": \"time\"}", 
       		InvocationType: "RequestResponse"
       	};
       
       	var lambdaResult = "Failed";
       	// Invoke the updated Lambda function.
       	lambda.invoke(lambdaParams, function(err, data) {
       		if (err){	// an error occurred
       			console.log(err, err.stack);
       			lambdaResult = "Failed";
       		}
       		else{	// successful response
       			var result = JSON.parse(data.Payload);
       			console.log("Result: " +  JSON.stringify(result));
             console.log("statusCode: " + result.statusCode);
             
             // Check if the status code returned by the updated
             // function is 400. If it is, then it failed. If 
             // is not, then it succeeded.
       			if (result.statusCode != "400"){
               console.log("Validation succeeded");
       				lambdaResult = "Succeeded";
             }
             else {
               console.log("Validation failed");
             }
       
       			// Complete the PreTraffic Hook by sending CodeDeploy the validation status
       			var params = {
       				deploymentId: deploymentId,
       				lifecycleEventHookExecutionId: lifecycleEventHookExecutionId,
       				status: lambdaResult // status can be 'Succeeded' or 'Failed'
       			};
       			
       			// Pass CodeDeploy the prepared validation test results.
       			codedeploy.putLifecycleEventHookExecutionStatus(params, function(err, data) {
       				if (err) {
       					// Validation failed.
       					console.log("CodeDeploy Status update failed");
       					console.log(err, err.stack);
       					callback("CodeDeploy Status update failed");
       				} else {
       					// Validation succeeded.
       					console.log("CodeDeploy status updated successfully");
       					callback(null, "CodeDeploy status updated successfully");
       				}
       			});
       		}  
       	});
       }
   ```

# 为您的 AfterAllowTraffic Lambda 函数创建一个文件
<a name="tutorial-lambda-sam-create-lambda-after-traffic"></a>

为 `afterAllowTraffic` 挂钩 Lambda 函数创建文件。

1.  创建文本文件，并以 `afterAllowTraffic.js` 文件形式保存到 `SAM-Tutorial` 目录中。

1.  将以下 Node.js 代码复制到 `afterAllowTraffic.js` 中。该函数在部署的 `AfterAllowTraffic` 挂钩期间执行。

   ```
   'use strict';
       
       const AWS = require('aws-sdk');
       const codedeploy = new AWS.CodeDeploy({apiVersion: '2014-10-06'});
       var lambda = new AWS.Lambda();
       
       exports.handler = (event, context, callback) => {
       
       	console.log("Entering PostTraffic Hook!");
       	
       	// Read the DeploymentId and LifecycleEventHookExecutionId from the event payload
         var deploymentId = event.DeploymentId;
       	var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId;
       
       	var functionToTest = process.env.NewVersion;
       	console.log("AfterAllowTraffic hook tests started");
       	console.log("Testing new function version: " + functionToTest);
       
       	// Create parameters to pass to the updated Lambda function that
       	// include the original "date" parameter. If the function did not 
       	// update as expected, then the "date" option might be invalid. If 
       	// the parameter is invalid, the function returns
       	// a statusCode of 400 indicating it failed.
       	var lambdaParams = {
       		FunctionName: functionToTest,    
       		Payload: "{\"option\": \"date\", \"period\": \"today\"}", 
       		InvocationType: "RequestResponse"
       	};
       
       	var lambdaResult = "Failed";
       	// Invoke the updated Lambda function.
       	lambda.invoke(lambdaParams, function(err, data) {
       		if (err){	// an error occurred
       			console.log(err, err.stack);
       			lambdaResult = "Failed";
       		}
       		else{	// successful response
       			var result = JSON.parse(data.Payload);
       			console.log("Result: " +  JSON.stringify(result));
             console.log("statusCode: " + result.statusCode);
             
             // Check if the status code returned by the updated
             // function is 400. If it is, then it failed. If 
             // is not, then it succeeded.
       			if (result.statusCode != "400"){
               console.log("Validation of time parameter succeeded");
       				lambdaResult = "Succeeded";
             }
             else {
               console.log("Validation failed");
             }
       
       			// Complete the PostTraffic Hook by sending CodeDeploy the validation status
       			var params = {
       				deploymentId: deploymentId,
       				lifecycleEventHookExecutionId: lifecycleEventHookExecutionId,
       				status: lambdaResult // status can be 'Succeeded' or 'Failed'
       			};
       			
       			// Pass CodeDeploy the prepared validation test results.
       			codedeploy.putLifecycleEventHookExecutionStatus(params, function(err, data) {
       				if (err) {
       					// Validation failed.
       					console.log("CodeDeploy Status update failed");
       					console.log(err, err.stack);
       					callback("CodeDeploy Status update failed");
       				} else {
       					// Validation succeeded.
       					console.log("CodeDeploy status updated successfully");
       					callback(null, "CodeDeploy status updated successfully");
       				}
       			});
       		}  
       	});
       }
   ```

# Package 打包 AWS SAM 应用程序
<a name="tutorial-lambda-sam-package"></a>

 现在，`SAM-Tutorial` 目录下应当具备四个文件：
+ `beforeAllowTraffic.js`
+ `afterAllowTraffic.js`
+ `myDateTimeFunction.js`
+ `template.yml`

 现在，您可以使用 AWS SAM **sam package** 命令为您的 Lambda 函数和应用程序创建和 CodeDeploy 打包工件。构件将被上传到 S3 存储桶。命令的输出是名为 `package.yml` 的新文件。 AWS SAM **sam deploy** 命令将在下一步中使用此文件。

**注意**  
 有关 **sam package** 命令的更多信息，请参阅《AWS Serverless Application Model 开发人员指南》**中的 [AWS SAM CLI 命令参考](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html)。

 在 `SAM-Tutorial` 目录中，运行以下命令。

```
sam package \
  --template-file template.yml \
  --output-template-file package.yml \
  --s3-bucket amzn-s3-demo-bucket
```

对于 `s3-bucket` 参数，指定作为本教程先决条件而创建的 Amazon S3 存储桶。指`output-template-file`定 AWS SAM **sam deploy** 命令使用的新文件的名称。

# 部署 S AWS AM 应用程序
<a name="tutorial-lambda-sam-deploy"></a>

 使用带有`package.yml`文件的 AWS SAM **sam deploy** 命令来创建您的 Lambda 函数以及 CodeDeploy 应用程序和部署组。 CloudFormation

**注意**  
有关 **sam deploy** 命令的更多信息，请参阅《AWS Serverless Application Model 开发人员指南》**中的 [AWS SAM CLI 命令参考](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html)。

 在 `SAM-Tutorial` 目录中，运行以下命令。

```
sam deploy \
  --template-file package.yml \
  --stack-name my-date-time-app \
  --capabilities CAPABILITY_IAM
```

 `--capabilities CAPABILITY_IAM` 参数是授权 CloudFormation 创建 IAM 角色的必需项。

# （可选）检查并测试基础设施
<a name="tutorial-lambda-sam-confirm-components"></a>

 本主题介绍了如何查看基础设施组件以及测试 Lambda 函数。

**在运行 `sam deploy` 后查看堆栈结果**

1. 在 [https://console.aws.amazon.com/cloudformat](https://console.aws.amazon.com/cloudformation/) ion 上打开 CloudFormation 控制台。

1.  在导航窗格中，选择 **Stacks（堆栈）**。`my-date-time-app` 堆栈显示在顶部。

1.  选择 **Events（事件）**选项卡，以查看哪些事件已完成。您可以在堆栈创建过程中查看事件。堆栈创建完成后，您可以查看所有的堆栈创建事件。

1.  在已选择堆栈的情况下，选择 **Resources（资源）**。在**类型**列中，您可以看到 Lambda 函数、`myDateTimeFunction`、`CodeDeployHook_beforeAllowTraffic` 和 `CodeDeployHook_afterAllowTraffic`。您的每个 Lambda 函数的**物理 ID** 列都包含一个用于在 Lambda 控制台中查看这些函数的链接。
**注意**  
 `myDateTimeFunction`Lambda 函数的名称前面有 CloudFormation 堆栈的名称，并添加了一个标识符，所以看起来像。`my-date-time-app-myDateTimeFunction-123456ABCDEF`

1. 打开 CodeDeploy 控制台，网址为[https://console.aws.amazon.com/codedeploy/](https://console.aws.amazon.com/codedeploy/)。

1.  在导航窗格中，展开 **Deploy（部署）**，然后选择 **Applications（应用程序）**。

1.  您应该会看到一个由 CloudFormation 创建的新 CodeDeploy 应用程序，其名称以开头`my-date-time-app-ServerlessDeploymentApplication`。选择此应用程序。

1.  您应当看到一个名称以 `my-date-time-app-myDateTimeFunctionDeploymentGroup` 开头的部署组。选择此部署组。

    在 “**部署配置”** 下，您应该看到**CodeDeployDefault。 LambdaLinear10 PercentEvery 1分钟**。

**（可选）测试函数（控制台）**

1. 打开 AWS Lambda 控制台，网址为[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/)。

1.  从导航窗格中，选择 `my-date-time-app-myDateTimeFunction` 函数。在控制台中，其名称包含一个标识符，因此看起来类似 `my-date-time-app-myDateTimeFunction-123456ABCDEF`。

1.  选择**测试**。

1.  在 **Event name（事件名称）**中，为测试事件输入名称。

1.  为测试事件输入以下内容，然后选择 **Create（创建）**。

   ```
   {
     "option": "date",
     "period": "today"
   }
   ```

1.  选择**测试**。在测试事件列表中，您应当只看到自己的测试事件。

    对于 **Execution result（执行结果）**，您应当看到 **succeeded（已成功）**。

1.  在 **Execution result（执行结果）**下，展开 **Details（详细信息）**以查看结果。您应当看到当前的月份、日期和年份。

**（可选）测试函数（AWS CLI）**

1.  找到 Lambda 函数的 ARN。当您查看函数时，它显示在 Lambda 控制台的顶部。

1.  运行如下命令。*your-function-arn*替换为函数 ARN。

   ```
   aws lambda invoke \
   --function your-function-arn \
   --cli-binary-format raw-in-base64-out \
   --payload "{\"option\": \"date\", \"period\": \"today\"}" out.txt
   ```

1.  打开 `out.txt` 以确认结果中是否包含当前的月份、日期和年份。

# 步骤 2：更新 Lambda 函数
<a name="tutorial-lambda-sam-update-function"></a>

 在本主题中，您将更新 `myDateTimeFunction.js` 文件。在下一个步骤中，您将使用该文件部署更新的函数。这会触发通过 CodeDeploy 将生产流量从当前版本的 Lambda 函数转移到更新的版本来部署它。

**更新 Lambda 函数**

1.  打开 `myDateTimeFunction.js`。

1.  删除两个注释标记（“`/*`”和“`*/`”），以及 `switch` 块中名为 `time` 的 `case` 开头和结尾处的说明文本。

    您可以通过取消注释代码，将新参数 `time` 传递给该函数。如果将 `time` 传递给更新的函数，则函数将返回当前的 `hour`、`minute` 和 `second`。

1.  保存 `myDateTimeFunction.js`。它应该类似以下内容：

   ```
   'use strict';
   
   exports.handler = function(event, context, callback) {
   
     if (event.body) {
       event = JSON.parse(event.body);
     }
   
     var sc; // Status code
     var result = ""; // Response payload
   
     switch(event.option) {
       case "date":
         switch(event.period) {
           case "yesterday":
             result = setDateResult("yesterday");
             sc = 200;
             break;
           case "today":
             result = setDateResult();
             sc = 200;
             break;
           case "tomorrow":
             result = setDateResult("tomorrow");
             sc = 200;
             break;
           default:
             result = {
               "error": "Must specify 'yesterday', 'today', or 'tomorrow'."
             };
             sc = 400;
             break;
         }
         break;
         case "time":
           var d = new Date();
           var h = d.getHours();
           var mi = d.getMinutes();
           var s = d.getSeconds();
   
           result = {
             "hour": h,
             "minute": mi,
             "second": s
           };
           sc = 200;
           break;
   
         default:
           result = {
             "error": "Must specify 'date' or 'time'."
           };
           sc = 400;
         break;
     }
   
     const response = {
       statusCode: sc,
       headers: { "Content-type": "application/json" },
       body: JSON.stringify( result )
     };
   
     callback(null, response);
   
     function setDateResult(option) {
   
       var d = new Date(); // Today
       var mo; // Month
       var da; // Day
       var y; // Year
   
       switch(option) {
         case "yesterday":
           d.setDate(d.getDate() - 1);
           break;
         case "tomorrow":
           d.setDate(d.getDate() + 1);
         default:
          break;
       }
   
       mo = d.getMonth() + 1; // Months are zero offset (0-11)
       da = d.getDate();
       y = d.getFullYear();
   
       result = {
         "month": mo,
         "day": da,
         "year": y
       };
   
       return result;
     }
   };
   ```

# 步骤 3：部署更新的 Lambda 函数
<a name="tutorial-lambda-sam-deploy-update"></a>

 在本步骤中，您将使用更新的 `myDateTimeFunction.js`，以更新并启动 Lambda 函数的部署。您可以在 CodeDeploy 或 AWS Lambda 控制台中监控部署进度。

 AWS SAM 模板中的这一`AutoPublishAlias: live`行会使您的基础架构检测到使用`live`别名的函数的更新。函数的更新会触发部署 CodeDeploy ，从而将生产流量从函数的原始版本转移到更新的版本。

 **sam package** 和 **sam deploy** 命令用于更新和触发 Lambda 函数的部署。您已在 [Package 打包 AWS SAM 应用程序](tutorial-lambda-sam-package.md) 和 [部署 S AWS AM 应用程序](tutorial-lambda-sam-deploy.md) 中执行这些命令。

**部署更新的 Lambda 函数**

1.  在 `SAM-Tutorial` 目录中，运行以下命令。

   ```
   sam package \
     --template-file template.yml \
     --output-template-file package.yml  \
     --s3-bucket amzn-s3-demo-bucket
   ```

    这会创建一组新的构件，这些构件引用您的 S3 存储桶中更新的 Lambda 函数。

1.  在 `SAM-Tutorial` 目录中，运行以下命令。

   ```
   sam deploy \
     --template-file package.yml \
     --stack-name my-date-time-app \
     --capabilities CAPABILITY_IAM
   ```

   由于堆栈名称仍为`my-date-time-app`，因此可以 CloudFormation 识别这是堆栈更新。要查看更新的堆栈，请返回 CloudFormation 控制台，然后从导航窗格中选择 **Stacks**。

**（可选）用于在部署期间查看流量（CodeDeploy 控制台）**

1. 打开 CodeDeploy 控制台，网址为[https://console.aws.amazon.com/codedeploy/](https://console.aws.amazon.com/codedeploy/)。

1.  在导航窗格中，展开 “**应用程序**”，然后选择您的 **my-date-time-app-ServerlessDeploymentApplication** 应用程序。

1.  在 **Deployment groups（部署组）**中，选择应用程序的部署组。其状态应为 **In progress（正在进行）**。

1.  在 **Deployment group history（部署组历史记录）**中，选择正在进行中的部署。

   此页面上的 **Traffic shifting（流量转移）**进度条以及 **Original（原始）**和 **Replacement（替换）**框中的百分比显示了其进度。  
![\[CodeDeploy 控制台的流量转移进度部分。\]](http://docs.aws.amazon.com/zh_cn/codedeploy/latest/userguide/images/lambda-tutorial-codedeploy-console-20-percent-deployed.png)

**（可选）在部署期间查看流量（Lambda 控制台）**

1. 打开 AWS Lambda 控制台，网址为[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/)。

1.  从导航窗格中，选择 `my-date-time-app-myDateTimeFunction` 函数。在控制台中，其名称包含一个标识符，因此看起来类似 `my-date-time-app-myDateTimeFunction-123456ABCDEF`。

1.  依次选择**别名**和 **live**。

原始函数版本（版本 1）和更新的函数版本（版本 2）旁边的权重，显示了在加载此 AWS Lambda 控制台页面时提供给每个版本的流量。该页面不会随时间更新权重。如果每隔一分钟刷新一次页面，则版本 1 的权重降低 10%，版本 2 的权重增加 10%，直到版本 2 的权重达到 100%。

![\[CodeDeploy 控制台的别名部分。\]](http://docs.aws.amazon.com/zh_cn/codedeploy/latest/userguide/images/lambda-tutorial-lambda-console-20-percent-deployed.png)


# 步骤 4：查看部署结果
<a name="tutorial-lambda-sam-deploy-view-results"></a>

在本步骤中，您将查看部署的结果。如果部署成功，即可确认更新的 Lambda 函数收到了生产流量。如果部署失败，您可以使用 CloudWatch 日志在 Lambda 函数中查看在部署生命周期挂钩期间运行的验证测试的输出。

**Topics**
+ [测试部署的函数](#tutorial-lambda-sam-deploy-test-deployed-function)
+ [在 CloudWatch 日志中查看挂钩事件](#tutorial-lambda-view-hook-events)

## 测试部署的函数
<a name="tutorial-lambda-sam-deploy-test-deployed-function"></a>

 **sam deploy** 命令更新 `my-date-time-app-myDateTimeFunction` Lambda 函数。函数版本更新为 2 并添加到 `live` 别名。

**在 Lambda 控制台中查看更新**

1. 打开 AWS Lambda 控制台，网址为[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/)。

1.  从导航窗格中，选择 `my-date-time-app-myDateTimeFunction` 函数。在控制台中，其名称包含一个标识符，因此看起来类似 `my-date-time-app-myDateTimeFunction-123456ABCDEF`。

1.  依次选择 **Qualifiers（限定词）**和 **Aliases（别名）**。部署完成后（大约 10 分钟），对于 `live` 别名，您应当看到 **Version: 2（版本：2）**。  
![\[CodeDeploy 控制台的别名部分。\]](http://docs.aws.amazon.com/zh_cn/codedeploy/latest/userguide/images/lambda-tutorial-function-version.png)

1.  在 **Function code（函数代码）**中，查看函数的源代码。此时应显示您的更改。

1.  （可选）您可以使用 [步骤 2：更新 Lambda 函数](tutorial-lambda-sam-update-function.md) 中的测试说明测试更新的函数。采用以下有效负载创建新的测试事件，然后确认结果中是否包含当前的小时、分钟和秒。

   ```
   {
       "option": "time"
     }
   ```

    要使用 AWS CLI 来测试更新后的函数，请运行以下命令，然后打开`out.txt`以确认结果包含当前小时、分钟和秒。

   ```
   aws lambda invoke --function your-function-arn --payload "{\"option\": \"time\"}" out.txt 
   ```
**注意**  
 如果您在部署完成之前使用 AWS CLI 来测试您的函数，则可能会收到意想不到的结果。这是因为每分钟将10％的流量 CodeDeploy 逐渐转移到更新的版本。在部署过程中，部分流量仍然指向原始版本，因此 `aws lambda invoke` 可能使用原始版本。10 分钟后，部署完成，所有流量均指向函数的新版本。

## 在 CloudWatch 日志中查看挂钩事件
<a name="tutorial-lambda-view-hook-events"></a>

 在`BeforeAllowTraffic`挂钩期间， CodeDeploy 执行您的 Lambd `CodeDeployHook_beforeAllowTraffic` a 函数。在`AfterAllowTraffic`挂钩期间， CodeDeploy 执行您的 Lambd `CodeDeployHook_afterAllowTraffic` a 函数。每个函数运行验证测试，使用新的 `time` 参数调用更新的函数版本。如果 Lambda 函数更新成功，则 `time` 选项不会导致错误，并且验证成功。如果函数未更新，则无法识别的参数会导致错误，并且验证失败。这些验证测试仅供演示之用。您自行编写测试来验证部署。您可以使用 CloudWatch 日志控制台查看您的验证测试。

**查看您的 CodeDeploy 挂钩事件**

1. 打开 CloudWatch 控制台，网址为[https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/)。

1.  从导航窗格中，选择 **Logs（日志）**。

1.  从日志组列表中选择**/aws/lambda/CodeDeployHook\$1 beforeAllowTraffic** 或**/aws/lambda/CodeDeployHook\$1 afterAllowTraffic**。

1.  选择日志流。您应当只看到一个。

1.  展开事件以查看其详细信息。  
![\[日志组的 CodeDeployHook 日志流。\]](http://docs.aws.amazon.com/zh_cn/codedeploy/latest/userguide/images/lambda-tutorial-cloudwatch.png)

# 第 5 步：清理
<a name="tutorial-lambda-clean-up"></a>

为避免对您在本教程中使用的资源收取更多费用，请删除您的 AWS SAM 模板创建的资源和您的 Lambda 验证函数创建的 CloudWatch 日志。

**删除您的 CloudFormation 堆栈**

1. 登录 AWS 管理控制台 并在 [https://console.aws.amazon.com/cloudformat](https://console.aws.amazon.com/cloudformation/) ion 上打开 CloudFormation 控制台。

1. 在 **Stacks（堆栈）**列中，选择 `my-date-time-app` 堆栈，然后选择 **Delete（删除）**。

1. 当系统提示时，选择 **Delete stack（删除堆栈）**。由创建的 Lambda 函数、 CodeDeploy应用程序和部署组以及 IAM 角色 AWS SAM 已删除。

**在 “日志” 中删除您的 CloudWatch 日志**

1. 打开 CloudWatch 控制台，网址为[https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/)。

1.  从导航窗格中，选择 **Logs（日志）**。

1.  从日志组列表中，选择**/aws/lambda/CodeDeployHook\$1** 旁边的按钮beforeAllowTraffic。

1.  从 **Actions（操作）**中，选择 **Delete log group（删除日志组）**，然后选择 **Yes, Delete（是，删除）**。

1.  从日志组列表中，选择**/aws/lambda/CodeDeployHook\$1** 旁边的按钮afterAllowTraffic。

1.  从 **Actions（操作）**中，选择 **Delete log group（删除日志组）**，然后选择 **Yes, Delete（是，删除）**。