先日えいやと書いた AWS Lambda のデプロイツール lambroll ですが、これと公開済みの bash layer を使うとかなり気軽に(雑な) shell script を Lambda で実行できて体験がよかったので書いておきます。
AWS Lambda のミニマルなデプロイツール lambroll を書いた - 酒日記 はてな支店
ちょっとしたものをLambdaで書くの億劫さのほうが強かったけど、bash layerとlambrollを使ったら雑shell scriptをホストで書いてるのに近い感じになり、顧客が本当にほしかったもの感があるなこれ
— fujiwara (@fujiwara) 2019年11月13日
今回はとある理由で ECS のサービス内のタスクを定期的に入れ換えたかったので、aws ecs update-service
を一発実行する、という要件。やりたいことはただ aws-cli を一発叩くだけでできるのに、Lambda 化するには Node やら Go やらで SDK 使ってコードを書いて…というのがめんどくさいこと、よくあると思います。
まず適当にディレクトリを掘って、そこで lambroll init
で初期化します。
$ mkdir ecs-task-replacer $ cd ecs-task-replacer $ lambroll init --function-name ecs-task-replacer 2019/11/13 22:16:21 [info] lambroll v0.2.1 2019/11/13 22:16:23 [info] function ecs-task-replacer is not found 2019/11/13 22:16:23 [info] creating .lambdaignore 2019/11/13 22:16:23 [info] creating function.json 2019/11/13 22:16:23 [info] completed
関数が存在しないので、次のようなスケルトンの function.json が生成されます。
{ "FunctionName": "ecs-task-replacer", "Handler": "index.handler", "MemorySize": 128, "Role": "arn:aws:iam::123456789012:role/YOUR_LAMBDA_ROLE_NAME", "Runtime": "nodejs10.x", "Timeout": 3 }
これを bash layer GitHub - gkrizek/bash-lambda-layer: Run Bash scripts in AWS Lambda via Layers を使うように書き換えます。 Layers, MemorySize, Runtime, Role, Timeout を適当に指定。この例のように aws-cli を使う場合、Memory が少ない(CPUも比例して少ない) と aws コマンドを起動するのに大変時間がかかるので、1コアをフルに使える 1.5GB を指定するのがよいです。
{ "FunctionName": "ecs-task-replacer", "Handler": "index.handler", "Layers": [ "arn:aws:lambda:ap-northeast-1:744348701589:layer:bash:8" ], "MemorySize": 1536, "Role": "arn:aws:iam::123456789012:role/lambda-function", "Runtime": "provided", "Timeout": 30 }
関数本体は index.sh というファイルに、event を jq で処理して aws-cli を叩くだけのを書きます。今回は event で ECS cluster と service を指定できるように {"cluster": "CLUSTER_NAME", "service": "SERVICE_NAME"}
という構造にしてみました。(決め打ちでいいならハードコードしちゃってもいいですね)
function handler { set -e EVENT=$1 SERVICE=$(echo "$EVENT" | jq -r .service) CLUSTER=$(echo "$EVENT" | jq -r .cluster) aws ecs update-service \ --cluster "$CLUSTER" \ --service "$SERVICE" \ --force-new-deployment echo "{\"success\": true}" >&2 }
lambroll deploy
でデプロイします。
$ lambroll deploy 2019/11/14 08:40:52 [info] lambroll v0.2.1 2019/11/14 08:40:52 [info] starting deploy function ecs-task-replacer 2019/11/14 08:40:54 [info] creating zip archive from . 2019/11/14 08:40:54 [info] zip archive wrote 342 bytes 2019/11/14 08:40:54 [info] updating function configuration 2019/11/14 08:40:54 [info] updating function code 2019/11/14 08:41:00 [info] deployed version 2 2019/11/14 08:41:00 [info] updating alias set current to version 2 2019/11/14 08:41:01 [info] alias updated 2019/11/14 08:41:01 [info] completed
手元から実行するには lambroll invoke
を使って、標準入力に event を与えます。--log-tail を付けると実行した Lambda のログ(の最後の4KB)が出力されます。
$ echo '{"cluster":"default", "service":"example"}' | lambroll invoke --log-tail 2019/11/14 08:46:35 [info] lambroll v0.2.1 {"success": true} 2019/11/14 08:46:57 [info] StatusCode:200 ExecutionVersion:$LATEST s reached a steady state." }, { "id": "8304a25b-8329-4099-903f-677013206fe8", "createdAt": 1572800779.798, "message": "(service example) has reached a steady state." }, ...(略) "schedulingStrategy": "REPLICA", "enableECSManagedTags": false, "propagateTags": "NONE" } } END RequestId: c821f023-0af2-48e5-ae21-c6f381e9ec65 REPORT RequestId: c821f023-0af2-48e5-ae21-c6f381e9ec65 Duration: 20484.06 ms Billed Duration: 20600 ms Memory Size: 128 MB Max Memory Used: 103 MB Init Duration: 44.74 ms 2019/11/14 08:46:57 [info] completed
おしまい。
定期実行するには CloudWatch Events でポチポチとスケジュールを定義してやればよいですね。
最初に IAM role を用意するところと、init で生成された function.json を書き換えるところが少しだけ手間ですが、そのあとは index.sh を書き換えて deploy して invoke するのに1コマンドずつ叩くだけです。
個人的には雑に cron で shell script を定期実行したいだけなのに、コード書いて Lambda 用意してって億劫だなあ、みたいなストレスがだいぶ減る気がします。どうぞお試しください。