11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS LambdaのコンテナサポートをJavaで試してみた。

Last updated at Posted at 2020-12-08

AWS LambdaとServerless Advent Calendar 2020 12/8分の記事です。


COVID-19の影響で、オンライン開催となったAWS re:Invent 2020。
初日のキーノートで、AWS Lambdaがコンテナをサポートするという発表がありました。
New for AWS Lambda – Container Image Support

なんかやってみたいなーと思って、昔Javaをやっていたので、
Java用のコンテナイメージ使って、Lambda関数を作ってみました。

どんなコンテナイメージあるの?

コンテナイメージ(Base Image)にはどんなのがあるのかと、
DockerHubをみると、以下のものがあるようですね。

  • amazon/aws-lambda-nodejs
  • amazon/aws-lambda-python
  • amazon/aws-lambda-java
  • amazon/aws-lambda-dotnet
  • amazon/aws-lambda-ruby
  • amazon/aws-lambda-go
  • amazon/aws-lambda-provided

現在標準のランタイムとして提供されている言語はすべてありそうです。
amazon/aws-lambda-providedはTagをみると、ALとかAL2があるようなので、
カスタムランタイム用のようです。

各言語も、例えば、Javaでは、
バージョン8と11が用意されています。

イメージの取得場所なんですが、
FROM public.ecr.aws/lambda/java:11
となっていて、
同じくKeyNoteで発表された
Announcing Amazon ECR Public and Amazon ECR Public Gallery
が早速使われているみたいですね。

Base Imageを使うと、AWSがパッチ当てとかメンテナンスをしてくれると聞いております。

やってみた

コードをがっつり書くつもりはなく(すみません)。コード自体は、公式ドキュメントのAWS Lambda for Javaに記載のあったSample Appsのうち、java-basicHander.javaを使わさせていただいてます。
ビルド自体はMavenを使ってます(昔に使ってたので)。

DockerfileもDockerhub記載のものをベースにしてます。

FROM public.ecr.aws/lambda/java:11

# Copy function code
COPY target/java-sample-1.0-SNAPSHOT.jar ${LAMBDA_TASK_ROOT}
RUN ls -al
RUN jar -xvf java-sample-1.0-SNAPSHOT.jar
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "example.Handler::handleRequest" ]

最初は、jarファイルを${LAMBDA_TASK_ROOT}(/var/task)に配置すれば、
よしなにやってくれるかなと思ったんですが、
そんなわけもなく、
コンパイルされた状態で配置させる必要があるようで、
作成されたjarファイルを解凍してます(めっちゃ下手書きですみません)。

とてもシンプルなJava("Hello Java")と返すようなプログラムだと、
ビルドしたClassファイルだけあれば行けそうだったんですが、
サンプルだと、Googleが提供するJSONデータとJavaオブジェクトを相互に変換するライブラリのGSONを使ってて、
それがClassNotFoundExceptionになっちゃうので、この形式にしてます。

久々に使ったので、
もしかしたら、他にいい手段があるかもしれないので、
わかったらアップデートしようかなと思います。

コンテナの中で全部できるかな?ってことでやってみたのがこちら

FROM public.ecr.aws/lambda/java:11

RUN yum -y update && yum -y install wget
RUN wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
RUN sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
RUN yum install -y apache-maven
ENV JAVA_HOME=/usr/lib/jvm/java-11-amazon-corretto.x86_64/

# Copy function code
COPY src/ ${LAMBDA_TASK_ROOT}/src/
COPY pom.xml ${LAMBDA_TASK_ROOT}
RUN mvn package
RUN cp -r target/java-sample-1.0-SNAPSHOT.jar ${LAMBDA_TASK_ROOT}
RUN jar -xvf java-sample-1.0-SNAPSHOT.jar
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "example.Handler::handleRequest" ]

こちらもjarファイルを解凍してます(同じく下手書きですみません)。

便利だなと思ったのは、
コンテナを起動して、cUrlなどでアクセスしてローカルテスト可能なのは便利かなと思いました。

docker build -t java-sample-v11 .
Successfully tagged java-sample-v11:latest
docker run -p 9000:8080 java-sample-v11:latest

別ターミナルで、curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
って感じで実行すると、問題なければレスポンスがあります。

ログもちゃんと出力されます。
Init Duration: 0.22 ms Duration: 504.55 ms Billed Duration: 600 ms
となっていて、Billed Durationが100ms単位だなーって思いましたが、ローカルなのでw

