my icon

jagijagijag1 tech note

jagijagijag1

GraalVMのネイティブイメージをAWS Lambdaで動かしてみたかったので,Micronautを触ってみたログとつまづきメモ.

目次

環境

  • OS: macOS Big Sur 11.3
  • Java: openjdk version “15.0.2” 2021-01-19
  • Homebrew 3.1.5
  • aws-cli/2.2.1
  • Docker 20.10.5

準備

Micronaut CLIインストール

  • Homebrewでインストール
$ brew install --cask micronaut-projects/tap/micronaut
  • 続いてmn --versionコマンドを実行しようとすると,"mn"は,開発元を検証できないため開けません.というメッセージが出る
  • 実行許可するため,システム環境設定セキュリティとプライバシー内の一般を開き,ウインドウ下部の「このまま許可」を選ぶ
  • 再度mn --versionコマンドを実行すると,先程と異なり"mn"は,開発元を検証できません.開いてもよろしいですか?というメッセージがでるので,「開く」を選ぶ
  • するとバージョン情報が出力される
$ mn --version
Micronaut Version: 2.5.0

SAM CLIインストール

  • Lambda関数のローカルテストとデプロイに利用
$ brew tap aws/tap
$ brew install aws-sam-cli
$ sam --version
SAM CLI, version 1.23.0

AWS提供Javaランタイムを用いるLambda関数

プロジェクト作成

  • 適当な場所で下記コマンドを実行し,雛形生成
$ mn create-function-app example.micronaut.complete --lang kotlin --features aws-lambda
  • 以下,生成されたcompleteディレクトリで作業
  • (FATな)JARを生成
    • 成功するとcomplete/build/libs/complete-0.1-all.jarが出力される
$ ./gradlew shadowJar

ローカル実行でテスト

  • sam templateを作成
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Micronaut Lambda function.
Resources:
  function:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: build/libs/complete-0.1-all.jar
      Handler: example.micronaut.BookRequestHandler
      Runtime: java11
      Description: Micronaut function
      MemorySize: 512
      Timeout: 15
      # Function's execution role
      Policies:
        - AWSLambdaBasicExecutionRole
        - AWSLambda_ReadOnlyAccess
        - AWSXrayWriteOnlyAccess
        - AWSLambdaVPCAccessExecutionRole
      Tracing: Active
  • テスト用データを作成
{
  "name": "Building Microservices"
}
  • 上記テンプレートとデータを用い,Lambda関数をローカル実行
    • 要Docker
    • 最後の部分のレスポンスが想定どおりであることを確認
$ saml local invoke -e testevent.json
Invoking example.micronaut.BookRequestHandler (java11)
Decompressing /Users/ryo/work/java/java-sandbox/mn-lambda/complete/build/libs/complete-0.1-all.jar
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-java11:rapid-1.23.0.

Mounting /private/var/folders/_7/m8bf5d2n6cj5xg9ptdvsry5m0000gn/T/tmpwr1pmk59 as /var/task:ro,delegated inside runtime container
START RequestId: 4e0de115-ae68-4e8d-bdc4-58e7805eb69a Version: $LATEST
04:44:33.933 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [ec2, cloud, function, lambda]
END RequestId: 4e0de115-ae68-4e8d-bdc4-58e7805eb69a
REPORT RequestId: 4e0de115-ae68-4e8d-bdc4-58e7805eb69a  Init Duration: 0.64 ms  Duration: 5730.52 ms    Billed Duration: 5800 ms        Memory Size: 512 MB  Max Memory Used: 512 MB
{"name":"Building Microservices","isbn":"b3b2e286-811e-437e-b85d-04f7e564384a"}

デプロイしてテスト

  • sam deployする (事前にjarアップロード用のS3バケットを用意)
$ sam deploy --stack-name mn-lambda-test --capabilities CAPABILITY_IAM --s3-bucket <your-bucket-name>
  • デプロイ成功したら,AWS CLIから実行してみる
$ aws lambda invoke --function-name <your-function-name> --payload '{"name": "Building Microservices"}' response.json --cli-binary-format raw-in-base64-out
  • 実行が成功すると,response.jsonファイルが出力される
{"name":"Building Microservices","isbn":"dfc835cc-8be7-4f78-86db-39a80ab40151"}
  • AWSマネジメントコンソールなどからトレースを確認可能
    • 今回の場合,コールドスタートの初回起動に約3秒かかった

f83084f6.png

ネイティブコンパイル&カスタムランタイムを用いるLambda関数

  • 今度はGraalVMのネイティブコンパイル版をLambdaで動かしてみる

プロジェクト作成

  • 適当なディレクトリで,graalvm機能対応の雛形を生成
$ mn create-function-app example.micronaut.complete --lang kotlin --features=aws-lambda-custom-runtime,graalvm
  • 以下,生成されたcompleteディレクトリで作業
  • 下記コマンドでネイティブコンパイルしたイメージを生成
    • 要Docker
    • 成功するとbuild/libs/complete-0.1-lambda.zipが出力される
