

# 使用 Amazon CDK 部署 Lambda 函数
<a name="lambda-cdk-tutorial"></a>

Amazon Cloud Development Kit (Amazon CDK) 是一个基础设施即代码（IaC）框架，可以让您使用所选编程语言来定义 Amazon 云基础设施。要定义您自己的云基础设施，请首先编写一个包含一个或多个堆栈的应用程序（使用 CDK 支持的一种语言）。然后，将其合成为 Amazon CloudFormation 模板并将您的资源部署到 Amazon Web Services 账户。按照本主题中的步骤部署可从 Amazon API Gateway 端点返回事件的 Lambda 函数。

CDK 中包含的 Amazon 构造库提供可用于对 Amazon Web Services 服务 提供的资源进行建模的模块。对于常用的服务，该库提供具有智能默认值和最佳实践的精选构造。只需几行代码，就可以使用 [aws\$1lambda](https://docs.amazonaws.cn/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html) 模块来定义您的函数和支持资源。

## 先决条件
<a name="lambda-cdk-prerequisites"></a>

在开始本教程之前，通过运行以下命令安装 Amazon CDK。

```
npm install -g aws-cdk
```

## 第 1 步：设置您的 Amazon CDK 项目
<a name="lambda-cdk-step-1"></a>

为您的新 Amazon CDK 应用程序创建目录并初始化项目。

------
#### [ JavaScript ]

```
mkdir hello-lambda
cd hello-lambda
cdk init --language javascript
```

------
#### [ TypeScript ]

```
mkdir hello-lambda
cd hello-lambda
cdk init --language typescript
```

------
#### [ Python ]

```
mkdir hello-lambda
cd hello-lambda
cdk init --language python
```

项目启动后，请激活项目的虚拟环境并安装 Amazon CDK 的基线依赖关系。

```
source .venv/bin/activate
python -m pip install -r requirements.txt
```

------
#### [ Java ]

```
mkdir hello-lambda
cd hello-lambda
cdk init --language java
```

将此 Maven 项目导入到 Java 集成式开发环境（IDE）中。例如，在 Eclipse 中，依次选择**文件**、**导入**、**Maven**、**现有的 Maven 项目**。

------
#### [ C\$1 ]

```
mkdir hello-lambda
cd hello-lambda
cdk init --language csharp
```

------

**注意**  
Amazon CDK 应用程序模板使用项目目录的名称来生成源文件和类的名称。在此示例中，该目录名为 `hello-lambda`。如果您使用其他项目目录名称，则您的应用将与这些说明不匹配。

Amazon CDK v2 在名为 `aws-cdk-lib` 的单个程序包中包含适用于所有 Amazon Web Services 服务 的稳定构造。当您初始化该项目时，此程序包作为依赖项进行安装。使用某些编程语言时，会在您首次构建项目时安装该程序包。

## 步骤 2：定义 Amazon CDK 堆栈
<a name="lambda-cdk-step-2"></a>

CDK *堆栈*是一个或多个构造的集合，用于定义 Amazon 资源。每个 CDK 堆栈代表您的 CDK 应用程序中的一个 Amazon CloudFormation 堆栈。

要定义您的 CDK 堆栈，请按照首选编程语言的说明操作。此堆栈定义以下内容：
+ 函数的逻辑名称：`MyFunction`
+ 在 `code` 属性中指定的函数代码的位置。有关更多信息，请参阅**《Amazon Cloud Development Kit (Amazon CDK) API Reference》中的 [Handler code](https://docs.amazonaws.cn/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html#handler-code)。
+ REST API 的逻辑名称：`HelloApi`
+ API Gateway 端点的逻辑名称：`ApiGwEndpoint`

请注意，本教程中的所有 CDK 堆栈都将 Node.js [运行时](lambda-runtimes.md)用于 Lambda 函数。可以为 CDK 堆栈和 Lambda 函数使用不同的编程语言，以利用每种语言的优势。例如，可以将 TypeScript 用于 CDK 堆栈，以利用静态输入为基础设施代码带来的好处。可以将 JavaScript 用于 Lambda 函数，以利用动态输入语言的灵活性和快速开发优势。

------
#### [ JavaScript ]

打开 `lib/hello-lambda-stack.js` 文件并将相应内容替换为以下内容。

```
const { Stack } = require('aws-cdk-lib');
const lambda = require('aws-cdk-lib/aws-lambda');
const apigw = require('aws-cdk-lib/aws-apigateway');

class HelloLambdaStack extends Stack {
  /**
   *
   * @param {Construct} scope
   * @param {string} id
   * @param {StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);
    const fn = new lambda.Function(this, 'MyFunction', {
      code: lambda.Code.fromAsset('lib/lambda-handler'),
      runtime: lambda.Runtime.NODEJS_LATEST,
      handler: 'index.handler'
    });

    const endpoint = new apigw.LambdaRestApi(this, 'MyEndpoint', {
      handler: fn,
      restApiName: "HelloApi"
    });

  }
}

module.exports = { HelloLambdaStack }
```

------
#### [ TypeScript ]

打开 `lib/hello-lambda-stack.ts` 文件并将相应内容替换为以下内容。

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as apigw from "aws-cdk-lib/aws-apigateway";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as path from 'node:path';

export class HelloLambdaStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps){
    super(scope, id, props)
    const fn = new lambda.Function(this, 'MyFunction', {
      runtime: lambda.Runtime.NODEJS_LATEST,
      handler: 'index.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
    });

    const endpoint = new apigw.LambdaRestApi(this, `ApiGwEndpoint`, {
      handler: fn,
      restApiName: `HelloApi`,
    });

  }
}
```

------
#### [ Python ]

打开 `/hello-lambda/hello_lambda/hello_lambda_stack.py` 文件并将相应内容替换为以下内容。

```
from aws_cdk import (
    Stack,
    aws_apigateway as apigw,
    aws_lambda as _lambda
)
from constructs import Construct

class HelloLambdaStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        fn = _lambda.Function(
            self,
            "MyFunction",
            runtime=_lambda.Runtime.NODEJS_LATEST,
            handler="index.handler",
            code=_lambda.Code.from_asset("lib/lambda-handler")
        )

        endpoint = apigw.LambdaRestApi(
            self,
            "ApiGwEndpoint",
            handler=fn,
            rest_api_name="HelloApi"
        )
```

------
#### [ Java ]

打开 `/hello-lambda/src/main/java/com/myorg/HelloLambdaStack.java` 文件并将相应内容替换为以下内容。

```
package com.myorg;

import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.apigateway.LambdaRestApi;
import software.amazon.awscdk.services.lambda.Function;

public class HelloLambdaStack extends Stack {
    public HelloLambdaStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public HelloLambdaStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        Function hello = Function.Builder.create(this, "MyFunction")
                            .runtime(software.amazon.awscdk.services.lambda.Runtime.NODEJS_LATEST)
                            .code(software.amazon.awscdk.services.lambda.Code.fromAsset("lib/lambda-handler"))
                            .handler("index.handler")
                            .build();

        LambdaRestApi api = LambdaRestApi.Builder.create(this, "ApiGwEndpoint")
                                .restApiName("HelloApi")
                                .handler(hello)
                                .build();

    }
}
```

------
#### [ C\$1 ]

打开 `src/HelloLambda/HelloLambdaStack.cs` 文件并将相应内容替换为以下内容。

```
using Amazon.CDK;
using Amazon.CDK.AWS.APIGateway;
using Amazon.CDK.AWS.Lambda;
using Constructs;

namespace HelloLambda
{
    public class HelloLambdaStack : Stack
    {
        internal HelloLambdaStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var fn = new Function(this, "MyFunction", new FunctionProps
            {
                Runtime = Runtime.NODEJS_LATEST,
                Code = Code.FromAsset("lib/lambda-handler"),
                Handler = "index.handler"
            });

