

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

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

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

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

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

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

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

为您的新 AWS 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
```

项目启动后，请激活项目的虚拟环境并安装 AWS 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
```

------

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

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

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

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

要定义您的 CDK 堆栈，请按照首选编程语言的说明操作。此堆栈定义以下内容：
+ 函数的逻辑名称：`MyFunction`
+ 在 `code` 属性中指定的函数代码的位置。有关更多信息，请参阅**《AWS Cloud Development Kit (AWS CDK) API Reference》中的 [Handler code](https://docs.aws.amazon.com/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` 目录。此目录在 AWS 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：部署 AWS CDK 堆栈
<a name="lambda-cdk-step-4"></a>

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

   ```
   cdk synth
   ```

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

   ```
   --app is required either in command-line, in cdk.json or in ~/.cdk.json
   ```  
**Example 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.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-deploy.html) 命令：

   ```
   cdk deploy
   ```

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

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

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

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

```
curl -s https://abcd1234.execute-api.us-east-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.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-destroy.html) 命令，删除堆栈和所有关联资源。

```
cdk destroy
```

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

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

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

[在 TypeScript 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-typescript.html)

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

[在 JavaScript 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-javascript.html)

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

[在 Python 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html)

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

[在 Java 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-java.html)

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

[在 C\$1 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-csharp.html)

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

[在 Go 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-go.html)

------