AWS環境でのmicronautサーバレスアプリケーションのデプロイ

こんにちは、ココネでビリングシステムエンジニアを担当しているKです。

今回はmicronautアプリケーションをAWS Lambdaに二つの方法でデプロイし、

サーバレスAPIを実際に動かすまでの流れについて書きたいと思います。

今回の環境

開発環境 macOS Monterey 12.3.1
IntelliJ IDEA
Lambda Java 11
Amazon Linux 2 のカスタムランタイム
ビルドツール Gradle 7.3.1
フレームワーク Micronaut 3.0.2
仕様ツール AWS SAM (SAM CLI, version 1.40.0)
AWS Toolkit for JetBrains
Docker 20.10.11

概要

micronautアプリケーションをAWS Lambdaに二つの方法でデプロイします。

 

1.AWS Lambdaにjava11ランタイムでデプロイする。

2.GraalVM native imageを作成しAWS Lambdaにカスタムランタイムでデプロイする。

 

※GraalVM native imageをAWS Lambdaにアップロードし動作させることのメリットは、起動時間を短縮し、メモリ消費量を削減できることです。メモリの使用が制限されている環境や、起動時間が重要な場合に有用です。

事前準備

AWS SAM(Serverless Application Model)を用います。

AWS SAMはサーバーレスアプリケーションを構築するために使用できるオープンソースのフレームワークです。

今回は主にAWS Lambda、API Gatewayに焦点を当てていますが、AWS SAMの構文はLambda 関数、イベントソース、およびその他のリソースの組み合わせであることに注意してください。

 

今回使用しているAWS SAM テンプレートのtemplate.yamlは、AWS SAM CLI の

「Hello World Example – template.yaml(Java11, Gradle)」を元にしています。

 

参考:「AWS SAM CLI のインストール」 https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

( サイトを開いた際エラーが出る場合は別ブラウザーでお試しください )

 

また、統合開発環境のIntelliJ IDEAにAWS Toolkit for JetBrainsのプラグインを導入します。AWS Toolkit for JetBrainsを使うことで、AWSサーバーレスアプリケーションの開発効率を上げることができます。

Micronautアプリケーションの作成

micronautプロジェクトの作成を開始します。

プロジェクトの作成にはMicronaut CLIを用います。

#Step 1 – 開発環境用ディレクトリを作成し、作成したディレクトリに移動

$ mkdir coconeBlog
$ cd coconeBlog

#Step 2 – AWS SAM テンプレートの空ファイルを作成しておく

$ touch template.yaml

#Step 3 – Micronaut コマンド ライン インターフェイスを使用してアプリケーションを作成

$ mn create-app example.micronaut.micronautguide \
--features=aws-lambda,graalvm \
--build=gradle --lang=java
※featuresの部分で、build.gradlelにaws-lambdaとGraalVM native image作成のための依存関係を追加し、ビルドツールにgradleを、言語にjavaを選択しています。

 

IntelliJ IDEAでcoconeBlogディレクトリを開き中身を確認します。

Book.java

package example.micronaut;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Introspected;
import javax.validation.constraints.NotBlank;

@Introspected
public class Book {
    @NonNull
    @NotBlank
    private String name;
	public Book() {
    }
	@NonNull
    public String getName() {
        return name;
    }
	public void setName(@NonNull String name) {
        this.name = name;
    }
}

BookController.java

package example.micronaut;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;import javax.validation.Valid;
import java.util.UUID;@Controller

public class BookController {
  @Post
  public BookSaved save(@Valid @Body Book book) {
    BookSaved bookSaved = new BookSaved();
    bookSaved.setName(book.getName());
    bookSaved.setIsbn(UUID.randomUUID().toString());
    return bookSaved;
  }
}

 

BookSaved.java

package example.micronaut;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Introspected;import javax.validation.constraints.NotBlank;@Introspected

public class BookSaved {
  @NonNull
  @NotBlank
  private String name;

  @NonNull
  @NotBlank
  private String isbn;public BookSaved() {}

  @NonNull
  public String getName() {
    return name;
  }

  public void setName(@NonNull String name) {
    this.name = name;
  }

  @NonNull
  public String getIsbn() {
    return isbn;
  }

  public void setIsbn(@NonNull String isbn) {
    this.isbn = isbn;
  }
}

Micronautの書き方はSpring Bootに近く、Restful API アプリケーションの開発に適しています。

AWS Lambdaにデプロイし、ControllerのパスとAPI GatewayのURLとを紐づけることで、サーバレスアプリケーションとして動作させることができます。

 

AWS SAMテンプレート構文(template.yaml)を用いて、上記ソースをAWSにデプロイします。

CodeUriにはbuild.gradleのあるmicronautguideディレクトリを指定します。

Handlerにはio.micronaut.function.aws.proxy.MicronautLambdaHandlerというMicronautアプリケーション固有のHandlerを指定します。

Runtimeにはjava11を指定します。

API GatewayのPathには(/)を、Methodにはpostメソッドを指定します。

template.yaml


AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app
 
  Sample SAM Template for sam-app
 
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 20
 
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: HelloWorldFunction micronautguide
      Handler: helloworld.App::handleRequest io.micronaut.function.aws.proxy.MicronautLambdaHandler
      Runtime: java11
      Architectures:
        - x86_64
      MemorySize: 512
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          PARAM1: VALUE
          JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 # More info about tiered compilation https://aws.amazon.com/blogs/compute/optimizing-aws-lambda-function-performance-for-java/
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get post
 
Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

AWSへのデプロイ①

AWS Toolkit for JetBrainsを使います。

template.yamlを右クリックし、AWSにデプロイします。

 

 

 

micronaut-lambda-applicationという名前でStackを作りAWSにデプロイしました。

AWSにデプロイしたAPI GatewayとAWS LambdaをAWS マネジメントコンソールで確認します。

 

API Gateway

 

 

 

想定通りにAWSにSAMでデプロイ出来ていることを確認しました。

 

API GatewayのURLからAPIを呼び出し、サーバレスアプリケーション が動作することを確認します。

※API GatewayのURLはAWS マネジメントコンソールのAPI Gatewayのステージ、もしくは

IntelliJ IDEAのAWS ToolkitのOutputsなどから確認することができます。

 

 $ curl -X POST -H "Content-Type: application/json" -d "{\"name\":\"Hello World\"}" \
https://             .amazonaws.com/Prod
→ {"name":"Hello World","isbn":"bfef99fb-e8af-414c-a6cd-54e86aedd319"}% 

 

想定通りのレスポンスが返ってきました。

micronautアプリケーションをAWSにデプロイし、サーバレスAPIを動かすことができました。

AWSへのデプロイ②

次にGraalVM native imageを作成し、AWS Lambdaにデプロイします。

GraalVM native image作成用のコンパイラーは既にmicronautアプリケーションに含まれています。

GraalVM native imageの作成にはDockerが必要です。

(※Docker Desktopの初期設定だとメモリ不足になります。

Preferences-Resourcesでメモリを8GB以上にしておく必要があります。)

 

build.gradleがあるmicronautguideディレクトリに移動し、ネイティブコンパイルします。(5~15分程度かかります。)
#Step 1 – micronautguideディレクトリに移動

$ pwd
→ coconeBlog
$ cd micronautguide/ 

#Step 2 – ネイティブコンパイルの実行

$ ./gradlew buildNativeLambda 

buildディレクトリ内にネイティブコンパイルで作成されたmicronautguide-0.1-lambda.zipが生成され、これをAWS Lambdaにデプロイし、サーバレスAPIを動作させます。

template.yamlの必要な部分を修正します。

CodeUriで直接micronautguide-0.1-lambda.zipを指定します。

Runtimeはjava11ではなく、Amazon Linux 2 のカスタムランタイムを指定します。

template.yaml

 

...
Properties:
CodeUri: HelloWorldFunction micronautguide/build/libs/micronautguide-0.1-lambda.zip
Handler: helloworld.App::handleRequest io.micronaut.function.aws.proxy.MicronautLambdaHandler
Runtime: java11 provided.al2
...

 

 

AWSへのデプロイ①と同様にtemplate.yamlを右クリックし、AWSにデプロイします。

micronaut-lambda-application-nativeという名前で新たにStackを作りAWSにデプロイしました。

AWSにデプロイしたAWS LambdaをAWS マネジメントコンソールで確認します。

Lambda

 

 

想定通りにAWSにSAMでデプロイ出来ていることを確認しました。

API GatewayのURLからAPIを呼び出し、サーバレスアプリケーション が動作することを確認します。

 $ curl -X POST -H "Content-Type: application/json" -d "{\"name\":\"Hello World\"}" \
https://  .amazonaws.com/Prod
→ {"name":"Hello World","isbn":"7cd66125-e879-4ec9-aec9-a126aaf29a38"}% 

想定通りのレスポンスが返ってきました。

GraalVM native imageをAWSにデプロイし、サーバレスAPIを動かすことができました。

 

AWS Lambdaのコードのプロパティでパッケージサイズを比較したところ、

  • 通常のデプロイ→42.9MB
  • GraalVM native imageのデプロイ→15.9MB

と約1 / 3程度まで縮小することができました。

 

サーバレスAPIのレスポンスが返ってくる時間も、GraalVM native imageを用いたものの方が、かなり速くなっていることを確認できます。

終わりに

今回、「AWS環境でのmicronautサーバレスアプリケーションのデプロイ」という内容で、二つの方法でデプロイし、サーバレスAPIを実際に動かすまでについて書かせていただきました。

私たちも最近業務内で初めてサーバレスなシステムを作ることになり、「普段使い慣れているSpring Bootに近い設計ができる」ことと、「起動時間を速くし、メモリ消費量を抑える」という2点に重きを置きmicronautを使うことにしました。

同じような立場の人の導入の手引きになればと思い、この記事を書かせていただきました。

 

micronautやAWS SAMなどの情報は日々更新されていくので、この記事通りにやっても上手くいかないことや、この記事に記載した内容よりもより良い方法もあるかもしれませんので、その点はご了承ください。

少しでも役に立ったと思っていただけたなら幸いです。最後まで読んでいただきありがとうございました。


ココネでは一緒に働く仲間を募集中です。

ご興味のある方は、ぜひこちらの採用特設サイトをご覧ください。

https://www.cocone.co.jp/recruit/contents/

Category

Tag

%d