ENVIRONMENT VARIABLES: {
  "PATH": "/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
  "LAMBDA_TASK_ROOT": "/var/task",
  "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "3008",
  "TZ": ":/etc/localtime",
  "AWS_EXECUTION_ENV": "AWS_Lambda_java11",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/Functions",
  "AWS_LAMBDA_LOG_STREAM_NAME": "$LATEST",
  "LANG": "en_US.UTF-8",
  "_HANDLER": "example.Handler::handleRequest",
  "LAMBDA_RUNTIME_DIR": "/var/runtime",
  "HOSTNAME": "b10284f7ea25",
  "LD_LIBRARY_PATH": "/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "PWD": "/var/task",
  "AWS_LAMBDA_RUNTIME_API": "127.0.0.1:9001",
  "SHLVL": "0",
  "HOME": "/root",
  "AWS_LAMBDA_FUNCTION_NAME": "test_function"
}CONTEXT: {
  "memoryLimit": 3008,
  "awsRequestId": "fc64a0bd-890c-4339-a460-8ecfc5fa1686",
  "logGroupName": "/aws/lambda/Functions",
  "logStreamName": "$LATEST",
  "functionName": "test_function",
  "functionVersion": "$LATEST",
  "invokedFunctionArn": "",
  "deadlineTimeInMs": 3214833340286,
  "logger": {}
}EVENT: {
  "payload": "hello world!"
}EVENT TYPE: class java.util.LinkedHashMapEND RequestId: fc64a0bd-890c-4339-a460-8ecfc5fa1686
REPORT RequestId: fc64a0bd-890c-4339-a460-8ecfc5fa1686  Init Duration: 0.22 ms  Duration: 504.55 ms     Billed Duration: 600 ms Memory Size: 3008 MB    Max Memory Used: 3008 MB

ローカルテストを実行してみて、問題なければ、

  1. ECRにレポジトリ作成
  2. ECRにログイン
  3. TAGづけ
  4. ECRにPush、
  5. ECRにPushしたイメージを使ってLambdaをデプロイ
    となります。
    2〜4はコンソールでレポジトリ作成すると、ECR上でコマンド出してくれるので、その通りに実行すれば大丈夫そうです。

デプロイして、テスト実行なりして、動けばOKです。

違いあるのかな?

コンテナ利用したLambda関数と普通にjarファイルをアップしてデプロイしたLambda関数で違いあるのかな?っていうのをついでに確認してみました。
両方ともテストイベントのHello World をベースにしたテストイベントを実行してます。

コンテナ

スクリーンショット 2020-12-08 19.52.30.png

  • 初回実行(Cold Start)
    スクリーンショット 2020-12-08 19.56.31.png

  • 2度目(Warm Start)
    スクリーンショット 2020-12-08 20.00.24.png

jarファイルをデプロイ

スクリーンショット 2020-12-08 19.52.58.png

  • 初回実行(Cold Start)
    スクリーンショット 2020-12-08 19.56.24.png

  • 2度目(Warm Start)
    スクリーンショット 2020-12-08 20.00.30.png

直前にTwitterで流れてきて、知ったのですが、
コンテナ利用な場合は、Init処理の時間も課金単位に含まれるようです。

カスタムランタイムも同じなんですね。それは知らなかったです。
Java2度目以降は速いですねー(今更感)。
そして、課金単位がちゃんと1ms単位になってる!

まとめ

AWSJの西谷さんが発表後にブログAWS Lambdaがコンテナをサポートしたのでちょっと試してみたを書かれており、そこで、
「あくまでもLambdaの実行モデルであるファンクションモデルはそのままにランタイムの自由度が増した」
と述べられていますが、その通りかなと思います。

Dockerイメージ用意して、そのままECRにPushして、デプロイできるのは、
Docker上で開発されている開発者にとっては便利なのかもしれませんね。
(普段Docker上で開発してないもので。。。今後の検討課題ではあります)

以前、Amazon Linux2上でビルドしたモジュールを含んだLambdaのパッケージを作ってデプロイするという機会があったのですが、
そういう場合、
Docker上(Amazon Linux2ベースのはず)でモジュールをビルドして、そのままアップロードできるのかなと思ったりしてます。
※あとで試そうかなとは思っています。

ただ、そういう用途でなければ、自分が定常的に使うか。。。と言われると、今のところ、積極的には使わないかなぁーっていうのが結論です。

おまけ

今年の7月に共著でAWS LambdaをはじめとするServerlessでの開発などについて記した基礎から学ぶ サーバーレス開発という本を書かせていただいたのですが、
コンテナサポート以外にも、Lambda Extensions(これre:Invent落選組だったんだな。。。)、課金単位の変更(100ms->1ms)、メモリ量の増加などのアップデートがあったので、本自体もアップデートしない(書いてた時から、すぐに変わっちゃうんだろうなとは思っていましたが)とダメだなぁ。。。なんて思った人です。


検証して一気に書き上げたので、至らぬ点があるかもしませんが、ご容赦ください。。。

11
10
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
11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?