この記事は マイナビ Advent Calendar 2021 の22日目の記事です。
そろそろクリスマスが近づいて参りました。
只今12月20日の23:21です。明日の昼12時までにこの記事を書き上げる必要があります。(いつもギリギリや・・)
今回は、2021年末に世間を騒がせている大変ホットなLog4J脆弱性対応をきっかけに、今更ながらDocker版Lambdaを試してみた!という記事を書こうと思います。
実は他のネタを書こうと思っていたのですが、Log4j脆弱性関連の対応により、こんなタイミングで平日だけでなく土日も溶かしてしまったためLog4J対応しか用意できる記事がないや!ってこととなりこのネタにしました!!わーいわーい
お話の概要
- 技術キーワード
- aws lambda(Java)
- aws lambda(Docker)
- 脆弱性発覚からLambdaのDocker化までの流れ
- Log4j脆弱性により、本Lambda関数(Javaランタイム)が利用しているApache Beamというライブラリにも影響があるとGoogleより連絡を受ける
- 脆弱性を完全に回避するには、Apache Beamライブラリの最新化を強く推奨とのこと
- 但し、デフォルト設定ではユーザデータがライブラリ内で勝手にロギングされるようなことはないとのこと(つまり恐らく致命性はそこまでは高くない)
- とはいえ、依存はあるので安全なバージョンへのアップデートを強く推奨するというスタンスのようでした。
- 脆弱性を完全に回避するには、Apache Beamライブラリの最新化を強く推奨とのこと
- ライブラリを最新化してLambdaへデプロイしたら、なんとアップロードZipファイルサイズ上限(解凍後250MB)を超えた!
- ライブラリ削減でのサイズ縮小も試みたが、いっそJava版LambdaをDocker化してみようと思い立った
- Docker版はイメージの最大サイズはなんと**余裕の10GB!**なのです。
- 少しハマったが、結果的には結構簡単にできた!
- Log4j脆弱性により、本Lambda関数(Javaランタイム)が利用しているApache Beamというライブラリにも影響があるとGoogleより連絡を受ける
Lambda(Javaランタイム版)の容量制限
以下のページを見れば明記されていますが、デプロイパッケージ(Zipファイル)の解凍後サイズは250MBが上限です。
これを超えたサイズのJarファイルはデプロイできません。
しかもこの制限は、上限緩和もできないハードリミットであることにご注意を。皆さん注意しましょう。
なので、今回のように急遽ライブラリの最新化などが必須となった時にサイズ上限と戦う場合もあるかもしれません。
Lambdaなどサーバレスのサービスはこの手のリミットと戦うケースが多いので、クォータの情報にはよく注意して利用を決断するようにしましょう。→と言っている私がハマっているわけですが・・・
Dockerイメージですと、10GBまで行けるので、パッケージサイズが大きいものは早々にDockerとしてLambdaを開発するほうがトータルではリーズナブルな選択になるかもしれません。
javaランタイムのLambdaをDocker化するには
流れはだいたい以下のようになります。
- Dockerfileを書き、Dockerビルドする
- ECR(Elastic Container Registory)にリポジトリを作成する
- ECRのリポジトリへDockerイメージをPush
- Lambda関数をDocker版を指定して作成し、ECRにPushしたイメージのURIを指定する
1.Dockerfileを書き、Dockerビルドする
以下のようなDockerfileを書きます。
FROM amazon/aws-lambda-java:11
COPY ./target/app-bundled-0.1.jar ${LAMBDA_TASK_ROOT}/lib/
CMD [ "jp.xxxxx.App::handleRequest" ]
- 上記Dockerfileの前提
- Java11を利用
- デプロイするJarは
./target/app-bundled-0.1.jar
とします。 - エントリポイントとなるhandleRequest関数が実装されたクラスは
jp.xxxxx.App
であるとします。 - Jar以外のclassファイルもデプロイ必要である場合は、${LAMBDA_TASK_ROOT}直下に配置するようにします。(上記例はJarのみ)
つまり、${LAMBDA_TASK_ROOT}配下のディレクトリ構成のイメージは以下のようになります。
${LAMBDA_TASK_ROOT}
├── com
│ └── example
│ ├── LambdaHandler.class
│ └── <your other class files here>
└── lib
├── aws-lambda-java-core-1.2.1.jar
├── aws-lambda-java-events-3.7.0.jar
└── <your other JAR dependencies here>
詳しくは、以下に情報があります。
https://gallery.ecr.aws/lambda/java
Dockerfileを作成したら、以下のコマンド例のようにDockerビルドします。(appがイメージ名の1.0がバージョン番号とします。)
$ docker build . -t app:1.0
2. ECR(Elastic Container Registory)にリポジトリを作成する
可視性設定を プライベート
にして、リポジトリ名は任意の名前にして、「リポジトリを作成」ボタンクリックでECRリポジトリが作成されます。これがDockerイメージが登録される箱になります。
3. ECRのリポジトリへDockerイメージをPush
2で作成したリポジトリ名をクリックすると、「プッシュコマンドの表示」というボタンがあります。
「プッシュコマンドの表示」をクリックすると以下のようなダイアログにコマンドがズバリ表示されるので、ほとんどそのままのコマンドでECRリポジトリにPushができます。(秘密の箇所は赤で塗りつぶしています。)
4. Lambda関数をDocker版を指定して作成し、ECRにPushしたイメージのURIを指定する
お膳立ては完了しました。あとはLambda関数を作成し、そこにデプロイするだけです。
Lambdaの画面を開き、「関数の作成」ボタンで関数を作成します。
関数の作成画面にて、右から2番め「コンテナイメージ」を選択し、先程のECRコンテナイメージを選択すれば、最後に「関数の作成」クリックでDocker版Lambda関数の完成となります。
ECRにDockerイメージの履歴が残るので、版管理や切り戻しもJava単独で開発&デプロイするよりもしやすいかもしれません。
おわりに
以上のような感じで、割と簡単に既存のJava版のLambda関数をDocker版のLambda関数へ移行ができました。
蓋を開けてみれば、ほとんどDockerfileを書くだけで済みましたが、${LAMBDA_TASK_ROOT}の辺りのディレクトリ構成などは初めてではなかなかややこしいところでは有りました。(それなりにはハマりました。)
もしもパッケージサイズで苦労している方がおられましたら、お手軽に試してみることをオススメします。
それではみなさま、よいクリスマスを!