

# 使用容器镜像部署 Java Lambda 函数
<a name="java-image"></a>

有三种方法可以为 Java Lambda 函数构建容器映像：
+ [使用 Java 的 AWS 基本映像](#java-image-instructions)

  [AWS 基本映像](images-create.md#runtimes-images-lp)会预加载一个语言运行时系统、一个用于管理 Lambda 和函数代码之间交互的运行时系统接口客户端，以及一个用于本地测试的运行时系统接口仿真器。
+ [使用 AWS 仅限操作系统的基础镜像](images-create.md#runtimes-images-provided)

  [AWS 仅限操作系统的运行时系统](https://gallery.ecr.aws/lambda/provided)包含 Amazon Linux 发行版和[运行时系统接口模拟器](https://github.com/aws/aws-lambda-runtime-interface-emulator/)。这些镜像通常用于为编译语言（例如 [Go](go-image.md#go-image-provided) 和 [Rust](lambda-rust.md)）以及 Lambda 未提供基础映像的语言或语言版本（例如 Node.js 19）创建容器镜像。您也可以使用仅限操作系统的基础映像来实施[自定义运行时系统](runtimes-custom.md)。要使映像与 Lambda 兼容，您必须在映像中包含 [Java 的运行时系统接口客户端](#java-image-clients)。
+ [使用非 AWS 基本映像](#java-image-clients)

  您还可以使用其他容器注册表的备用基本映像，例如 Alpine Linux 或 Debian。您还可以使用您的组织创建的自定义映像。要使映像与 Lambda 兼容，您必须在映像中包含 [Java 的运行时系统接口客户端](#java-image-clients)。

**提示**  
要缩短 Lambda 容器函数激活所需的时间，请参阅 Docker 文档中的[使用多阶段构建](https://docs.docker.com/build/building/multi-stage/)。要构建高效的容器映像，请遵循[编写 Dockerfiles 的最佳实践](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)。

此页面介绍了如何为 Lambda 构建、测试和部署容器映像。

**Topics**
+ [Java AWS 基本映像](#java-image-base)
+ [使用 Java 的 AWS 基本映像](#java-image-instructions)
+ [将备用基本映像与运行时系统接口客户端配合使用](#java-image-clients)

## Java AWS 基本映像
<a name="java-image-base"></a>

AWS 为 Java 提供了以下基本镜像：


| 标签 | 运行时 | 操作系统 | Dockerfile | 弃用 | 
| --- | --- | --- | --- | --- | 
| 25 | Java 25 | Amazon Linux 2023 | [GitHub 上的适用于 Java 25 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java25/Dockerfile.java25) |   2029 年 6 月 30 日   | 
| 21 | Java 21 | Amazon Linux 2023 | [GitHub 上的适用于 Java 21 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java21/Dockerfile.java21) |   2029 年 6 月 30 日   | 
| 17 | Java 17 | Amazon Linux 2 | [GitHub 上的适用于 Java 17 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java17/Dockerfile.java17) |   2027 年 6 月 30 日   | 
| 11 | Java 11 | Amazon Linux 2 | [GitHub 上的适用于 Java 11 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java11/Dockerfile.java11) |   2027 年 6 月 30 日   | 
| 8.al2 | Java 8 | Amazon Linux 2 | [GitHub 上的适用于 Java 8 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java8.al2/Dockerfile.java8.al2) |   2027 年 6 月 30 日   | 

Amazon ECR 存储库：[gallery.ecr.aws/lambda/java](https://gallery.ecr.aws/lambda/java)

Java 21 及更高版本的基础映像基于 [Amazon Linux 2023 最小容器映像](https://docs.aws.amazon.com/linux/al2023/ug/minimal-container.html)。早期的基础映像使用 Amazon Linux 2。与 Amazon Linux 2 相比，AL2023 具有多项优势，包括较小的部署占用空间以及 `glibc` 等更新版本的库。

基于 AL2023 的映像使用 `microdnf`（符号链接为 `dnf`）作为软件包管理器，而不是 Amazon Linux 2 中的默认软件包管理器 `yum`。`microdnf` 是 `dnf` 的独立实现。有关基于 AL2023 的映像中已包含软件包的列表，请参阅[比较 Amazon Linux 2023 容器映像上安装的软件包](https://docs.aws.amazon.com/linux/al2023/ug/al2023-container-image-types.html)中的**最小容器**列。有关 AL2023 和 Amazon Linux 2 之间区别的更多信息，请参阅 AWS Compute Blog 上的 [Introducing the Amazon Linux 2023 runtime for AWS Lambda](https://aws.amazon.com/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/)。

**注意**  
要在本地运行基于 AL2023 的映像，包括使用 AWS Serverless Application Model（AWS SAM），您必须使用 Docker 版本 20.10.10 或更高版本。

## 使用 Java 的 AWS 基本映像
<a name="java-image-instructions"></a>

### 先决条件
<a name="java-image-prerequisites"></a>

要完成本节中的步骤，您必须满足以下条件：
+ Java（例如，[Amazon Corretto](https://aws.amazon.com/corretto)）
+ [Apache Maven](https://maven.apache.org/) 或 [Gradle](https://gradle.org/install/)
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [Docker](https://docs.docker.com/get-docker)（最低版本 25.0.0）
+ Docker [buildx 插件](https://github.com/docker/buildx/blob/master/README.md)。

### 从基本映像创建映像
<a name="java-image-create"></a>

------
#### [ Maven ]

1. 运行以下命令，以使用[适用于 Lambda 的原型](https://github.com/aws/aws-sdk-java-v2/tree/master/archetypes/archetype-lambda)创建 Maven 项目。以下参数为必需参数：
   + **服务** – 在 Lambda 函数中使用的 AWS 服务 客户端。有关可用源列表，请参阅 GitHub 上的 [aws-sdk-java-v2/services](https://github.com/aws/aws-sdk-java-v2/tree/master/services)。
   + **区域** – 要在其中创建 Lambda 函数的 AWS 区域。
   + **groupId** – 应用程序的完整程序包命名空间。
   + **artifactId** – 项目名称。这将成为项目的目录的名称。

   在 Linux 和 macOS 中，运行以下命令：

   ```
   mvn -B archetype:generate \
      -DarchetypeGroupId=software.amazon.awssdk \
      -DarchetypeArtifactId=archetype-lambda -Dservice=s3 -Dregion=US_WEST_2 \
      -DgroupId=com.example.myapp \
      -DartifactId=myapp
   ```

   在 PowerShell 中，运行以下命令：

   ```
   mvn -B archetype:generate `
      "-DarchetypeGroupId=software.amazon.awssdk" `
      "-DarchetypeArtifactId=archetype-lambda" "-Dservice=s3" "-Dregion=US_WEST_2" `
      "-DgroupId=com.example.myapp" `
      "-DartifactId=myapp"
   ```

   适用于 Lambda 的 Maven 原型已预配置为使用 Java SE 8 进行编译，并包含对 适用于 Java 的 AWS SDK 的依赖项。如果使用其他原型或其他方法创建项目，则必须[为 Maven 配置 Java 编译器](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#configure-maven-compiler)并[将开发工具包声明为依赖项](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#configure-maven-compiler)。

1. 打开 `myapp/src/main/java/com/example/myapp` 目录，找到 `App.java` 文件。这是适用于 Lambda 函数的代码。您可以使用提供的示例代码进行测试，也可以将其替换为您自己的代码。

1. 导航回项目的根目录，然后创建一个具有以下配置的新 Dockerfile：
   + 将 `FROM` 属性设置为[基本映像的 URI](https://gallery.ecr.aws/lambda/java)。
   + 将 `CMD` 参数设置为 Lambda 函数处理程序。

   请注意，示例 Dockerfile 不包含 [USER 指令](https://docs.docker.com/reference/dockerfile/#user)。当您将容器映像部署到 Lambda 时，Lambda 会自动定义具有最低权限的默认 Linux 用户。这与标准 Docker 行为不同，标准 Docker 在未提供 `USER` 指令时默认为 `root` 用户。  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/lambda/java:21
     
   # Copy function code and runtime dependencies from Maven layout
   COPY target/classes ${LAMBDA_TASK_ROOT}
   COPY target/dependency/* ${LAMBDA_TASK_ROOT}/lib/
       
   # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
   CMD [ "com.example.myapp.App::handleRequest" ]
   ```

1. 编译项目并收集运行时系统依赖项。

   ```
   mvn compile dependency:copy-dependencies -DincludeScope=runtime
   ```

1. 使用 [docker build](https://docs.docker.com/engine/reference/commandline/build/) 命令构建 Docker 映像。以下示例将映像命名为 `docker-image` 并为其提供 `test` [标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。要使您的映像与 Lambda 兼容，您必须使用 `--provenance=false` 选项。

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**注意**  
该命令指定了 `--platform linux/amd64` 选项，可确保无论生成计算机的架构如何，容器始终与 Lambda 执行环境兼容。如果打算使用 ARM64 指令集架构创建 Lambda 函数，请务必将命令更改为使用 `--platform linux/arm64` 选项。

------
#### [ Gradle ]

1. 为项目创建一个目录，然后切换到该目录。

   ```
   mkdir example
   cd example
   ```

1. 运行以下命令来让 Gradle 在环境中的 `example` 目录中生成新的 Java 应用程序项目。在**选择构建脚本 DSL** 中，选择 **2: Groovy**。

   ```
   gradle init --type java-application
   ```

1. 打开 `/example/app/src/main/java/example` 目录，找到 `App.java` 文件。这是适用于 Lambda 函数的代码。您可以使用以下示例代码进行测试，也可以将其替换为您自己的代码。  
**Example App.java**  

   ```
   package com.example;
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   public class App implements RequestHandler<Object, String> {
       public String handleRequest(Object input, Context context) {
           return "Hello world!";
       }
   }
   ```

1. 打开 `build.gradle` 文件。如果您使用的是上一步中的示例函数代码，请将 `build.gradle` 的内容替换为以下内容。如果您使用的是自己的函数代码，请根据需要修改 `build.gradle` 文件。  
**Example build.gradle (Groovy DSL)**  

   ```
   plugins {
     id 'java'
   }
   group 'com.example'
   version '1.0-SNAPSHOT'
   sourceCompatibility = 1.8
   repositories {
     mavenCentral()
   }
   dependencies {
     implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
   }
   jar {
     manifest {
         attributes 'Main-Class': 'com.example.App'
     }
   }
   ```

1. 第 2 步中的 `gradle init` 命令还在 `app/test` 目录中生成了一个虚拟测试用例。在本教程中，请删除 `/test` 目录，从而跳过运行测试。

1. 构建项目。

   ```
   gradle build
   ```

1. 在项目的根目录 (`/example`) 中，创建一个具有以下配置的 Dockerfile：
   + 将 `FROM` 属性设置为[基本映像的 URI](https://gallery.ecr.aws/lambda/java)。
   + 使用 COPY 命令将函数代码和运行时系统依赖项复制到 `{LAMBDA_TASK_ROOT}`，此为 [Lambda 定义的环境变量](configuration-envvars.md#configuration-envvars-runtime)。
   + 将 `CMD` 参数设置为 Lambda 函数处理程序。

   请注意，示例 Dockerfile 不包含 [USER 指令](https://docs.docker.com/reference/dockerfile/#user)。当您将容器映像部署到 Lambda 时，Lambda 会自动定义具有最低权限的默认 Linux 用户。这与标准 Docker 行为不同，标准 Docker 在未提供 `USER` 指令时默认为 `root` 用户。  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/lambda/java:21
     
   # Copy function code and runtime dependencies from Gradle layout
   COPY app/build/classes/java/main ${LAMBDA_TASK_ROOT}
     
   # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
   CMD [ "com.example.App::handleRequest" ]
   ```

1. 使用 [docker build](https://docs.docker.com/engine/reference/commandline/build/) 命令构建 Docker 映像。以下示例将映像命名为 `docker-image` 并为其提供 `test` [标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。要使您的映像与 Lambda 兼容，您必须使用 `--provenance=false` 选项。

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**注意**  
该命令指定了 `--platform linux/amd64` 选项，可确保无论生成计算机的架构如何，容器始终与 Lambda 执行环境兼容。如果打算使用 ARM64 指令集架构创建 Lambda 函数，请务必将命令更改为使用 `--platform linux/arm64` 选项。

------

### （可选）在本地测试映像
<a name="java-image-test"></a>

1. 使用 **docker run** 命令启动 Docker 映像。在此示例中，`docker-image` 是映像名称，`test` 是标签。

   ```
   docker run --platform linux/amd64 -p 9000:8080 docker-image:test
   ```

   此命令会将映像作为容器运行，并在 `localhost:9000/2015-03-31/functions/function/invocations` 创建本地端点。
**注意**  
如果为 ARM64 指令集架构创建 Docker 映像，请务必使用 `--platform linux/arm64` 选项，而不是 `--platform linux/amd64` 选项。

1. 在新的终端窗口中，将事件发布到本地端点。

------
#### [ Linux/macOS ]

   在 Linux 和 macOS 中，运行以下 `curl` 命令：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
   ```

------
#### [ PowerShell ]

   在 PowerShell 中，运行以下 `Invoke-WebRequest` 命令：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
   ```

------

1. 获取容器 ID。

   ```
   docker ps
   ```

1. 使用 [docker kill](https://docs.docker.com/engine/reference/commandline/kill/) 命令停止容器。在此命令中，将 `3766c4ab331c` 替换为上一步中的容器 ID。

   ```
   docker kill 3766c4ab331c
   ```

### 部署映像
<a name="java-image-deploy"></a>

**将映像上传到 Amazon ECR 并创建 Lambda 函数**

1. 运行 [get-login-password](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html) 命令，以针对 Amazon ECR 注册表进行 Docker CLI 身份验证。
   + 将 `--region` 值设置为要在其中创建 Amazon ECR 存储库的 AWS 区域。
   + 将 `111122223333` 替换为您的 AWS 账户 ID。

   ```
   aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
   ```

1. 使用 [create-repository](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/create-repository.html) 命令在 Amazon ECR 中创建存储库。

   ```
   aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
   ```
**注意**  
Amazon ECR 存储库必须与 Lambda 函数位于同一 AWS 区域 内。

   如果成功，您将会看到如下响应：

   ```
   {
       "repository": {
           "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
           "registryId": "111122223333",
           "repositoryName": "hello-world",
           "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
           "createdAt": "2023-03-09T10:39:01+00:00",
           "imageTagMutability": "MUTABLE",
           "imageScanningConfiguration": {
               "scanOnPush": true
           },
           "encryptionConfiguration": {
               "encryptionType": "AES256"
           }
       }
   }
   ```

1. 从上一步的输出中复制 `repositoryUri`。

1. 运行 [docker tag](https://docs.docker.com/engine/reference/commandline/tag/) 命令，将本地映像作为最新版本标记到 Amazon ECR 存储库中。在此命令中：
   + `docker-image:test` 是 Docker 映像的名称和[标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。这是您在 `docker build` 命令中指定的映像名称和标签。
   + 将 `<ECRrepositoryUri>` 替换为复制的 `repositoryUri`。确保 URI 末尾包含 `:latest`。

   ```
   docker tag docker-image:test <ECRrepositoryUri>:latest
   ```

   示例：

   ```
   docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 运行 [docker push](https://docs.docker.com/engine/reference/commandline/push/) 命令，以将本地映像部署到 Amazon ECR 存储库。确保存储库 URI 末尾包含 `:latest`。

   ```
   docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 如果您还没有函数的执行角色，请[创建执行角色](lambda-intro-execution-role.md#permissions-executionrole-api)。在下一步中，您需要提供角色的 Amazon 资源名称（ARN）。

1. 创建 Lambda 函数。对于 `ImageUri`，指定之前的存储库 URI。确保 URI 末尾包含 `:latest`。

   ```
   aws lambda create-function \
     --function-name hello-world \
     --package-type Image \
     --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
     --role arn:aws:iam::111122223333:role/lambda-ex
   ```
**注意**  
只要映像与 Lambda 函数位于同一区域内，您就可以使用其他 AWS 账户中的映像创建函数。有关更多信息，请参阅 [Amazon ECR 跨账户权限](images-create.md#configuration-images-xaccount-permissions)。

1. 调用函数。

   ```
   aws lambda invoke --function-name hello-world response.json
   ```

   应出现如下响应：

   ```
   {
     "ExecutedVersion": "$LATEST", 
     "StatusCode": 200
   }
   ```

1. 要查看函数的输出，请检查 `response.json` 文件。

要更新函数代码，您必须再次构建映像，将新映像上传到 Amazon ECR 存储库，然后使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令将映像部署到 Lambda 函数。

Lambda 会将映像标签解析为特定的映像摘要。这意味着，如果您将用于部署函数的映像标签指向 Amazon ECR 中的新映像，则 Lambda 不会自动更新该函数以使用新映像。

要将新映像部署到相同的 Lambda 函数，即使 Amazon ECR 中的映像标签保持不变，也必须使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令。在以下示例中，`--publish` 选项使用更新的容器映像创建函数的新版本。

```
aws lambda update-function-code \
  --function-name hello-world \
  --image-uri 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
  --publish
```

## 将备用基本映像与运行时系统接口客户端配合使用
<a name="java-image-clients"></a>

如果使用[仅限操作系统的基础映像](images-create.md#runtimes-images-provided)或者备用基础映像，则必须在映像中包括运行时系统接口客户端。运行时系统接口客户端可扩展 [运行时 API](runtimes-api.md)，用于管理 Lambda 和函数代码之间的交互。

在 Dockerfile 中安装 Java 的运行时系统接口客户端，或作为项目中的依赖项安装。例如，要使用 Maven 程序包管理器安装运行时系统接口客户端，请将以下内容添加到您的 `pom.xml` 文件中：

```
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-runtime-interface-client</artifactId>
    <version>2.3.2</version>
</dependency>
```

有关程序包的详细信息，请参阅 Maven Central 存储库中的 [AWS Lambda Java 运行时系统接口客户端](https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-runtime-interface-client)。您还可以在 [AWS Lambda Java 支持库](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-runtime-interface-client) GitHub 存储库中查看运行时系统接口客户端源代码。

以下示例演示了如何使用 [Amazon Corretto 映像](https://gallery.ecr.aws/amazoncorretto/amazoncorretto)为 Java 构建容器映像。Amazon Corretto 是开放 Java 开发工具包（OpenJDK）的免费、多平台、生产就绪型分发版。Maven 项目将运行时系统接口客户端作为依赖项包括在内。

### 先决条件
<a name="java-alt-prerequisites"></a>

要完成本节中的步骤，您必须满足以下条件：
+ Java（例如，[Amazon Corretto](https://aws.amazon.com/corretto)）
+ [Apache Maven](https://maven.apache.org/)
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [Docker](https://docs.docker.com/get-docker)（最低版本 25.0.0）
+ Docker [buildx 插件](https://github.com/docker/buildx/blob/master/README.md)。

### 从备用基本映像创建映像
<a name="java-alt-create"></a>

1. 创建 Maven 项目。以下参数为必需参数：
   + **groupId** – 应用程序的完整程序包命名空间。
   + **artifactId** – 项目名称。这将成为项目的目录的名称。

------
#### [ Linux/macOS ]

   ```
   mvn -B archetype:generate \
      -DarchetypeArtifactId=maven-archetype-quickstart \
      -DgroupId=example \
      -DartifactId=myapp \
      -DinteractiveMode=false
   ```

------
#### [ PowerShell ]

   ```
   mvn -B archetype:generate `
      -DarchetypeArtifactId=maven-archetype-quickstart `
      -DgroupId=example `
      -DartifactId=myapp `
      -DinteractiveMode=false
   ```

------

1. 打开项目目录。

   ```
   cd myapp
   ```

1. 打开 `pom.xml` 文件并将相应内容替换为以下内容。此文件将 [aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-runtime-interface-client) 作为依赖项包括在内。或者，您可以在 Dockerfile 中安装运行时系统接口客户端。但是，最简单的方法是将库作为依赖项包含在内。

   ```
   <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>example</groupId>
     <artifactId>hello-lambda</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>hello-lambda</name>
     <url>http://maven.apache.org</url>
     <properties>
       <maven.compiler.source>1.8</maven.compiler.source>
       <maven.compiler.target>1.8</maven.compiler.target>
     </properties>
     <dependencies>
       <dependency>
         <groupId>com.amazonaws</groupId>
         <artifactId>aws-lambda-java-runtime-interface-client</artifactId>
         <version>2.3.2</version>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-dependency-plugin</artifactId>
           <version>3.1.2</version>
           <executions>
             <execution>
               <id>copy-dependencies</id>
               <phase>package</phase>
               <goals>
                 <goal>copy-dependencies</goal>
               </goals>
             </execution>
           </executions>
         </plugin>
       </plugins>
     </build>
   </project>
   ```

1. 打开 `myapp/src/main/java/com/example/myapp` 目录，找到 `App.java` 文件。这是适用于 Lambda 函数的代码。使用以下内容替换相应代码。  
**Example 函数处理程序**  

   ```
   package example;
   
   public class App {
       public static String sayHello() {
           return "Hello world!";
       }
   }
   ```

1. 第 1 步中的 `mvn -B archetype:generate` 命令还会在 `src/test` 目录中生成了一个虚拟测试用例。在本教程中，请完整删除生成的 `/test` 目录，从而跳过运行测试。

1. 导航回项目的根目录，然后创建一个新 Dockerfile。以下示例 Dockerfile 使用 [Amazon Corretto 映像](https://gallery.ecr.aws/amazoncorretto/amazoncorretto)。Amazon Corretto 是 OpenJDK 的免费、多平台、生产就绪型分发版。
   + 将 `FROM` 属性设置为基本映像的 URI。
   + 将 `ENTRYPOINT` 设置为您希望 Docker 容器在启动时运行的模块。在本例中，模块为运行时系统接口客户端。
   + 将 `CMD` 参数设置为 Lambda 函数处理程序。

   请注意，示例 Dockerfile 不包含 [USER 指令](https://docs.docker.com/reference/dockerfile/#user)。当您将容器映像部署到 Lambda 时，Lambda 会自动定义具有最低权限的默认 Linux 用户。这与标准 Docker 行为不同，标准 Docker 在未提供 `USER` 指令时默认为 `root` 用户。  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/amazoncorretto/amazoncorretto:21 as base
   
   # Configure the build environment
   FROM base as build
   RUN yum install -y maven
   WORKDIR /src
   
   # Cache and copy dependencies
   ADD pom.xml .
   RUN mvn dependency:go-offline dependency:copy-dependencies
   
   # Compile the function
   ADD . .
   RUN mvn package 
   
   # Copy the function artifact and dependencies onto a clean base
   FROM base
   WORKDIR /function
   
   COPY --from=build /src/target/dependency/*.jar ./
   COPY --from=build /src/target/*.jar ./
   
   # Set runtime interface client as default command for the container runtime
   ENTRYPOINT [ "/usr/bin/java", "-cp", "./*", "com.amazonaws.services.lambda.runtime.api.client.AWSLambda" ]
   # Pass the name of the function handler as an argument to the runtime
   CMD [ "example.App::sayHello" ]
   ```

1. 使用 [docker build](https://docs.docker.com/engine/reference/commandline/build/) 命令构建 Docker 映像。以下示例将映像命名为 `docker-image` 并为其提供 `test` [标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。要使您的映像与 Lambda 兼容，您必须使用 `--provenance=false` 选项。

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**注意**  
该命令指定了 `--platform linux/amd64` 选项，可确保无论生成计算机的架构如何，容器始终与 Lambda 执行环境兼容。如果打算使用 ARM64 指令集架构创建 Lambda 函数，请务必将命令更改为使用 `--platform linux/arm64` 选项。

### （可选）在本地测试映像
<a name="java-alt-test"></a>

使用[运行时系统接口仿真器](https://github.com/aws/aws-lambda-runtime-interface-emulator/)在本地测试映像。您可以[将仿真器构建到映像中](https://github.com/aws/aws-lambda-runtime-interface-emulator/?tab=readme-ov-file#build-rie-into-your-base-image)，也可以使用以下程序将其安装在本地计算机上。

**在本地计算机上安装并运行运行时系统接口仿真器**

1. 从项目目录中，运行以下命令以从 GitHub 下载运行时系统接口仿真器（x86-64 架构）并将其安装在本地计算机上。

------
#### [ Linux/macOS ]

   ```
   mkdir -p ~/.aws-lambda-rie && \
       curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \
       chmod +x ~/.aws-lambda-rie/aws-lambda-rie
   ```

   要安装 arm64 仿真器，请将上一条命令中的 GitHub 存储库 URL 替换为以下内容：

   ```
   https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
   ```

------
#### [ PowerShell ]

   ```
   $dirPath = "$HOME\.aws-lambda-rie"
   if (-not (Test-Path $dirPath)) {
       New-Item -Path $dirPath -ItemType Directory
   }
         
   $downloadLink = "https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie"
   $destinationPath = "$HOME\.aws-lambda-rie\aws-lambda-rie"
   Invoke-WebRequest -Uri $downloadLink -OutFile $destinationPath
   ```

   要安装 arm64 模拟器，请将 `$downloadLink` 替换为以下内容：

   ```
   https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
   ```

------

1. 使用 **docker run** 命令启动 Docker 映像。请注意以下几点：
   + `docker-image` 是映像名称，`test` 是标签。
   + `/usr/bin/java -cp './*' com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello` 是 `ENTRYPOINT`，后跟您 Dockerfile 中的 `CMD`。

------
#### [ Linux/macOS ]

   ```
   docker run --platform linux/amd64 -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
       --entrypoint /aws-lambda/aws-lambda-rie \
       docker-image:test \
           /usr/bin/java -cp './*' com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello
   ```

------
#### [ PowerShell ]

   ```
   docker run --platform linux/amd64 -d -v "$HOME\.aws-lambda-rie:/aws-lambda" -p 9000:8080 `
   --entrypoint /aws-lambda/aws-lambda-rie `
   docker-image:test `
       /usr/bin/java -cp './*' com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello
   ```

------

   此命令会将映像作为容器运行，并在 `localhost:9000/2015-03-31/functions/function/invocations` 创建本地端点。
**注意**  
如果为 ARM64 指令集架构创建 Docker 映像，请务必使用 `--platform linux/arm64` 选项，而不是 `--platform linux/amd64` 选项。

1. 将事件发布到本地端点。

------
#### [ Linux/macOS ]

   在 Linux 和 macOS 中，运行以下 `curl` 命令：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
   ```

------
#### [ PowerShell ]

   在 PowerShell 中，运行以下 `Invoke-WebRequest` 命令：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
   ```

------

1. 获取容器 ID。

   ```
   docker ps
   ```

1. 使用 [docker kill](https://docs.docker.com/engine/reference/commandline/kill/) 命令停止容器。在此命令中，将 `3766c4ab331c` 替换为上一步中的容器 ID。

   ```
   docker kill 3766c4ab331c
   ```

### 部署映像
<a name="java-alt-deploy"></a>

**将映像上传到 Amazon ECR 并创建 Lambda 函数**

1. 运行 [get-login-password](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html) 命令，以针对 Amazon ECR 注册表进行 Docker CLI 身份验证。
   + 将 `--region` 值设置为要在其中创建 Amazon ECR 存储库的 AWS 区域。
   + 将 `111122223333` 替换为您的 AWS 账户 ID。

   ```
   aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
   ```

1. 使用 [create-repository](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/create-repository.html) 命令在 Amazon ECR 中创建存储库。

   ```
   aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
   ```
**注意**  
Amazon ECR 存储库必须与 Lambda 函数位于同一 AWS 区域 内。

   如果成功，您将会看到如下响应：

   ```
   {
       "repository": {
           "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
           "registryId": "111122223333",
           "repositoryName": "hello-world",
           "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
           "createdAt": "2023-03-09T10:39:01+00:00",
           "imageTagMutability": "MUTABLE",
           "imageScanningConfiguration": {
               "scanOnPush": true
           },
           "encryptionConfiguration": {
               "encryptionType": "AES256"
           }
       }
   }
   ```

1. 从上一步的输出中复制 `repositoryUri`。

1. 运行 [docker tag](https://docs.docker.com/engine/reference/commandline/tag/) 命令，将本地映像作为最新版本标记到 Amazon ECR 存储库中。在此命令中：
   + `docker-image:test` 是 Docker 映像的名称和[标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。这是您在 `docker build` 命令中指定的映像名称和标签。
   + 将 `<ECRrepositoryUri>` 替换为复制的 `repositoryUri`。确保 URI 末尾包含 `:latest`。

   ```
   docker tag docker-image:test <ECRrepositoryUri>:latest
   ```

   示例：

   ```
   docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 运行 [docker push](https://docs.docker.com/engine/reference/commandline/push/) 命令，以将本地映像部署到 Amazon ECR 存储库。确保存储库 URI 末尾包含 `:latest`。

   ```
   docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 如果您还没有函数的执行角色，请[创建执行角色](lambda-intro-execution-role.md#permissions-executionrole-api)。在下一步中，您需要提供角色的 Amazon 资源名称（ARN）。

1. 创建 Lambda 函数。对于 `ImageUri`，指定之前的存储库 URI。确保 URI 末尾包含 `:latest`。

   ```
   aws lambda create-function \
     --function-name hello-world \
     --package-type Image \
     --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
     --role arn:aws:iam::111122223333:role/lambda-ex
   ```
**注意**  
只要映像与 Lambda 函数位于同一区域内，您就可以使用其他 AWS 账户中的映像创建函数。有关更多信息，请参阅 [Amazon ECR 跨账户权限](images-create.md#configuration-images-xaccount-permissions)。

1. 调用函数。

   ```
   aws lambda invoke --function-name hello-world response.json
   ```

   应出现如下响应：

   ```
   {
     "ExecutedVersion": "$LATEST", 
     "StatusCode": 200
   }
   ```

1. 要查看函数的输出，请检查 `response.json` 文件。

要更新函数代码，您必须再次构建映像，将新映像上传到 Amazon ECR 存储库，然后使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令将映像部署到 Lambda 函数。

Lambda 会将映像标签解析为特定的映像摘要。这意味着，如果您将用于部署函数的映像标签指向 Amazon ECR 中的新映像，则 Lambda 不会自动更新该函数以使用新映像。

要将新映像部署到相同的 Lambda 函数，即使 Amazon ECR 中的映像标签保持不变，也必须使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令。在以下示例中，`--publish` 选项使用更新的容器映像创建函数的新版本。

```
aws lambda update-function-code \
  --function-name hello-world \
  --image-uri 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
  --publish
```