Amazon Polly Amazon S3 AWS CLI AWS IAM AWS Lambda
カスタムAWS Lambdaランタイムで音声ファイルを生成・変換させてみる
2019/02/12
はじめに
ほぼ1年ぶりにブログ記事を書こうとAWSシンプルアイコン素材をダウンロードしたつもりがアーキテクチャアイコンに変更されていて、時の移り変わりを感じているプログラマA(50歳)です。
ローカルな話ですが、私の地元では各家庭に告知端末(IP-TV電話)が配布されており、自治会行事の告知をしたい場合など役場に画像/音声ファイルを持ち込むと地域限定で配信してくれたりします。
で、自治会の書記(小間使いの別名)である私はいつもAWSコンソールにログインして、AWS Pollyでテキストを音声に変換・ダウンロードして、音楽(フリー素材)をAudacityでくっつけて、音声レベルをノーマライズして...とかチマチマ音声ファイルを作ってたりする訳ですが、そろそろ自動化しないとなぁと考えておりました。
最初Amazon Elastic Transcoderを使えば楽勝かなと考えていたのですが、参考記事をひととおり読み、気が変わりました!!
せいぜい30秒かそこらの短い音声だし、(多分)Amazon Elastic Transcoderを使うまでもないのではないかと。まずはテキストから音声ファイルを生成し、WAV形式に変換するまでをカスタムAWS Lambdaランタイム/AWS CLI/FFmpegで実装する事にしました。
システムイメージ
謝辞
この記事はカスタムAWS Lambdaランタイム(Lambda Layer)の作成手順について参考1.2の記事、AWS CLI Layer For AWS Lambda(以下awscli-lambda-layer)リポジトリのリソースを使用させていただきました。
利用するサービス、コマンド
AWS Lambda、Polly、IAM、S3、CLI、FFmpeg
全体の作業の流れ
- AWS CLIを実行するカスタムAWS Lambdaランタイム(Lambda Layer)を作成
- Lambda Layer上で動作するLambda関数(bashスクリプト)を作成
1. Lambda Layer作成
awscli-lambda-layerリポジトリのスクリプトを実行する環境としてWindows 10上のWindows Subsystem for Linux(以下WSL)/Ubuntu18.04を使用しました。
ローカル環境準備
WSL/Ubuntuで使用したコマンド・バージョン
コマンド | バージョン | 確認コマンド |
git | 2.17.1 | git version |
AWS CLI | 1.16.90※ | aws --version |
soxi | 14.4.2 | soxi |
※ AWS CLIは"aws lambda publish-layer-version"に対応したバージョンが必要です。
AWS CLIセットアップ後のaws configureはプロファイル指定なし(default)で実行します。
項目 | 値 |
AWS CLIプロファイル | default |
AWS環境準備
AWS環境を設定します。(後でスクリプトを実行する際、これらの値を引数として指定します。)以降の作業は全てWSL/Ubuntuコンソール上で実行します。
項目 | 値 |
IAMロール | lambdaLayerBuilder |
S3バケット | pg-a-temp-kumoto |
IAMロールlambdaLayerBuilderを作成し、 AmazonS3FullAccess、AWSLambdaBasicExecutionRoleポリシーをアタッチします。
[pg-a@kumoto]$ vi ./AssumeRoleLambda.json
[pg-a@kumoto]$ cat AssumeRoleLambda.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
[pg-a@kumoto]$ aws iam create-role --role-name lambdaLayerBuilder --assume-role-policy-document file://./AssumeRoleLambda.json
{
"Role": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
]
},
"RoleId": "*********************",
"CreateDate": "2019-01-**T**:**:**Z",
"RoleName": "lambdaLayerBuilder",
"Path": "/",
"Arn": "arn:aws:iam::************:role/lambdaLayerBuilder"
}
}
[pg-a@kumoto]$ for ARN in $(aws iam list-policies --query "Policies[?PolicyName==\`AmazonS3FullAccess\`||PolicyName==\`AWSLambdaBasicExecutionRole\`].Arn" --output text)
> do
> aws iam attach-role-policy --role-name lambdaLayerBuilder --policy-arn $ARN
> done
[pg-a@kumoto]$ aws iam list-attached-role-policies --role-name lambdaLayerBuilder
{
"AttachedPolicies": [
{
"PolicyName": "AmazonS3FullAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonS3FullAccess"
},
{
"PolicyName": "AWSLambdaBasicExecutionRole",
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
]
}
S3バケットpg-a-temp-kumotoを作成します。
アクセスのステータスは"バケットとオブジェクトは非公開"としました。
[pg-a@kumoto]$ aws s3api create-bucket --bucket pg-a-temp-kumoto --create-bucket-configuration LocationConstraint=ap-northeast-1
{
"Location": "http://pg-a-temp-kumoto.s3.amazonaws.com/"
}
[pg-a@kumoto]$ vi s3pubAccBlock.json
[pg-a@kumoto]$ cat s3pubAccBlock.json
{
"IgnorePublicAcls": true,
"BlockPublicPolicy": true,
"BlockPublicAcls": true,
"RestrictPublicBuckets": true
}
[pg-a@kumoto]$ aws s3api put-public-access-block --bucket pg-a-temp-kumoto --public-access-block-configuration file://./s3pubAccBlock.json
Lambda Layer作成スクリプト実行
awscli-lambda-layerの以下のスクリプトを実行し、AWS CLIを実行するためのLambda Layerを作成します。
- 01.build_deploy.sh
- 02.create_aws_layer.sh
本来は利用したいAWS CLIコマンドだけ抽出してLambda Layer化する使い方が想定されているようですが、今回は全コマンドを対象にします。
01.build_deploy.shはLambda環境にAWS CLIをインストール、展開後のコマンドをアーカイブしてS3にアップします。
[pg-a@kumoto]$ cd /mnt/c/temp
[pg-a@kumoto]$ git clone https://github.com/CM-Kajiwara/awscli-lambda-layer.git
...
[pg-a@kumoto]$ cd /mnt/c/temp/awscli-lambda-layer
[pg-a@kumoto]$ cp bootstrap bootstrap_org
[pg-a@kumoto]$ vi bootstrap
[pg-a@kumoto]$ diff bootstrap bootstrap_org
30c30
< aws s3 cp /tmp/provided${date}.tgz s3://${S3_BUCKET}/provided.tgz
---
> aws s3 cp /tmp/provided${date}.tgz s3://${Bucket}/provided.tgz
[pg-a@kumoto]$ ./shell/01.build_deploy.sh lambdaLayerBuilder pg-a-temp-kumoto default
rm: cannot remove 'function.zip': No such file or directory
adding: function.sh (stored 0%)
adding: bootstrap (deflated 42%)
delete: s3://pg-a-temp-kumoto/provided.tgz
{
"FunctionName": "layer-creater",
"LastModified": "2019-01-**T**:**:**.038+0000",
"RevisionId": "c8994255-3bd7-40a3-95ca-3cbebec3a4f9",
"MemorySize": 256,
"Environment": {
"Variables": {
"S3_BUCKET": "pg-a-temp-kumoto"
}
},
"Version": "$LATEST",
"Role": "arn:aws:iam::************:role/lambdaLayerBuilder",
"Timeout": 900,
"Runtime": "provided",
"TracingConfig": {
"Mode": "PassThrough"
},
"CodeSha256": "p9HuJ1RxoTS8yX4k9Jpyg57SD1du6X4+kwIQMlcbPP4=",
"Description": "",
"CodeSize": 1043,
"FunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:layer-creater",
"Handler": "function.handler"
}
{
"StatusCode": 202
}
[pg-a@kumoto]$ aws s3 ls s3://pg-a-temp-kumoto/provided.tgz
2019-01-** **:**:** 8696675 provided.tgz
02.create_aws_layer.shはアーカイブしたAWS CLIコマンドとbootstrapをマージしてLambda Layerとして登録します。(すでにVersionが4になってますが気にしないでください。)
[pg-a@kumoto]$ ./shell/02.create_aws_layer.sh pg-a-temp-kumoto default
Completed 256.0 KiB/8.3 MiB (195.3 KiB/s) with 1 file(s) remaining
...
adding: bootstrap (deflated 39%)
{
"Content": {
"CodeSize": 10356445,
"CodeSha256": "71hWnVS2I4znp0g2bdnFU1ILqSTpfVecH++J3snPdbs=",
"Location": "https://awslambda-ap-ne-1-layers.s3.ap-northeast-1.amazonaws.com/snapshots/************/aws-cli-layer-..."
},
"LayerVersionArn": "arn:aws:lambda:ap-northeast-1:************:layer:aws-cli-layer:4",
"Version": 4,
"Description": "",
"CreatedDate": "2019-01-**T**:**:**.428+0000",
"LayerArn": "arn:aws:lambda:ap-northeast-1:************:layer:aws-cli-layer"
}
[pg-a@kumoto]$ aws lambda list-layer-versions --layer-name aws-cli-layer
{
"LayerVersions": [
{
"LayerVersionArn": "arn:aws:lambda:ap-northeast-1:************:layer:aws-cli-layer:4",
"Version": 4,
"CreatedDate": "2019-01-**T**:**:**.428+0000"
}
]
}
事後作業
Lambda Layerが正常に登録されたら04.delete_layer_creater.shでLambda関数を削除します。あわせてS3上のAWS CLIコマンドのアーカイブファイル、バケットを削除します。
[pg-a@kumoto]$ ./shell/04.delete_layer_creater.sh default
[pg-a@kumoto]$ aws s3 rm s3://pg-a-temp-kumoto --recursive
delete: s3://pg-a-temp-kumoto/provided.tgz
[pg-a@kumoto]$ aws s3api delete-bucket --bucket pg-a-temp-kumoto
2. Lambda関数作成
さて、ようやく本論のLambda関数です。。。面倒な処理はしていません。
- S3バケットへのテキストファイルPUTイベントにより起動
- Pollyを呼び出し、テキストファイルをPCM音声ファイルに変換
- FFmpegでPCM音声ファイルを読取り、変換してWAV音声ファイルを生成
- WAV音声ファイルをS3バケットにPUT
Lambda、S3、IAMの設定です。
項目 | 値 |
Lambda関数 | tts-function |
S3バケット | g-a-tts-kumoto |
テキストファイル格納先 | src/ |
WAVファイル格納先 | dst/ |
中間(PCM)ファイル格納先 | tmp/ |
IAMロール | lambdaTTS |
AWS環境準備
まずはお約束のIAMロール、S3バケットの作成です。IAMロールlambdaTTSに、AmazonS3FullAccess、AWSLambdaBasicExecutionRole、AmazonPollyFullAccessポリシーをアタッチします。
[pg-a@kumoto]$ cd /mnt/c/temp
[pg-a@kumoto]$ aws iam create-role --role-name lambdaTTS --assume-role-policy-document file://./AssumeRoleLambda.json
{
"Role": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
]
},
"RoleId": "*********************",
"CreateDate": "2019-01-**T**:**:**Z",
"RoleName": "lambdaTTS",
"Path": "/",
"Arn": "arn:aws:iam::************:role/lambdaTTS"
}
}
[pg-a@kumoto]$ for ARN in $(aws iam list-policies --query "Policies[?PolicyName==\`AmazonS3FullAccess\`||PolicyName==\`AWSLambdaBasicExecutionRole\`||PolicyName==\`AmazonPollyFullAccess\`].Arn" --output text)
> do
> aws iam attach-role-policy --role-name lambdaTTS --policy-arn $ARN
> done
[pg-a@kumoto]$ aws iam list-attached-role-policies --role-name lambdaTTS
{
"AttachedPolicies": [
{
"PolicyName": "AmazonS3FullAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonS3FullAccess"
},
{
"PolicyName": "AWSLambdaBasicExecutionRole",
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
},
{
"PolicyName": "AmazonPollyFullAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonPollyFullAccess"
}
]
}
S3バケットpg-a-tts-kumotoを作成し、バケットの下にsrc/dst/tmpフォルダを作成します。アクセスのステータスは"バケットとオブジェクトは非公開"としました。
[pg-a@kumoto]$ aws s3api create-bucket --bucket pg-a-tts-kumoto --create-bucket-configuration LocationConstraint=ap-northeast-1
{
"Location": "http://pg-a-tts-kumoto.s3.amazonaws.com/"
}
[pg-a@kumoto]$ aws s3api put-public-access-block --bucket pg-a-tts-kumoto --public-access-block-configuration file://./s3pubAccBlock.json
...
[pg-a@kumoto]$ aws s3api put-object --bucket pg-a-tts-kumoto --key src/
[pg-a@kumoto]$ aws s3api put-object --bucket pg-a-tts-kumoto --key dst/
[pg-a@kumoto]$ aws s3api put-object --bucket pg-a-tts-kumoto --key tmp/
FFmpegダウンロード
FFmpegを用意します。依存ファイルをケアするのが大変なので今回はStaticリンクバージョンのFFmpegをダウンロードしてきます。
[pg-a@kumoto]$ cd /mnt/c/temp
[pg-a@kumoto]$ wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz
--2019-01-** **:**:**-- https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz
...
2019-01-** **:**:** (393 KB/s) - ‘ffmpeg-git-amd64-static.tar.xz’ saved [33512956/33512956]
[pg-a@kumoto]$ openssl dgst -md5 ffmpeg-git-amd64-static.tar.xz
MD5(ffmpeg-git-amd64-static.tar.xz)= a06edad9ed3ac1138fe15e6b39ab7c51
[pg-a@kumoto]$ curl https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz.md5
a06edad9ed3ac1138fe15e6b39ab7c51 ffmpeg-git-amd64-static.tar.xz
[pg-a@kumoto]$ tar Jxfv ffmpeg-git-amd64-static.tar.xz
ffmpeg-git-20190114-amd64-static/
...
[pg-a@kumoto]$ ffmpeg-git-20190114-amd64-static/ffmpeg -version
ffmpeg version N-47984-g301cee61fa-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516
configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg
libavutil 56. 25.100 / 56. 25.100
libavcodec 58. 43.101 / 58. 43.101
libavformat 58. 25.100 / 58. 25.100
libavdevice 58. 6.101 / 58. 6.101
libavfilter 7. 48.100 / 7. 48.100
libswscale 5. 4.100 / 5. 4.100
libswresample 3. 4.100 / 3. 4.100
libpostproc 55. 4.100 / 55. 4.100
デプロイパッケージ準備
03.deploy_lambda.shはデプロイパッケージを生成し、Lambda関数を作成するためのスクリプトです。デプロイパッケージ生成元フォルダに関数本体やFFmpeg等を配置します。
デプロイパッケージファイル構成
├──argparser.js・・・パラメータ(S3からのイベントデータ)のパーサ
├──ffmpeg・・・FFmpegコマンド
■ PCM->WAV変換仕様 ■
形式 | Sample Rate | Bit Depth | channel | エンディアン |
PCM | 16000 | 符号付き16 | 1 | リトル |
WAV | 44100 | 符号付き16 | 2 | リトル |
[pg-a@kumoto]$ cd /mnt/c/temp/awscli-lambda-layer/include-aws-cli
[pg-a@kumoto]$ vi function.sh
[pg-a@kumoto]$ cat function.sh
function handler () {
EVENT_DATA=$1
S3_EVENT=$(node $LAMBDA_TASK_ROOT/argparser.js "$EVENT_DATA")
S3_BUCKET=$(echo $S3_EVENT | cut -d' ' -f1)
S3_SRC_PATH=$(echo $S3_EVENT | cut -d' ' -f2)
FILE_NAME=$(echo $S3_SRC_PATH | cut -d/ -f2)
LAMBDA_TEXT_FILE="$(mktemp)"
aws s3 cp s3://${S3_BUCKET}/${S3_SRC_PATH} ${LAMBDA_TEXT_FILE}
LAMBDA_PCM_FILE="$(mktemp)"
aws polly synthesize-speech --output-format pcm --sample-rate 16000 --text-type text --text "$(cat ${LAMBDA_TEXT_FILE})" --voice-id Mizuki --language-code ja-JP ${LAMBDA_PCM_FILE}
S3_PCM_FILE=s3://${S3_BUCKET}/tmp/${FILE_NAME}.pcm
aws s3 cp ${LAMBDA_PCM_FILE} ${S3_PCM_FILE}
LAMBDA_WAV_FILE="$(mktemp).wav"
$LAMBDA_TASK_ROOT/ffmpeg -y -f s16le -ar 16000 -ac 1 -i ${LAMBDA_PCM_FILE} -ar 44100 -ac 2 ${LAMBDA_WAV_FILE}
S3_WAV_FILE=s3://${S3_BUCKET}/dst/${FILE_NAME}.wav
aws s3 cp ${LAMBDA_WAV_FILE} ${S3_WAV_FILE}
RESPONSE="upload: '$S3_WAV_FILE'"
echo $RESPONSE
}
[pg-a@kumoto]$ vi argparser.js
[pg-a@kumoto]$ cat argparser.js
var obj = JSON.parse(process.argv[2]);
process.stdout.write(obj.Records[0].s3.bucket.name+" "+obj.Records[0].s3.object.key);
[pg-a@kumoto]$ mv ../../ffmpeg-git-20190114-amd64-static/ffmpeg .
関数コードの説明
S3からのイベントデータはJSON形式ですが、Lambda環境にはデフォルトでNode.jsランタイムが用意されているのでjsコードで手っ取り早くパースして、バケット・キーを取り出します。
FFmpegの出力先WAVファイル名は".wav"で終わる必要があるらしいです。mktempで作成した拡張子のままだと"Unable to find a suitable output format for '/tmp/tmp.hzosA2AYdb'"のようなエラーが出て変換してくれませんでした。(要調査)
デプロイパッケージアップロード
03.deploy_lambda.shによりLambda関数を作成します。以下2点についてスクリプトをカスタマイズ後に実行します。
- デプロイパッケージにモジュール追加
- タイムアウト時間を延長(30秒だとギリギリ)
[pg-a@kumoto]$ cd /mnt/c/temp/awscli-lambda-layer/shell
[pg-a@kumoto]$ cp 03.deploy_lambda.sh 03.deploy_lambda.sh.org
[pg-a@kumoto]$ vi 03.deploy_lambda.sh
[pg-a@kumoto]$ diff 03.deploy_lambda.sh 03.deploy_lambda.sh.org
14c14
< zip function.zip -j ./include-aws-cli/ffmpeg ./include-aws-cli/function.sh ./include-aws-cli/argparser.js
---
> zip function.zip -j ./include-aws-cli/function.sh
22c22
< --timeout 900 \
---
> --timeout 30 \
[pg-a@kumoto]$ cd /mnt/c/temp/awscli-lambda-layer
[pg-a@kumoto]$ ./shell/03.deploy_lambda.sh tts-function lambdaTTS default
adding: ffmpeg (deflated 66%)
adding: function.sh (deflated 53%)
adding: argparser.js (deflated 18%)
{
"Layers": [
{
"CodeSize": 10356445,
"Arn": "arn:aws:lambda:ap-northeast-1:************:layer:aws-cli-layer:4"
}
],
"FunctionName": "tts-function",
"LastModified": "2019-01-**T**:**:**.875+0000",
"RevisionId": "d2f99d89-19df-447a-9952-485ff80e7afa",
"MemorySize": 128,
"Version": "$LATEST",
"Role": "arn:aws:iam::************:role/lambdaTTS",
"Timeout": 900,
"Runtime": "provided",
"TracingConfig": {
"Mode": "PassThrough"
},
"CodeSha256": "pEQux/F1dHDJw2xKoUmiE7SJkYvTfHyx4d++yT11uwM=",
"Description": "",
"CodeSize": 24338621,
"FunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:tts-function",
"Handler": "function.handler"
}
S3バケットへのファイルPUTイベントによるLambda関数の起動を設定します。srcフォルダのみを対象とするようプレフィックスを指定します。
[pg-a@kumoto]$ aws lambda add-permission --function-name tts-function --statement-id "s3-put-event" --action "lambda:InvokeFunction" --principal "s3.amazonaws.com" --source-arn "arn:aws:s3:::pg-a-tts-kumoto"
{
"Statement": "{\"Sid\":\"s3-put-event\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:************:function:tts-function\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:s3:::pg-a-tts-kumoto\"}}}"
}
[pg-a@kumoto]$ vi notification.json
[pg-a@kumoto]$ cat notification.json
{
"LambdaFunctionConfigurations": [
{
"LambdaFunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:tts-function",
"Filter": {
"Key": {
"FilterRules": [
{
"Name": "Prefix",
"Value": "src/"
}
]
}
},
"Events": [
"s3:ObjectCreated:Put"
]
}
]
}
[pg-a@kumoto]$ aws s3api put-bucket-notification-configuration --bucket "pg-a-tts-kumoto" --notification-configuration file://./notification.json
検証
srcフォルダにテキストファイルをPUTし、dstフォルダにWAVファイルができあがるのを待ちます。所要時間は文字数が8文字でも1000文字でもそれほど変わらず、30秒前後でした。
[pg-a@kumoto]$ cd /mnt/c/temp
[pg-a@kumoto]$ echo "日本語メッセージ" > msg.txt
[pg-a@kumoto]$ aws s3 cp msg.txt s3://pg-a-tts-kumoto/src/msg.txt
upload: ./msg.txt to s3://pg-a-tts-kumoto/src/msg.txt
[pg-a@kumoto]$ aws s3 ls s3://pg-a-tts-kumoto/dst/msg.txt.wav
2019-01-** **:**:** 214242 msg.txt.wav
WAVファイルが期待する形式になっていることを確認します。
[pg-a@kumoto]$ aws s3 cp s3://pg-a-tts-kumoto/dst/msg.txt.wav msg.wav
download: s3://pg-a-tts-kumoto/dst/msg.txt.wav to ./msg.wav
[pg-a@kumoto]$ soxi msg.wav
Input File : 'msg.wav'
Channels : 2
Sample Rate : 44100
Precision : 16-bit
Duration : 00:00:01.21 = 53541 samples = 91.0561 CDDA sectors
File Size : 214k
Bit Rate : 1.41M
Sample Encoding: 16-bit Signed Integer PCM
まとめ
「カスタムAWS Lambdaランタイム」便利です。これまでであれば、Lambdaが対応していないランタイムがあればEC2で対応、という流れだったと思うのですが、(Amazon)Linuxについてはそれが不要になってしまいました。15分以内に完結する処理であればLambda「が」良いです。
依存する共有ライブラリを意識しなければならず、どんなコマンドでも簡単にLambdaに持ち込める訳ではないですが、発表されたばかりの技術ですので、今後出てくるであろうベストプラクティスに期待です。
(いちばん最初の話に戻ると、サービスを考える立場の人間から言わせれば、ホントは役場の配信システムがAPI公開してくれればLambdaと連携するシステムも簡単に作れそうなのですが、なかなかそこまで一足飛びには行かないものです...。)