

# 컨테이너 이미지로 Java Lambda 함수 배포
<a name="java-image"></a>

Java Lambda 함수의 컨테이너 이미지를 빌드하는 세 가지 방법이 있습니다.
+ [Java용 AWS 기본 이미지 사용](#java-image-instructions)

  [AWS 기본 이미지](images-create.md#runtimes-images-lp)에는 언어 런타임, Lambda와 함수 코드 간의 상호 작용을 관리하는 런타임 인터페이스 클라이언트 및 로컬 테스트를 위한 런타임 인터페이스 에뮬레이터가 미리 로드되어 있습니다.
+ [AWS OS 전용 기본 이미지 사용](images-create.md#runtimes-images-provided)

  [AWS OS 전용 기본 이미지](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)의 컨테이너 이미지를 생성하는 데 사용됩니다. OS 전용 기본 이미지를 사용하여 [사용자 지정 런타임](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/)을 참조하세요. 효율적인 컨테이너 이미지를 빌드하려면 [Dockerfile 작성 모범 사례](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를 사용합니다. AL2023은 작은 배포 공간과 `glibc`와 같이 업데이트된 라이브러리 버전을 포함하여 Amazon Linux 2에 비해 여러 가지 이점을 제공합니다.

AL2023 기반 이미지는 `microdnf`(`dnf` 심볼릭 링크)를 Amazon Linux 2에서 기본 패키지 관리자인 `yum` 대신 패키지 관리자로 사용합니다. `microdnf`는 `dnf`의 독립 실행형 구현입니다. AL2023 기반 이미지에 포함된 패키지 목록의 경우 [Comparing packages installed on Amazon Linux 2023 Container Images](https://docs.aws.amazon.com/linux/al2023/ug/al2023-container-image-types.html)의 **Minimal Container** 열을 참조하세요. AL2023과 Amazon Linux 2의 차이점에 대한 자세한 내용은 AWS 컴퓨팅 블로그의 [Introducing the Amazon Linux 2023 runtime for AWS Lambda](https://aws.amazon.com/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/)를 참조하세요.

**참고**  
AWS Serverless Application Model(AWS SAM)을 포함하여 AL2023 기반 이미지를 로컬에서 실행하려면 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로 컴파일되도록 미리 구성되어 있으며 AWS SDK for Java에 대한 종속 구성 요소를 포함합니다. 다른 아키타이프 또는 다른 메서드를 사용하여 프로젝트를 생성하는 경우 [Maven용 Java 컴파일러를 구성](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#configure-maven-compiler)하고 [SDK를 종속 구성 요소로 선언](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 사용자를 자동으로 정의합니다. 이는 `USER` 지침이 제공되지 않을 때 `root` 사용자에게 기본 설정이 적용되는 표준 Docker 동작과는 다릅니다.  
**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-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 .
   ```
**참고**  
이 명령은 빌드 머신의 아키텍처에 관계없이 컨테이너가 Lambda 실행 환경과 호환되는지 확인하기 위해 `--platform linux/amd64` 옵션을 지정합니다. 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 정의 환경 변수](configuration-envvars.md#configuration-envvars-runtime)인 `{LAMBDA_TASK_ROOT}`에 복사합니다.
   + `CMD` 인수를 Lambda 함수 핸들러로 설정합니다.

   참고로 Dockerfile 예제에는 [USER 지침](https://docs.docker.com/reference/dockerfile/#user)이 포함되어 있지 않습니다. Lambda에 컨테이너 이미지를 배포할 때 Lambda는 권한이 최소 권한인 기본 Linux 사용자를 자동으로 정의합니다. 이는 `USER` 지침이 제공되지 않을 때 `root` 사용자에게 기본 설정이 적용되는 표준 Docker 동작과는 다릅니다.  
**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-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 .
   ```
**참고**  
이 명령은 빌드 머신의 아키텍처에 관계없이 컨테이너가 Lambda 실행 환경과 호환되는지 확인하기 위해 `--platform linux/amd64` 옵션을 지정합니다. 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 명령 세트 아키텍처를 위한 도커 이미지를 빌드한 경우 `--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>

[OS 전용 기본 이미지](images-create.md#runtimes-images-provided)나 대체 기본 이미지를 사용하는 경우 이미지에 런타임 인터페이스 클라이언트를 포함해야 합니다. 런타임 인터페이스 클라이언트는 Lambda와 함수 코드 간의 상호 작용을 관리하는 [런타임 API](runtimes-api.md)을 확장합니다.

Java용 런타임 인터페이스 클라이언트를 Docker 파일에 설치하거나 프로젝트의 종속 항목으로 설치합니다. 예를 들어, Maven 패키지 관리자를 사용하여 런타임 인터페이스 클라이언트를 설치하려면 `pom.xml` 파일에 다음을 추가하세요.

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

패키지에 대한 자세한 내용은 Maven Central Repository의 [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는 무료로 사용할 수 있는 Open Java Development Kit(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로 설정합니다.
   + Docker 컨테이너가 시작될 때 실행할 모듈로 `ENTRYPOINT`를 설정합니다. 이 경우 모듈은 런타임 인터페이스 클라이언트입니다.
   + `CMD` 인수를 Lambda 함수 핸들러로 설정합니다.

   참고로 Dockerfile 예제에는 [USER 지침](https://docs.docker.com/reference/dockerfile/#user)이 포함되어 있지 않습니다. Lambda에 컨테이너 이미지를 배포할 때 Lambda는 권한이 최소 권한인 기본 Linux 사용자를 자동으로 정의합니다. 이는 `USER` 지침이 제공되지 않을 때 `root` 사용자에게 기본 설정이 적용되는 표준 Docker 동작과는 다릅니다.  
**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-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 .
   ```
**참고**  
이 명령은 빌드 머신의 아키텍처에 관계없이 컨테이너가 Lambda 실행 환경과 호환되는지 확인하기 위해 `--platform linux/amd64` 옵션을 지정합니다. 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`는 Docker 파일의 `CMD` 다음에 오는 `ENTRYPOINT`입니다.

------
#### [ 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 명령 세트 아키텍처를 위한 도커 이미지를 빌드한 경우 `--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
```