対象読者: AWSをこれから触る人。 AWS勉強シリーズの7本目で、今回はLambdaです。全体の地図は索引記事にあります。
Lambdaの説明でよく出る「サーバーレス」で、こういう疑問に引っかかった人向けに書きます。
- 「サーバーレスと言うけど、じゃあコードはどこで動いてるの?」
- 「EC2があるのに、なんでもう1個実行サービスが要るの?」
どちらも「常勤か、呼び出しか」の1点で答えが出ます。

EC2とLambdaの違い(筆者作成)。左が常勤、右が呼び出し。この記事は右側の話です。
Lambdaとは何か
AWS Lambda(ラムダ)は、サーバーを自分で用意せずに、コード(関数)を置いておくだけで実行してくれるサービスです。「サーバーレス」はサーバーが無いという意味ではなく、サーバーの用意・OSの管理・台数の増減をAWS側が全部やるので、利用者からはサーバーが見えないという意味です。コードは実行のたびにAWSが用意した環境の上で動いて、終わったら消えます。
上の図の通りで、EC2が「常勤の料理人を雇う」(サーバーを1台確保して動かし続ける)なのに対して、Lambdaは「注文が入った時だけ出張シェフを呼ぶ」。呼ばれていない間は何も動いていないので、料金もかかりません。
何に使うか
- 何かが起きた時に小さな処理を走らせる: S3に画像が置かれたらサムネイルを作る、SQSにメッセージが来たら処理する。この「何かが起きた」をイベントと呼びます
- たまにしか動かない処理: 1日1回の集計、週1のレポート送信。そのためだけにEC2を24時間立てるのはムダなので、Lambda向き
- APIの裏側: API Gatewayと組み合わせて、リクエストが来た時だけ動くWeb APIを作る
- AWS内のサービス同士の接着剤: 索引記事の完成形の図では、SQSから写真の情報を取り出してSNSに通知を渡す役をLambdaがやっています