$ ./gradlew buildNativeLambda -Pmicronaut.runtime=lambda
  • 初回実行時,Task :dockerBuildNativeで失敗
    • エラーメッセージは以下

      Execution failed for task ‘:dockerBuildNative’. Could not build image: The command ‘/bin/sh -c native-image -H:Class=example.micronaut.BookLambdaRuntime -H:Name=application –no-fallback -cp /home/app/libs/*.jar:/home/app/resources:/home/app/application.jar’ returned a non-zero code: 137

    • 下記によると,メモリ不足によるOutOfMemoryっぽい
    • Docker Desktopを利用しているので,Preferences-Resourcesでメモリ割り当てを増やして解決
    • メモリ8GBのマシンだとコンパイルに10分強かかった…

ローカル実行でテスト

  • sam templateを作成
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Micronaut Lambda function with GraalVM native image.
Resources:
  function:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: build/libs/complete-0.1-lambda.zip
      Handler: note.used
      Runtime: provided
      Description: Micronaut native image function
      MemorySize: 512
      Timeout: 15
      # Function's execution role
      Policies:
        - AWSLambdaBasicExecutionRole
        - AWSLambda_ReadOnlyAccess
        - AWSXrayWriteOnlyAccess
        - AWSLambdaVPCAccessExecutionRole
      Tracing: Active
  • テスト用データを作成
    • 雛形のカスタムランタイムがAPI Gatewayからのリクエストを受ける用に作られているため,API Gatewayリクエスト形式のJSONを準備
    • Lambdaのテストで提供されているapigateway-aws-proxyテンプレートの"body"要素に書籍名をセット
テスト用データが長いので折りたたみ(詳細はここを開いてください)
{
  "body": "{\"name\": \"Building Microservices\"}",
  "resource": "/{proxy+}",
  "path": "/path/to/resource",
  "httpMethod": "POST",
  "isBase64Encoded": true,
  "queryStringParameters": {
    "foo": "bar"
  },
  "multiValueQueryStringParameters": {
    "foo": [
      "bar"
    ]
  },
  "pathParameters": {
    "proxy": "/path/to/resource"
  },
  "stageVariables": {
    "baz": "qux"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "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-Country": "US",
    "Host": "1234567890.execute-api.ap-northeast-1.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Custom User Agent String",
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "Accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
    ],
    "Accept-Encoding": [
      "gzip, deflate, sdch"
    ],
    "Accept-Language": [
      "en-US,en;q=0.8"
    ],
    "Cache-Control": [
      "max-age=0"
    ],
    "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-Country": [
      "US"
    ],
    "Host": [
      "0123456789.execute-api.ap-northeast-1.amazonaws.com"
    ],
    "Upgrade-Insecure-Requests": [
      "1"
    ],
    "User-Agent": [
      "Custom User Agent String"
    ],
    "Via": [
      "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
    ],
    "X-Amz-Cf-Id": [
      "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
    ],
    "X-Forwarded-For": [
      "127.0.0.1, 127.0.0.2"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ]
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "123456",
    "stage": "prod",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "requestTime": "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch": 1428582896000,
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "accessKey": null,
      "sourceIp": "127.0.0.1",
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Custom User Agent String",
      "user": null
    },
    "path": "/prod/path/to/resource",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "apiId": "1234567890",
    "protocol": "HTTP/1.1"
  }
}
  • 上記テンプレートとデータを用い,Lambda関数をローカル実行
    • 要Docker
    • 最後の部分のレスポンスが想定どおりであることを確認
$ saml local invoke -e testevent.json
Invoking note.used (provided)
Decompressing /Users/ryo/work/java/java-sandbox/mn-lambda-graalvm/complete/build/libs/complete-0.1-lambda.zip
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-provided:rapid-1.23.0.

Mounting /private/var/folders/_7/m8bf5d2n6cj5xg9ptdvsry5m0000gn/T/tmpz4qxuo5t as /var/task:ro,delegated inside runtime container
START RequestId: 9724d856-a797-485f-b665-2c6e40f918d9 Version: $LATEST
05:23:48.981 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [ec2, cloud, function, lambda]
END RequestId: 9724d856-a797-485f-b665-2c6e40f918d9
REPORT RequestId: 9724d856-a797-485f-b665-2c6e40f918d9  Init Duration: 0.23 ms  Duration: 455.86 ms     Billed Duration: 500 ms Memory Size: 512 MB     Max Memory Used: 512 MB
{"statusCode":200,"headers":{"Content-Type":"application/json"},"body":"{\"name\":\"Building Microservices\",\"isbn\":\"0915193e-21b1-4f7e-bf0b-a76b0cff22b7\"}"}

デプロイしてテスト

  • sam deployする (事前にjarアップロード用のS3バケットを用意)
$ sam deploy --stack-name mn-lambda-graalvm-test --capabilities CAPABILITY_IAM --s3-bucket <your-bucket-name>
  • デプロイ成功したら,AWS CLIから実行してみる
$ aws lambda invoke --function-name <your-function-name> --payload "$(cat testevent.json)" response.json --cli-binary-format raw-in-base64-out
  • 実行が成功すると,response.jsonファイルが出力される
{"statusCode":200,"headers":{"Content-Type":"application/json"},"body":"{\"name\":\"Building Microservices\",\"isbn\":\"55e9a092-3b03-4957-9459-96b8aef17962\"}"}
  • AWSマネジメントコンソールなどからトレースを確認可能
    • ネイティブイメージにより,コールドスタートの初回起動が約0.5秒に短縮された

1909d61.png

Say Something

Comments

Recent Posts

Categories

Blog PVs

Powered by Pixela