LoginSignup
5
5

More than 5 years have passed since last update.

SpringCloudFunctionとAWS Lambda/Lambda Layers/ServerlessFrameworkを試す

Last updated at Posted at 2018-12-20

この記事はaratana advent calendar 23日目の記事です。

動機

Serverless(AWS Lambda)+Java

AWS Lambdaは、アクセスされる度実行されるため、初回起動時間が短いnode.jsやpythonが一般的かと思いますが、関数の数が増えてくる、結構な規模のアプリケーションになってくると今度は型がない言語であることが辛くなってきます。

起動が遅い問題については、serverless-plugin-warmupなどで解決可能なので、Javaなどの静的型付け言語は現実的な解となりうるのではないかと思いました。

Scalaなんかもいい選択肢ではありますが、やはり僕のなかではSpringFramework(SpringBoot)の使い勝手とドメイン駆動設計に即したアーキテクチャ(@Service/@Repositoryのようなアノテーションなど)がわかりやすく、使いやすいと思っています。

Lambda Layers

しかし、JavaをLambdaで使用する場合の難点として、zipで50MB、展開したファイルが250MB制限というのがあります。(https://docs.aws.amazon.com/lambda/latest/dg/limits.html)

Javaで特にSpringCloudFunctionを使おうとすると50MB近く行くのでなかなか厳しい。

そこで今年re:inventで発表されたLambda Layersです。

プロダクトのコードとライブラリのコードを分けられる、さらに分割できるので、50MB制限を気にせず開発できます。

東京リージョンでも利用可能で、Serverless Frameworkも既に対応済みです。

Spring Cloud Function

ややマニアックなプロダクトですが、開発は活発です。関数ベースのSpringBootで、AWS LambdaやAzure、Apache OpenWhiskにも対応しています。
https://github.com/spring-cloud/spring-cloud-function

特徴としては、

  • SpringBootのエコシステムがそのまま利用できる。
  • 関数をRestAPIサーバー的な感じで実行できる。
  • RestAPIサーバー的な感じで実行するときは、ライブラリを実行時に取得するthin jarが作れる
  • AWS LambdaやAzure、Apache OpenWhiskのアダプターが使える

というところでしょうか。特にRestAPIとして実行できるので、わざわざ(そして使いづらい)Lambdaのローカル実行をからデバックしなくてもOKなところが魅力的です。

試す

ということで全体のsampleソースはこちら。
https://github.com/YasuhiroKimesawa/spring_cloud_function_lambda_layers_sample

ポイントを上げておきます。

gradle

SpringCloudFunctionではthin jar実行時にライブラリを取得するのため、mavenが基本となっていますが、今回は以下の目的のためにgradleを使っています。

  • LambdaLayerを使用するため、ライブラリのプロジェクトとプロダクトのプロジェクトを分けるので、そもそもthin jarがいらない。
  • mavenではマルチプロジェクトがあくまでモジュールとしての扱い、かつ親子関係が厳密で、ライブラリのプロジェクトとプロダクトのプロジェクトを分けてビルドするのが難しかった(何か方法があるかもしれないですが、解決できずgradleを利用しました)

SpringCloudFunctionのバージョン

試したのは2.0.0.RC2。RC版なので、まだそのまま持ってきても足りない依存関係などが結構あったので、gradle dependencies タスクとFrameworkのレポジトリを比較しながら足りないものを補う必要がありました。

Lambda Layers

Lambda Layers用のサブプロジェクトを作成しています。(該当コード)

Lamdba Layersにはjarライブラリ群をラップしたzipを上げないといけないので、gradleにzipAppタスクを追加しています。(該当コード)

プロダクト用のサブプロジェクトからはこのライブラリ用のサブプロジェクトを依存関係に指定していますが(該当コード)、プロダクトのjarには含まないようにしています。

Serverless FrameworkのLambda Layers設定

Layersの設定

ポイントはOutputsの設定。プロダクト用のプロジェクトとスタックを分けるためOutputを設定しています。Ref: JavaLibrariesLambdaLayerの箇所は命名規則があって、layers/javaLibrariesjavaLibraries先頭大文字、語尾にLambdaLayerという文字を付けるというややこしい制約があります。

このあたりドキュメントに記載はなく、ブログで紹介されているのみなのでそちらを参考に。(https://serverless.com/blog/publish-aws-lambda-layers-serverless-framework/)

layers:
  javaLibraries:
    package:
      artifact: build/distributions/libraries-1.0-SNAPSHOT.zip
    name: dev-javalibraries
    compatibleRuntimes:
    - java8
    allowedAccounts:
    - '*'

resources:
  Outputs:
    JavaLibrariesLayerExport:
      Value:
        Ref: JavaLibrariesLambdaLayer
      Export:
        Name: JavaLibrariesLambdaLayer

Lambda(Layersを使う側)の設定

Layersを別スタックにしたので以下のように、Outputを使用するための設定をします。

functions:
  hello:
    handler: org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler

    layers:
    - ${cf:sampleJavaLayer-dev.JavaLibrariesLayerExport}
    environment:
      FUNCTION_NAME: goods
      MAIN_CLASS: Application
    events:
    - http:
        path: goods
        method: post
        integration: lambda
        request:
          template:
            application/json: '$input.json("$")'

SpringCloudFunction

こちらは、この方のブログを参考にさせていただきました。
https://qiita.com/YusukeHasegawa/items/b9599834554b6ddb016e

概ねこの通りなのですが、いくつか補足を

複数の関数を同じスタックで管理するときは、Handlerの関数にComponentを設定し(該当コード)、serverless.ymlで環境変数FUNCTION_NAMEでそのComponentを指定するといけます(該当コード)。

gradleではAWS用のjarを作成するのに、shadowプラグインをそのまま使えず、META-INFのファイルをjarに含めるための設定がいくつか必要でした(該当コード)

まとめ

まだ、SpringCloudFunction自体広まってないですし、AWS LambdaでJava・SpringBootを使うなんてという方も多いと思いますが、実現可能になってきましたね!

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5