シリーズの完成形(筆者作成)。Lambdaは「AWS Lambda(関数)」の箱で、SQSから取り出してSNSに渡す役です。
EC2との違い
| EC2 | Lambda | |
|---|---|---|
| 動いている時間 | 起動してから止めるまでずっと | 呼ばれてから終わるまでだけ(最長15分) |
| 料金 | 起動中ずっと(待機中も) | 実行時間×メモリ量+呼び出し回数 |
| OSの管理 | 自分でやる(パッチ、SSH) | 無い。触れない |
| 置くもの | OSごと丸ごと | 関数のコードだけ |
| 向くもの | 常時動くもの(Webサーバー、DB) | イベントのたびに動く短い処理 |
「15分を超える処理」「常時リクエストを受け続けるもの」はEC2(かECS)、「たまに来るイベントに反応する短い処理」はLambda、が基本の線引きです。
重要用語
| 用語 | 意味 |
|---|---|
| 関数(Function) | Lambdaに置くコードの単位。1関数=1つの仕事 |
| ハンドラー(Handler) | 呼ばれた時に最初に実行される関数。ファイル名.関数名 で指定する |
| イベント(event) | 呼び出し時に渡されるデータ。S3から呼ばれれば「どのバケットの何が置かれたか」が入っている |
| ランタイム(Runtime) | 実行環境の言語とバージョン。python3.12 など |
| 実行ロール | この関数がAWSの何を触っていいかを決めるIAMロール。関数にも権限が要る |
| コールドスタート | しばらく呼ばれていない関数の初回起動が遅くなる現象。数百ミリ秒〜数秒 |
仕組みの要点
Lambdaの関数は、Pythonならこれだけです。
def handler(event, context):
return {"ok": True, "name": event.get("name")}
event に呼び出し元からのデータが入ってきて、returnしたものが呼び出し元に返る。この関数をzipに固めてAWSに置くと、あとは呼ばれるたびにAWSがどこかのサーバーでこれを実行します。
もう1つ、初心者が見落とすのが実行ロールです。IAMの記事で「EC2にはロールを付けてS3を読ませる」とやりましたが、Lambdaも同じで、関数を作る時に必ずIAMロールを1つ指定します。関数がS3を読むならS3の読み取り権限をそのロールに付けます。「関数は動いたのにS3でAccessDenied」の原因はここです。
このコードを動かす前提
この記事のコードはPython(boto3)です。動かす方法は2通りあります。
A. AWSアカウント無しで試す(おすすめ): motoというAWSのそっくりさんを使うと、課金もサインアップもなしでPCの中だけで動きます。
python3 -m venv venv
./venv/bin/pip install boto3 moto
from moto import mock_aws
@mock_aws # この中のboto3呼び出しは全部ローカルの偽AWSに行く
def main() -> None:
... # 以下の記事のコードをここに入れる
main()
B. 本物のAWSで動かす: 先にIAMユーザーとアクセスキーの設定が要ります。手順はIAM記事の「最初の1回だけやる手順」にあります。
使い方
「ロールを作る→コードをzipにする→関数を作る→呼ぶ」の4手です。コードをzipに固めるところまで含めて、全部Pythonでやります。
import io, json, textwrap, zipfile
import boto3
iam = boto3.client("iam")
lam = boto3.client("lambda", region_name="ap-northeast-1")
# ① 実行ロールを作る(Lambdaが引き受けていい、というロール)
trust = {"Version": "2012-10-17", "Statement": [{"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}
role = iam.create_role(RoleName="lambda-basic",
AssumeRolePolicyDocument=json.dumps(trust))["Role"]["Arn"]
# ② 関数のコードをzipに固める(ファイル名は lambda_function.py にする)
code = textwrap.dedent("""
def handler(event, context):
return {"ok": True, "name": event.get("name")}
""")
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w") as z:
z.writestr("lambda_function.py", code)
# ③ 関数を作る。Handlerは「ファイル名.関数名」
fn = lam.create_function(FunctionName="hello", Runtime="python3.12", Role=role,
Handler="lambda_function.handler",
Code={"ZipFile": buf.getvalue()},
Timeout=10, MemorySize=128)
print(fn["FunctionName"], fn["State"]) # -> hello Active
# ④ 呼ぶ
res = lam.invoke(FunctionName="hello",
Payload=json.dumps({"name": "taro"}).encode())
print(res["StatusCode"], res["Payload"].read())
③まで通ると、関数は Active になります。実測の出力です。
hello python3.12 MemorySize=128 Timeout=10 State=Active
サーバーの起動を待つ工程がどこにも無いのがEC2との一番の違いで、create_function が返ってきた時点でもう呼べる状態です。
設定だけ後から変えられる
タイムアウトとメモリは、コードを再アップロードせずに変えられます。
lam.update_function_configuration(FunctionName="hello", Timeout=30, MemorySize=512)
c = lam.get_function_configuration(FunctionName="hello")
print(c["Timeout"], c["MemorySize"]) # -> 30 512
メモリを増やすとCPUも比例して速くなる(メモリ設定がそのまま性能設定を兼ねる)仕様なので、「遅い」と思ったら最初に触るのはここです。既定の128MBはかなり非力です。
実際に叩くと止まるところ
作り方を間違えたときに何が返るか、5パターン実際に叩きました。
| やったこと | 返ってきたエラー | 意味 |
|---|---|---|
存在しないロールARNで create_function
|
InvalidParameterValueException: The role defined for the function cannot be assumed |
ロールが先。①を飛ばすと関数は作れない |
| 同名の関数をもう一度作る | ResourceConflictException: Function already exist |
作り直しは delete_function してから。コード更新なら update_function_code
|
無い関数名で invoke
|
ResourceNotFoundException: Function not found |
関数名のタイプミスか、別リージョンを見ている |
変なランタイム(python2.5)で作成 |
InvalidParameterValueException(moto)。実AWSでは対応一覧にない値は弾かれる |
ランタイムは決まった文字列から選ぶ。python3.12 等 |
削除した関数を get_function
|
ResourceNotFoundException |
消えるのは即時。EC2のような「終了中」の待ちは無い |
この実測の限界を1つ。motoで invoke の実行そのものを再現するにはDocker(関数を動かす箱)が必要で、Dockerなしの環境では④だけ error running docker が返ります。①〜③と設定変更・エラー系は全部Dockerなしで動くので、関数の作り方の練習には困りません。④の実行結果を手元で見たい人はDocker Desktopを起動してから叩いてください。
似たサービスとの使い分け
| 迷うところ | 答え |
|---|---|
| EC2かLambdaか | 常時動くならEC2、イベントのたびに動くならLambda。15分超の処理はLambda不可 |
| Lambdaのコードが大きくなってきた | zipで50MB(展開250MB)が上限。超えるならコンテナイメージ形式か、そもそもECS/EC2を検討 |
| 定期実行したい | EventBridgeのスケジュール(cron式)からLambdaを呼ぶ。EC2でcrontabを書くよりサーバー管理が無い分だけ楽 |
| SQSと組み合わせたい | LambdaのトリガーにSQSを指定すると、ポーリングと呼び出しをAWSがやってくれる。自分で receive_message のループを書かなくていい |
料金の考え方
課金は2つの掛け算です。
- リクエスト料: 100万回の呼び出しで0.20 USD
- 実行時間料: メモリ量(GB)×実行秒数。128MBで1秒動かすと約0.0000021 USD
毎月の無料枠(100万リクエスト+40万GB秒)が常設なので、個人の学習で課金が発生することはまずありません。逆に事故るのは無限ループで、LambdaがS3に書き込み、そのS3イベントで同じLambdaがまた呼ばれる、という循環を作ると呼び出しが止まらなくなります。トリガーを設定するときは「この関数の出力が、この関数の入力にならないか」を一度確認してください。
よくある注意点
-
実行ロールを忘れない。 関数の中でS3やSQSを触るなら、その権限を実行ロールに付ける。関数自体は動くのに中のboto3が
AccessDeniedになったら、まず実行ロールを見る - 15分で強制終了。 タイムアウトの上限は900秒。長い処理は分割するか、そもそもLambdaの仕事ではない
- コールドスタートがある。 しばらく呼ばれていない関数は初回だけ起動が遅い。人が待つAPIで毎回速さが要るなら、プロビジョニング済み同時実行という有料の常時待機オプションがあるが、入門の段階では気にしなくていい
-
ローカルのライブラリは持ち込みが要る。
pip installしたものは自動では入らない。zipに同梱するか、レイヤーという仕組みで足す。boto3だけは最初から入っている
まとめ
冒頭の2つの疑問に戻ります。
- 「コードはどこで動いてるの?」→ AWSが呼び出しのたびに用意する実行環境の上。利用者から見えないだけでサーバーはある。だから「サーバーレス」は「管理レス」と読み替えるのが正確です
- 「EC2があるのになんで要るの?」→ 常勤(EC2)と呼び出し(Lambda)の違い。たまにしか動かない処理のために24時間サーバーを立てる無駄を消すのがLambdaです
これでシリーズの完成形の図(索引記事の冒頭)に出てくる7サービスが全部そろいました。写真をアップロードすると通知が届くアプリの、どの箱が何をするか、全部の記事へのリンクは索引にあります。
次回はRDSとDynamoDB(データベース)を予定しています。
参考
- AWS Lambda 公式ドキュメント
- 前回: 【図解】AWS VPCとは
- シリーズ索引: AWSとは結局何なのか