LoginSignup
0
1

More than 3 years have passed since last update.

Lambda Container Image を Java 15ベースで作ってみる

Last updated at Posted at 2021-01-14

概要

AWS Lambdaがコンテナイメージをサポートしました。

コンテナイメージ作成には、AWSの提供しているベースイメージを使うのが手っ取り早いです。
が、2021/01/15現在、Java15は提供されていません。

私の参画しているプロジェクトでは現在Java15を採用しており、
11だと色々めんどくさいので15ベースでコンテナイメージを作れ! と言われ、
俺には無理だろ...と思いながら色々調べてやったら動いたので、共有します。

ソースコード

早速ですが成果物としてのソースコードを公開します。
多分動くと思います。

200 OK を返すだけのサンプルです。
intx24/java-15-lambda-container

Handlerの処理はほとんど awsdocs/aws-lambda-developer-guideのサンプル から拝借しています。
変更点として、Java15対応を確認するため、不要なswitch式を付け足しました。

どうやったか

1. まずJava11ベースイメージで動かしてみる

Java11のベースイメージを元に、まず動かしてみます。

ありがたいことに関連記事があったため、参考にして丸パクリし、Java11ベースのLambdaイメージを作成しました。
AWS LambdaのコンテナサポートをJavaで試してみた。

2. Java11ベースイメージDockerfileを見てみる

Java11ベースイメージのDockerfileを参考にしてみましょう」と上司に言われたので、見てみます。

よくわからない tar.xz を大量にADDしていることがわかります。
git lfs pull することで tar.xz ファイルが解凍可能になります。(最初これがわからなくて詰んだと思った)
以下、理解していったことを書いていきます

lambda-entrypoint.sh

DockerfileのENTRYPOINTです。

どうやら /var/runtime/bootstrap が実行されるらしいです。
${AWS_LAMBDA_RUNTIME_API} がない場合(多分ローカル環境)では エミュレータを使ってローカル実行を可能にしているっぽい。

エミュレータに関しては、Runtime support for Lambda container images
からダウンロード出来るので、これをローカルからコンテナにCOPYすれば良さそうです。

lambda-entrypoint.sh
#!/bin/sh
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.

if [ $# -ne 1 ]; then
  echo "entrypoint requires the handler name to be the first argument" 1>&2
  exit 142
fi
export _HANDLER="$1"

RUNTIME_ENTRYPOINT=/var/runtime/bootstrap
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
  exec /usr/local/bin/aws-lambda-rie $RUNTIME_ENTRYPOINT
else
  exec $RUNTIME_ENTRYPOINT
fi

/var/runtime/bootstrap

よくわからないファイルでした。
コンテナにコピペしても動かなかった & 中身がブラックボックス過ぎと判断したので、代替手段を探します。
どうやらカスタムランタイムを作る手順でbootstrapを作ればいけるらしい?

カスタムランタイムについて調べつつ、無理だから諦めようと思った頃に、
aws-lambda-java-runtime-interface-client でLambda関数が実行できることを知りました。
このコマンドを aws-lambda-rie の引数にすれば良さそうです。

3. 実装してみる

Dockerfile

Java15が入ってるAmazonLinux2のイメージがあるのでこれをベースにします。
後述する、自作の lambda-entrypoint.sh をENTRYPOINTとして,
Lambda関数 example.Handler::handleRequest を渡します。

FROM amazoncorretto:15

# set environment variables
ENV CLASSPATH /var/task/*

WORKDIR /var/task

# copy lambda execution files
COPY aws-lambda-rie /usr/local/bin/aws-lambda-rie
COPY lambda-entrypoint.sh /lambda-entrypoint.sh

# Copy function code
COPY build/libs/java-15-lambda-container-1.0-SNAPSHOT-all.jar /var/task/

# Set the Entrypoint
ENTRYPOINT ["/lambda-entrypoint.sh"]

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "example.Handler::handleRequest" ]

lambda-entrypoint.sh

aws-lambda-java-runtime-interface-client を実行します。
$HANDLER_NAME には、 Dockerfileの CMD で指定したLambda関数が渡されます。

aws-lambda-java-runtime-interface-client の README通りに java -cp ./* com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello を引数として渡すと、
aws-lambda-riejavabootstrap のあるパスだと解釈して死ぬため、/usr/bin/java に置き換えます。

また、ここではクラスパスをDockerfile内で環境変数として指定済みです。

#!/bin/sh

if [ $# -ne 1 ]; then
  echo "entrypoint requires the handler name to be the first argument" 1>&2
  exit 142
fi

HANDLER_NAME=$1

if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
  # local environment
  exec /usr/local/bin/aws-lambda-rie \
    /usr/bin/java \
    com.amazonaws.services.lambda.runtime.api.client.AWSLambda "$HANDLER_NAME"
else
  exec /usr/bin/java \
    com.amazonaws.services.lambda.runtime.api.client.AWSLambda "$HANDLER_NAME"
fi

/var/runtime/bootstrap

lambda-entrypoint.sh 内に統合したので不要となりました。

苦労したこと

javaのビルド周りがわからん

java自体の経験が1年ほど + intelliJ経由で触ってた + プロトタイプが出来ているプロジェクトに途中参画した 人間なので、
javaはgradleタスク実行したらとりあえず動くぐらいの認識でした。

そもそもクラスパスを通さなければ実行できないということも知らず, Class Not Found エラーで半日ほど消耗しました。
同じ原因で、カスタムランタイムのビルドについても1分で諦めました。

AWS提供のベースイメージをrun -it オプションでコンテナを探索できない

最初は ENTRYPOINT の概念すら知らなかったため、
各種ファイルを確認するため、 run -it でコンテナを探索しようと思ったら出来ず困ってました。
結果的には docker exec 経由で cat したり、
IntelliJのdocker のFilesタブで確認していました。

まとめ

今回、Lambda Container Image を Java 15ベースで作ってみて、一応動くところまで出来ました。

調査・実装含めて4日ほど作業にあてましたが、
まだまだ知るべきことが多くあるなと思わされる経験でした。

0
1
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
0
1