S3へのレプリケーションをトリガーにLambdaのコード更新・新規バージョン発行を実行する
はじめに
こんにちは、SHIFT の開発部門に所属しているKatayamaです。今期から転属になり、開発を担当していくことになりました。 ただ、前期はDevOps導入支援等に携わっていた関係で今回はCICD関係の記事を書こうと思います。
今回の記事の内容ですが、バックエンドの Build 成果物(jar)が Deploy 環境にレプリケートされた事をトリガーに、Lambda のコード更新・新規バージョン発行を行う CD の構築を行ったので、それに関する Tips のようなものになります。
(前々回は Cloud Front のキャッシュ削除についての記事、前回は ECS のサービス更新についての記事、でしたが、今回でレプリケーション後の Deploy シリーズ 3 つ目となる Lambda のコード更新・新規バージョン発行についての記事です。)
※今回の記事もの発端も、マルチベンダーなどの場合に開発資材のやり取り(コピー・複製=レプリケーション)が必要になるという事でその要望から始まりました。
構成イメージ
何らかのトリガーで Code Build が Start すると、S3 に Build 成果物である jar が push され、それをトリガーにレプリケートが走る。レプリケーション先の環境でレプリケート後に自動で Deploy(Lambda のコード更新・新規バージョン発行)を実行させる。
図で示すと以下のようになる。
Lambda のコード更新・新規バージョン発行
今回の Deploy は、S3 へのレプリケーションで jar が PUT されたら、Lambda のソースコード更新・新規バージョン発行を行うというもの。
Deploy 自体は、Cloud Front のキャッシュ削除の時と同様に、レプリケーションで S3 の Bucket に新規でオブジェクトが作成されるのでそれをトリガーに Lambda 関数を実行し、以下の AWS CLI コマンドで行っている事と同じ事を AWS SDK(今回は JavaScript)で実行させる、という感じで行う。
aws lambda update-function-code --function {function名} --s3-bucket {jarのあるS3 Bucket名} --s3-key {jarのファイル名}
aws lambda publish-version --function-name {function名}
※{jarのファイル名} について、Bucket の中でフォルダ階層を作っているならルートディレクトリからのパスで書く必要がある
ex) --s3-key artifact/lambda/hoge.jar
・参考:aws.lambda.update-function-code
・参考:aws.lambda.publish-version
IAM
S3 へのレプリケーションをトリガーに Cloud Front のキャッシュ削除を実行する#IAM と全く同じになるのでそれを参照。
Lambda 関数のトリガーを設定する
S3 へのレプリケーションをトリガーに Cloud Front のキャッシュ削除を実行する#Lambda 関数のトリガーを設定する と全く同じになるのでそれを参照。
Lambda 関数を実装する
ここも基本的には、S3 へのレプリケーションをトリガーに Cloud Front のキャッシュ削除を実行する#Lambda 関数を実装する とほぼ同じだが、Event オブジェクトの中身から Deploy(Lambda UpdateFunctionCode, PublishVersion)を行うのに必要な情報を抜き出す部分が少し異なってくる。
Lambda に渡ってくる Event オブジェクトの中身
S3 へのレプリケーションをトリガーに Cloud Front のキャッシュ削除を実行する#Lambda に渡ってくる Event オブジェクトの中身 と全く同じになるのでそれを参照。
Lambda 関数(index.handler)の実装
今回は Node.js の Lambda 関数で Deploy を実行させるので、実装としては以下のようした。
const {
LambdaClient,
UpdateFunctionCodeCommand,
PublishVersionCommand,
} = require("@aws-sdk/client-lambda");
const client = new LambdaClient({ region: process.env.REGION });
exports.handler = async (event) => {
try {
const bucket = event.Records[0].s3.bucket.name;
const object = event.Records[0].s3.object.key;
if (!object.includes(process.env.FILE_NAME)) {
console.log("not applicable. object is ", object);
return "";
}
const updateInput = {
FunctionName: process.env.FUNCTION_NAME,
S3Bucket: bucket,
S3Key: object,
};
const updateCommand = new UpdateFunctionCodeCommand(updateInput);
const updateResponse = await client.send(updateCommand);
console.log(
"UpdateFunctionCodeCommand status",
updateResponse.$metadata.httpStatusCode
);
console.log("UpdateFunctionCodeCommand state", updateResponse.State);
if (process.env.PUBLISH) {
const publishInput = {
FunctionName: process.env.FUNCTION_NAME,
};
const publishCommand = new PublishVersionCommand(publishInput);
const publishResponse = await client.send(publishCommand);
console.log(
"PublishVersionCommand status",
publishResponse.$metadata.httpStatusCode
);
console.log("PublishVersionCommand state", publishResponse.State);
return "";
} else {
return "";
}
} catch (error) {
return errorHandler(error);
}
};
const errorHandler = (error) => {
const obj = {};
obj["status"] = 500;
obj["message"] = error.message;
obj["stack"] = error.stack;
obj["result"] = "ng";
if (error.$metadata) {
obj["status"] = error.$metadata.httpStatusCode;
}
console.log("errorHandler", obj);
return "";
};
※実装について何点か補足すると、
・if (process.env.PUBLISH)
PubishVersion を行わないパターンも想定し、環境変数に PUBLISH(値は何でもいい)がある時のみ PublishVersion を行うようにしている
・console.log()
今回は Cloud Watch Events(EventBridge)が Lambda 関数のトリガーであり、return された結果を人が見る事ができないので、何が起きているか分かるように敢えて console.log()で log に出力させるようにした
・return "";
これも今回 S3 がトリガーで return された結果を人が見れないので""(空文字)を返すようにした(何か JSON を返しても意味ないと判断)。
・参考:Lambda Client - AWS SDK for JavaScript v3
・参考:Class UpdateFunctionCodeCommand
・参考:Class PublishVersionCommand
Lambda 関数の実行ロールの設定
Lambda 関数が他の AWS サービスに対して何らかの操作を行うには、実行ロールでその権限を設定する必要がある。今回は Lambda の UpdateFunctionCode・PublishVersion が実行できればいいので以下のようなインラインポリシーを実行ロールに追加でアタッチするれば OK。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": ["lambda:UpdateFunctionCode", "lambda:PublishVersion"],
"Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:{lambda関数名}"
}
]
}
まとめとして
前々回は Cloud Front のキャッシュ削除について、前回は ECS のサービス更新について、レプリケーション後の Deploy について過去 2 回で扱ったが、Lambda についても同じような考え方でレプリケーション後にそれをトリガーにして Deploy を実行させる事ができた。
《マガジン》 AWS DevOpsソリューション
__________________________________
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/