以前cdkbotというツールを出した。これはGitHubのPRからCDKのデプロイなどを実行できるツールでlambda上で動いていた。
PR上でCDKのレビューやデプロイを行うツールcdkbotを作った - sambaiz-net
npmやgitといった外部コマンドを実行するため、layerにバイナリを詰めて上げていた。
Lambda上でnpm installできるLayerを作った - sambaiz-net
CDKにはローカルのDockerfileをbuildしてECRに上げてくれる
ecs.ContainerImage.fromAsset()
という関数があって、これに対応させるためlayerにdockerを追加してみたのだがrootが取れず動かない。
rootなしで動くudockerでdind(docker in docker)のイメージを動かしたりもしてみたがbuildはできなかった。
この他にもCloudFrontなどリソースによっては作成に時間がかかり、Lambdaのタイムアウト上限に到達する問題もあったので、lambda+API Gatewayでwebhookのリクエストだけ受け取り、ECSのTaskを立ち上げて処理を行わせることにした。
templateにECSまわりのものを追加したところ、Serverless Application Repository非対応ということで上げられなくなってしまった。
AWS SAMでLambdaの関数をデプロイしServerless Application Repositoryに公開する - sambaiz-net
Taskへのパラメータの受け渡し
runTask
のcontainerOverrides
でコマンドの引数としてlambdaに来たリクエストをそのまま渡そうとしたところ、8192文字文字の上限に当たってしまったので
SQSで受け渡すことにした。
sess := session.New()
sqsSvc := sqs.New(sess)
if _, err := sqsSvc.SendMessage(&sqs.SendMessageInput{
MessageBody: aws.String(string(payload)),
QueueUrl: aws.String(os.Getenv("OPERATION_QUEUE_URL")),
MessageGroupId: aws.String("group"),
}); err != nil {
fmt.Println(err.Error())
return response{
StatusCode: http.StatusInternalServerError,
}, err
}
同時実行数を制限する
アプリケーションの特性上、同時実行されないようにしたい。 そこで普段はTask0のServiceを作成し、実行するときは要求Taskを1にして、Queueが空になるまで実行させ、最後に0に戻すようにした。Taskがない場合は立ち上がりにやや時間がかかるが、元々Lambdaで動いていたこともあって常に料金が発生する常駐リソースをなるべく使いたくなかった。
sess := session.New()
sqsSvc := sqs.New(sess)
for {
res, err := sqsSvc.ReceiveMessage(&sqs.ReceiveMessageInput{
QueueUrl: aws.String(os.Getenv("OPERATION_QUEUE_URL")),
MaxNumberOfMessages: aws.Int64(1),
})
if err != nil {
logger.Error("receive message error", zap.Error(err))
break
}
if len(res.Messages) == 0 {
break
}
...
}
ecsSvc := ecs.New(sess)
if _, err := ecsSvc.UpdateService(&ecs.UpdateServiceInput{
Cluster: aws.String(os.Getenv("TASK_ECS_CLUSTER_ARN")),
DesiredCount: aws.Int64(0),
Service: aws.String(os.Getenv("OPERATION_SERVICE_ARN")),
}); err != nil {
logger.Error("shutdown task error", zap.Error(err))
}