            var api = new LambdaRestApi(this, "ApiGwEndpoint", new LambdaRestApiProps
            {
                Handler = fn
            });
        }
    }
}
```

------

## 步骤 3：创建 Lambda 函数代码
<a name="lambda-cdk-step-3"></a>

1. 从项目的根目录 (`hello-lambda`)，创建 Lambda 函数代码的 `/lib/lambda-handler` 目录。此目录在 Amazon CDK 堆栈的 `code` 属性中指定。

1. 在 `index.js` 目录中创建名为 `/lib/lambda-handler` 的新文件。将以下代码粘贴到该文件中。该函数从 API 请求中提取特定属性，并将其作为 JSON 响应返回。

   ```
   exports.handler = async (event) => {
     // Extract specific properties from the event object
     const { resource, path, httpMethod, headers, queryStringParameters, body } = event;
     const response = {
       resource,
       path,
       httpMethod,
       headers,
       queryStringParameters,
       body,
     };
     return {
       body: JSON.stringify(response, null, 2),
       statusCode: 200,
     };
   };
   ```

## 步骤 4：部署 Amazon CDK 堆栈
<a name="lambda-cdk-step-4"></a>

1. 从项目的根目录运行 [cdk synth](https://docs.amazonaws.cn/cdk/v2/guide/ref-cli-cmd-synth.html) 命令：

   ```
   cdk synth
   ```

   此命令合成 CDK 堆栈中的 Amazon CloudFormation 模板。该模板是一个约 400 行的 YAML 文件，类似于以下内容。
**注意**  
如果出现以下错误，请确保您位于项目目录的根目录中。  

   ```
   --app is required either in command-line, in cdk.json or in ~/.cdk.json
   ```  
**Example Amazon CloudFormation 模板**  

   ```
   Resources:
     MyFunctionServiceRole3C357FF2:
       Type: AWS::IAM::Role
       Properties:
         AssumeRolePolicyDocument:
           Statement:
             - Action: sts:AssumeRole
               Effect: Allow
               Principal:
                 Service: lambda.amazonaws.com
           Version: "2012-10-17"		 	 	 
         ManagedPolicyArns:
           - Fn::Join:
               - ""
               - - "arn:"
                 - Ref: AWS::Partition
                 - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
       Metadata:
         aws:cdk:path: HelloLambdaStack/MyFunction/ServiceRole/Resource
     MyFunction1BAA52E7:
       Type: AWS::Lambda::Function
       Properties:
         Code:
           S3Bucket:
             Fn::Sub: cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}
           S3Key: ab1111111cd32708dc4b83e81a21c296d607ff2cdef00f1d7f48338782f92l3901.zip
         Handler: index.handler
         Role:
           Fn::GetAtt:
             - MyFunctionServiceRole3C357FF2
             - Arn
         Runtime: nodejs24.x
         ...
   ```

1. 运行 [cdk deploy](https://docs.amazonaws.cn/cdk/v2/guide/ref-cli-cmd-deploy.html) 命令：

   ```
   cdk deploy
   ```

   等待资源创建完成。最终输出包括 API Gateway 端点的 URL。示例：

   ```
   Outputs:
   HelloLambdaStack.ApiGwEndpoint77F417B1 = https://abcd1234.execute-api.cn-north-1.amazonaws.com/prod/
   ```

## 步骤 5：测试函数
<a name="lambda-cdk-step-5"></a>

要调用 Lambda 函数，请复制 API Gateway 端点，并将其粘贴到 Web 浏览器中或运行 `curl` 命令：

```
curl -s https://abcd1234.execute-api.cn-north-1.amazonaws.com/prod/
```

响应是原始事件对象中选定属性的 JSON 表示，其中包含有关向 API Gateway 端点发出的请求的信息。示例：

```
{
  "resource": "/",
  "path": "/",
  "httpMethod": "GET",
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "Accept-Encoding": "gzip, deflate, br, zstd",
    "Accept-Language": "en-US,en;q=0.9",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-ASN": "16509",
    "CloudFront-Viewer-Country": "US",
    "Host": "abcd1234.execute-api.us-east-1.amazonaws.com",
     ...
```

## 步骤 6：清除资源
<a name="lambda-cdk-step-6"></a>

API Gateway 端点可公开访问。为防止意外收费，请运行 [cdk destroy](https://docs.amazonaws.cn/cdk/v2/guide/ref-cli-cmd-destroy.html) 命令，删除堆栈和所有关联资源。

```
cdk destroy
```

## 后续步骤
<a name="lambda-cdk-next-steps"></a>

有关使用所选语言编写 Amazon CDK 应用程序的信息，请参阅以下内容：

------
#### [ TypeScript ]

[在 TypeScript 中使用 Amazon CDK](https://docs.amazonaws.cn/cdk/v2/guide/work-with-cdk-typescript.html)

------
#### [ JavaScript ]

[在 JavaScript 中使用 Amazon CDK](https://docs.amazonaws.cn/cdk/v2/guide/work-with-cdk-javascript.html)

------
#### [ Python ]

[在 Python 中使用 Amazon CDK](https://docs.amazonaws.cn/cdk/v2/guide/work-with-cdk-python.html)

------
#### [ Java ]

[在 Java 中使用 Amazon CDK](https://docs.amazonaws.cn/cdk/v2/guide/work-with-cdk-java.html)

------
#### [ C\$1 ]

[在 C\$1 中使用 Amazon CDK](https://docs.amazonaws.cn/cdk/v2/guide/work-with-cdk-csharp.html)

------
#### [ Go ]

[在 Go 中使用 Amazon CDK](https://docs.amazonaws.cn/cdk/v2/guide/work-with-cdk-go.html)

------