LocalStackを使って AWS Lambdaを味わってみます。
サーバーエンジニアとして就職活動を行っておりますと、必須経験に AWSや Azureといったパブリッククラウドの運用が出てくるんですが、求人ニーズに対して「実務」経験としてこれを積んでいる人はそう多くないと思いますがねぇ…。
それと個人でやるにゃ貧乏エンジニアにはパブリッククラウドは高過ぎるのと、ミスると大きく課金されたまま気づかないってのも少々怖い。
という訳で、(金銭的に)安全な AWSのエミュレーターの LocalStack(Free Edition)を使って、せめて「こういうものだよね」くらいの知識は得ておこうというものです。
ここでは数ある AWSの諸々のサービスのうちLambdaを自宅のローカル環境でやってみようと思います。
Lambdaは FaaS(Function as a Service)と呼ばれるサービスで、AWS内で発生する様々なイベントをトリガに自作プログラムを実行してくれる環境です。
1回実行でいくらという料金体系で 100万回/月までタダのようです。
2025年8月14日時点では Lambdaで実行できるプログラムの開発言語として JavaScript・TypeScript・Python・Ruby・Java・Go・C#・Powershell・Rustがラインナップされています。
なお、私は当初ランバダかと思っていたんですが、正しくはラムダです。
1.環境
環境は以下の通りです。
- Ubuntu Server 24.04
- Docker Engine - Community 28.3.3
- LocalStack 4.7.0
Ubuntu Serverのインストールは「Ubuntu 24.04 Server インストール」に、Dockerエンジンのインストールは「Dockerエンジン インストール」に、LocalStackのインストールは「LocalStackを使う (AWSエミュレーター)」にそれぞれ書いており、そこで作った環境が前提になっています。
これに加えて「LocalStackを使う (AWSエミュレーター)」の通り、 AWS CLIに含まれるコマンド群を実行するクライアントとして Lubuntu 24.04があり、ネットワーク経由でLocalStackを使う構成にしています。
2.LocalStack準備
まずは Ubuntu Serverで LocalStackをスタートするところから。
「LocalStackを使う (AWSエミュレーター)」に書いている途中からですが、LocalStackを起動します。
(localstackコマンドの前に [GATEWAY_LISTEN]環境変数を設定しておくことで外部ノードからのアクセスを可能にしています。)
localstack@UbuntuServer2404-1:~$ GATEWAY_LISTEN=0.0.0.0:4566 localstack start
__ _______ __ __
/ / ____ _________ _/ / ___// /_____ ______/ /__
/ / / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
/ /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
/_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|
- LocalStack CLI: 4.7.0
- Profile: default
- App: https://app.localstack.cloud
[07:01:15] starting LocalStack in Docker mode 🐳 localstack.py:532
2025-08-12T07:01:15.662 WARN --- [ MainThread] localstack.utils.bootstrap : Non-prefixed environment variable GATEWAY_LISTEN is forwarded to the LocalStack container! Please use `LOCALSTACK_GATEWAY_LISTEN` instead of GATEWAY_LISTEN to explicitly mark this environment variable to be forwarded from the CLI to the LocalStack Runtime.
────────────────────────────────────────────────────────────────────────── LocalStack Runtime Log (press CTRL-C to quit) ───────────────────────────────────────────────────────────────────────────
LocalStack version: 4.7.1.dev29
LocalStack build date: 2025-08-08
LocalStack build git hash: c1d19e211
Ready.
立ち上がりました。
ターミナルをもう一つ開いて LocalStackを動かしているサーバー(Ubuntu Server)に sshでログインして状態を確認しました。
localstack@UbuntuServer2404-1:~$ localstack status services
┏━━━━━━━━━━━━━┳━━━━━━┓
┃ Service ┃ Status ┃
┡━━━━━━━━━━━━━╇━━━━━━┩
│ acm │ ✔ available │
│ apigateway │ ✔ available │
│ cloudformation │ ✔ available │
│ cloudwatch │ ✔ available │
│ config │ ✔ available │
│ dynamodb │ ✔ available │
│ dynamodbstreams │ ✔ available │
│ ec2 │ ✔ available │
│ es │ ✔ available │
│ events │ ✔ available │
│ firehose │ ✔ available │
│ iam │ ✔ available │
│ kinesis │ ✔ available │
│ kms │ ✔ available │
│ lambda │ ✔ available │
│ logs │ ✔ available │
│ opensearch │ ✔ available │
│ redshift │ ✔ available │
│ resource-groups │ ✔ available │
│ resourcegroupstaggingapi │ ✔ available │
│ route53 │ ✔ available │
│ route53resolver │ ✔ available │
│ s3 │ ✔ available │
│ s3control │ ✔ available │
│ scheduler │ ✔ available │
│ secretsmanager │ ✔ available │
│ ses │ ✔ available │
│ sns │ ✔ available │
│ sqs │ ✔ available │
│ ssm │ ✔ available │
│ stepfunctions │ ✔ available │
│ sts │ ✔ available │
│ support │ ✔ available │
│ swf │ ✔ available │
│ transcribe │ ✔ available │
└─────────────┴──────┘
良さげです。
これで LocalStack側はOKです。
クライアントの Lubuntuでは awslocalコマンドの利用するため Pythonの仮想環境を使うのと、[LOCALSTACK_HOST]環境変数に LocalStackのサーバーのマシン名を設定しておきます。
([UbuntuServer2404-1] は [/etc/hosts]ファイルにて名前解決しています。)
subro@Lubuntu2404:~/work/localstack$ source localstack-env/bin/activate
(localstack-env) subro@Lubuntu2404:~/work/localstack$ export LOCALSTACK_HOST=UbuntuServer2404-1
後で Lambdaに登録するプログラムを作りますので、カレントディレクトリを
[~/work/localstack] としています。
これで Lubuntuから LocalStackにアクセスする準備ができました。
Lambdaに作成したプログラムを登録するのに Zip圧縮が必要ですが、Ubuntu(Lubuntu)はデフォルトだと zipコマンドがありません。
これをインストールしておきます。
(localstack-env) subro@Lubuntu2404:~/work/localstack$ sudo apt install -y zip
これで一通り準備できました。
3.Lambdaのドキュメント
公式のドキュメントはこちら。
AWS Lambda とは
基本的にこのドキュメント以下に沿って進めていきます。
Lambdaのお勉強としては開発言語は何でも良いので、とりあえず JavaScriptにすることとして、そのドキュメントはこちら。
Node.js による Lambda 関数の構築
このドキュメントの冒頭では AWSが提供している Lambdaコンソールという画面で JavaScriptを記述していくように書かれていますが、LocalStackにはそれがありませんのでお作法だけ真似ることとします。
実際に JavaScriptが実行されるのは Node.jsのようで、2025年8月14日時点では Ver.18・20・22が用意されていました。
ここでは Ver.22を使うことにします。
4.JavaScriptでファンクションを作ってみる
公式ドキュメントの記載は JavaScriptが分からないと難解な内容なんですが、本当に必要なところだけピックアップするとこんな感じです。
- [mjs]拡張子つきの JavaScriptファイルを作る。
- [mjs]拡張子のファイルには ESモジュールの形式でソースを書く。
- Lambdaで実行されるのは [handler]変数に紐付いた関数である。
- このファイルを Zip圧縮する。
- Zip圧縮ファイルを awslocalコマンドで Lambdaに登録する。
作成するJavaScriptのプログラムは単なる Hello Worldです。
ここからファイル名などに aaa やら bbb など使っていきますが、後ほど実行する awslocalコマンドの書式のどこに何が対応しているのか分かるようにするためです。
Lubuntu 24.04で JavaScriptファイルを [aaa.mjs] という名前で作ります。
内容はこんな感じ。
export const handler = async(event) => {
console.log("Hello World!\n")
return 0
}
できあがったファイルの様子。
(localstack-env) subro@Lubuntu2404:~/work/localstack$ ls -l
合計 4
-rw-rw-r-- 1 subro subro 87 8月 14 05:41 aaa.mjs
[aaa.mjs]ファイルを Zip圧縮して [bbb.zip]fファイルを作ります。
(localstack-env) subro@Lubuntu2404:~/work/localstack$ zip bbb.zip aaa.mjs
adding: aaa.mjs (deflated 3%)
(localstack-env) subro@Lubuntu2404:~/work/localstack$ ls -l
合計 8
-rw-rw-r-- 1 subro subro 87 8月 14 05:41 aaa.mjs
-rw-rw-r-- 1 subro subro 248 8月 14 05:43 bbb.zip
[bbb.zip]ファイルができました。
このファイルを awslocal lambda create-functionコマンドで LocalStackに登録します。
コマンドリファレンスはこちら。
create-function
--function-name | この関数につける名前 |
--zip-file | 圧縮ファイル名の前に[fileb://]と書きましょう。 (blobの意味らしい) |
--handler | Lambdaが実行するハンドラ名 zip圧縮ファイルでのデプロイに必須とあります |
--runtime | プログラムの実行環境 |
--role | ARN(Amazon Resource Name)で指定するロール 数字の部分は AWSアカウントのIDで数字12桁です |
ですので実行ロールは Lambdaへのファンクション登録時に必要ながらも中身は確認されてなく、適当なものを入れておけば良いようです。
いよいよ LocalStackの Lambdaに登録をします。
LocalStack上での名称を [hello-world] としています。
(オプションが長いので途中で改行していて、実際に入力しているのが緑色のところ。)
(localstack-env) subro@Lubuntu2404:~/work/localstack$ awslocal lambda create-function \
> --function-name hello-world \
> --zip-file fileb://bbb.zip \
> --handler aaa.handler \
> --runtime nodejs22.x \
> --role arn:aws:iam::000000000000:role/tekitou
{
"FunctionName": "hello-world",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:hello-world",
"Runtime": "nodejs22.x",
"Role": "arn:aws:iam::000000000000:role/tekitou",
"Handler": "aaa.handler",
"CodeSize": 248,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2025-08-13T20:51:09.792040+0000",
"CodeSha256": "PzHkok35ExoMdsaVBhHh7IcozBLIk+Gj4id3ei/xofM=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "276443b6-b34c-4fde-8ba4-318c13b39cf2",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/hello-world"
}
}
登録できて JSON形式の出力がありました。
ピンク色の箇所がコマンドで入れたものと対応する箇所ですね。
手動実行してみましょう。
(localstack-env) subro@Lubuntu2404:~/work/localstack$ awslocal lambda invoke --function-name hello-world out --log-type Tail
{
"StatusCode": 200,
"LogResult": "U1RBUlQgUmVxdWVzdElkOiAyNDM3NzM3NC05ZTZiLTRmZTAtYWFjMi05OGQ1OTc1OTMyZDIgVmVyc2lvbjogJExBVEVTVAoyMDI1LTA4LTEzVDIwOjU3OjQxLjkxNloJMjQzNzczNzQtOWU2Yi00ZmUwLWFhYzItOThkNTk3NTkzMmQyCUlORk8JSGVsbG8gV29ybGQhDQpFTkQgUmVxdWVzdElkOiAyNDM3NzM3NC05ZTZiLTRmZTAtYWFjMi05OGQ1OTc1OTMyZDIKUkVQT1JUIFJlcXVlc3RJZDogMjQzNzczNzQtOWU2Yi00ZmUwLWFhYzItOThkNTk3NTkzMmQyCUR1cmF0aW9uOiA4LjM0IG1zCUJpbGxlZCBEdXJhdGlvbjogOSBtcwlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTI4IE1CCQo=",
"ExecutedVersion": "$LATEST"
}
[LogResult] に何やら暗号的なものが出てきました。
base64でエンコードされたログ(最大 4k)になってるらしいです。
今度は出力結果を JSONじゃなくテキスト形式で出力するようにし、パイプで base64コマンド(base64 encode/decode data and print to standard output)に渡してデコードしてみます。
(localstack-env) subro@Lubuntu2404:~/work/localstack$ awslocal lambda invoke --function-name hello-world out --log-type Tail --query 'LogResult' --output text | base64 -d
START RequestId: fe06d3bf-710b-4a71-b1a8-92f390ae0d99 Version: $LATEST
2025-08-13T21:02:24.695Z fe06d3bf-710b-4a71-b1a8-92f390ae0d99 INFO Hello World!
END RequestId: fe06d3bf-710b-4a71-b1a8-92f390ae0d99
REPORT RequestId: fe06d3bf-710b-4a71-b1a8-92f390ae0d99 Duration: 1.16 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 128 MB
[LogResult] の内容が読めるようになって「Hello World!」が書いてありました。
\(^o^)/
これで終わりです。
==========
LocalStackの Lambdaで JavaScriptのコードを実行してみました。
「簡単〜」と言われていますが、未経験者がやるには前提知識が結構必要ですね。
単に自分の PCで開発したプログラムを実行してるのとは違います。
ここでの実験ではコンソールに出力したものを拾っていますが、実際にはこういうことはしないと思います。
入力と出力は S3など AWSの他サービスと連携するのが一般かと。
ただ他サービスとの連携という話になると別に Lambda上じゃないと連携できないというのではなく、作ったプログラムの内部で他サービス用の APIを実行するってことになるんでしょう。
そうなると自分の PC上の Node.jsで作ってテストしてから、Lambdaで実行って手順になるんでしょうね。
LocalStackでやる妙味があるとすれば、他サービスで発生したイベントをトリガに Lambdaファンクションを実行するという連携の連続を設定することになるかと思いますが、それはまた LocalStackの別テーマになりますかね。
それにしても LocalStack…良いですね。
散々 LocalStackでいじってから AWSでちょっと課金、とできると良いな〜と思いました。
LocalStackとこういう本でガッツリ勉強。
でも日本語の新しい Lambda本ってこれだけ?