Docker に慣れちゃうと、別にインストールしても問題ないものまでコンテナ上で動かしたくなりません?特に更新が激しいものはアップデートの導入が面倒なので、つい Docker image を探しがち。
というわけで、AWS Lambda で実行する Java 11 コードを、docker-lambda を用いてローカル環境で開発・テスト実行させてみました。Docker 以外はインストール不要なので、気軽に試せて良い感じです。
ググってみても python や nodejs の説明ばかりヒットする気がしたので、簡単なメモとしてまとめておきます。
docker-lambda とは
docker-lambda は AWS Lambda 開発環境を Docker ベースで提供するものです。以下、概要部分を簡単に訳しておきますね。
インストールされているソフトウェアとライブラリ、ファイル構造と権限、環境変数、コンテキストオブジェクトと動作を含む「ライブ AWS Lambda 環境」をほぼ同じように複製するサンドボックス化されたローカル環境。ユーザーと実行中のプロセスも同じです。
docker-lambda を使用することで、同じ厳密な Lambda 環境で関数を実行し、ライブでデプロイしたときに同じ動作を示せます。AWS Lambda に存在するライブラリバージョンと同様にネイティブ依存関係をコンパイルし、AWS CLI を使用してデプロイすることもできます。
サンプルコードの導入
テスト用のフォルダ(ディレクトリ)を作成し、docker-lambda/examples/java/ にあるファイルを配置します。該当リポジトリ を丸ごと clone もしくは zip 形式でダウンロードして、該当部分だけをコピーすると楽でしょう。
サンプルコードのビルド
ダウンロードしたサンプルは Java のソースコード です。テスト用のフォルダに移動し、Docker コマンドでビルド(コードのコンパイル)を実施します。
docker run --rm -v $PWD:/var/task lambci/lambda:build-java11 gradle build
なお私は Windows 環境なので $PWD
が使えないため、実際のフォルダ (今回は c:/work/java/docker-lambda) を指定しました。
docker run --rm -v c:/work/java/docker-lambda:/var/task lambci/lambda:build-java11 gradle build
問題無く実行されれば build フォルダが作成され、その中に幾つかのフォルダが生成されるはずです。この中の docker フォルダに、コンパイルされたクラスのバイトコードと、実行に必要なライブラリが格納されています。
サンプルコードの実行
作成された実行クラスは、build/docker/
フォルダ内にあります。以下のコマンドで実行してみます。
docker run --rm -v $PWD/build/docker:/var/task \
lambci/lambda:java11 org.lambci.lambda.ExampleHandler \
'{"some": "event"}'
Windows 環境では改行を使わず、JSON の指定方法も修正して、実際には以下のように実施しています。
docker run --rm -v c:/work/java/docker-lambda/build/docker:/var/task lambci/lambda:java11 org.lambci.lambda.ExampleHandler "{\"some\": \"event\"}"
AWS SDK API を利用してみる
サンプルの実行だけで終わるのも寂しいですので、S3 のバケット一覧を取得してみます。
サンプルコードの修正
AWSのガイド を参考に、まずは import 文を追加します。
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.Bucket;
import java.util.List;
そして handleRequest
関数の終わり (Return文の前) に以下のコードを追加します。
logger.log("Your Amazon S3 buckets are:\n");
final AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
List<Bucket> buckets = s3.listBuckets();
for (Bucket b : buckets) {
logger.log("* " + b.getName() + "\n");
}
この段階でビルドを実施すると、S3 関連の SDK ライブラリが見つからずエラーになります。テスト用フォルダにある build.gradle にビルド用の設定(2行)を追加します。
dependencies {
implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.99') // この行を追加
implementation (
'com.amazonaws:aws-lambda-java-core:1.2.0',
'com.amazonaws:aws-lambda-java-events:2.2.7', // カンマ追加
'com.amazonaws:aws-java-sdk-s3' // この行を追加
)
}
これで先ほどと同じコマンドで、ビルドできるようになりました。
修正したサンプルコードの実行
さきほどと同様にコードを実行しようとすると、権限エラーになります。以下のように -e オプションで AWS_ACCESS_KEY
と AWS_SECRET_ACCESS_KEY
の値を渡してあげましょう。
docker run --rm -v $PWD/build/docker:/var/task \
-e AWS_ACCESS_KEY_ID=XXX -e AWS_SECRET_ACCESS_KEY=XXXXXX \
lambci/lambda:java11 org.lambci.lambda.ExampleHandler \
'{"some": "event"}'
Windows の場合は1行にまとめて、JSON 表記など修正して以下のように実行します。
docker run --rm -v c:/work/java/docker-lambda/build/docker:/var/task -e AWS_ACCESS_KEY_ID=XXX -e AWS_SECRET_ACCESS_KEY=XXXXXX lambci/lambda:java11 org.lambci.lambda.ExampleHandler "{\"some\": \"event\"}"
AWS_ACCESS_KEY
と AWS_SECRET_ACCESS_KEY
は、AWS でユーザーを作成して、その「認証情報」タブから作成・取得ができます。なんの権限もない新規ユーザーを作成し、開発対象である Lambda 関数と同じアクセス権限(ロールやポリシー)を与えて利用するのが良さそうです。
stay-open モードで開発する
4つのオプションを追加して、更に便利に開発していきましょう。
- 環境変数
DOCKER_LAMBDA_STAY_OPEN=1
で API 待ち受けモードになる -
-p 9001:9001
オプションで待ち受けするポートを指定する - 環境変数
DOCKER_LAMBDA_WATCH=1
でファイルの変更を監視し、サービスの再スタートを実施する -
--restart on-failure
Dockerオプションでエラー時に自動的に再起動する (Java 11では必須ではない)
以下のようなコマンド実行になります。
docker run -v $PWD/build/docker:/var/task \
--restart on-failure \
-e DOCKER_LAMBDA_WATCH=1 -e DOCKER_LAMBDA_STAY_OPEN=1 -p 9001:9001 \
-e AWS_ACCESS_KEY_ID=XXX -e AWS_SECRET_ACCESS_KEY=XXXXXX \
lambci/lambda:java11 org.lambci.lambda.ExampleHandler \
'{"some": "event"}'
Windows の場合は以下のように実行します。
docker run -v c:/work/java/docker-lambda/build/docker:/var/task --restart on-failure -e DOCKER_LAMBDA_WATCH=1 -e DOCKER_LAMBDA_STAY_OPEN=1 -p 9001:9001 -e AWS_ACCESS_KEY_ID=XXX -e AWS_SECRET_ACCESS_KEY=XXXXXX lambci/lambda:java11 org.lambci.lambda.ExampleHandler "{\"some\": \"event\"}"
別なターミナル画面を開き、以下のように curl などでアクセスしてみましょう。
curl -d "{}" http://localhost:9001/2015-03-31/functions/myfunction/invocations
以下のように Return 文の内容 "Hello World!" が返ってくれば動作は成功です。ログは Docker を実行したほうのターミナルのほうに表示されています。
試しにソースコードを修正してビルドを実行すると、/build/docker フォルダの変更を感知して、サービスが自動的に再始動します。
おわりに
Docker 環境されあれば docker-lambda を用いてローカルで AWS Lambda 関数の開発を実施できます。好みのエディタでサクサク開発でき、テストも容易です。いろんなコードを書いて、試してみましょう!
それではまた。