Serverless Operations, inc

>_cd /blog/id_q_jbc6mp3a

title

AWS Lambda MicroVMs に Node.js アプリをデプロイして、サスペンド/レジュームで状態保持を体感してみた

2026年6月22日に発表された AWS Lambda MicroVMs を、東京リージョンで実際に触ってみました。

https://aws.amazon.com/jp/blogs/aws/run-isolated-sandboxes-with-full-lifecycle-control-aws-lambda-introduces-microvms/

ローカルで書いたシンプルな Node.js アプリを MicroVM 上で動かし、最終的に「サスペンド/レジュームをまたいでもアプリの状態が消えない」という MicroVMs 最大の特徴を、数字で確認するところまでやってみました。

Lambda MicroVMs とは

ひとことで言うと、ユーザーや AI が生成したコードを、隔離されたステートフルな環境で実行できる新しいサーバーレス基盤です。Lambda 関数を支えてきた軽量仮想化技術 Firecracker がベースになっています。

従来は、こういう「他人の書いたコードを安全に実行したい」用途で次のトレードオフを強いられていました。

  • 仮想マシン: 隔離は強いが起動に数分かかる
  • コンテナ: 起動は速いがホストのカーネルを共有するため独自のハードニングが必要
  • FaaS(従来の Lambda 関数): 長時間の対話セッションや状態保持には向かない

MicroVMs はこの隙間を埋めるもので、主に次の3つを同時に提供します。

  1. VM レベルの隔離 — セッションごとに専用 MicroVM。カーネルもリソースも他ユーザーと共有しない
  2. 高速な起動・再開 — スナップショットから復元するのでコールドブートしない
  3. ステートフルな実行 — メモリ・ディスク・実行中プロセスをセッション中ずっと保持し、アイドル時はサスペンド、トラフィック到着時にレジュームできる(最大8時間)

AI コーディングアシスタント、対話型コード環境、データ分析基盤、脆弱性スキャナーなどが代表的なユースケースです。

MicroVMs と FireCracker

Firecracker は、AWS が 2018 年にオープンソースとして公開した 軽量仮想化技術(VMM=Virtual Machine Monitor) です。Rust で書かれていて、Linux の KVM の上で動きます。

特徴は「VM の強い隔離を保ちつつ、コンテナ並みに軽くて速い」こと。これを実現するために、通常の仮想マシンが持っている大量の仮想デバイス(GUI、USB、各種エミュレーション等)をばっさり削ぎ落とし、サーバーレス用途に必要な最小限の機能だけに絞った「マイクロな VM」を作ります。その結果、

  • 起動が非常に速い(歴史的に約125ミリ秒オーダー)
  • 1 VM あたりのメモリオーバーヘッドが小さい(数MB程度)
  • 削ぎ落とした分だけ攻撃対象面(attack surface)が小さい=セキュアにしやすい
  • 各 microVM が自分専用のゲストカーネルを持つ(前回説明したカーネル共有問題が無い)

という性質を持ちます。重要なのは、Firecracker はもともと AWS Lambda や AWS Fargate の裏側で何年も使われてきた実績ある基盤だということです。Lambda の月間15兆回超とも言われる呼び出しを支えてきたのが、この Firecracker です。ただし従来、ユーザーがその存在を直接意識することはありませんでした。

Lambda MicroVMs は、2026年6月22日に発表された AWS の新しいサーバーレス基盤で、これまで裏方だった Firecracker の microVM を、ユーザーが直接起動・制御できる形で表に出したものです。

提供する価値は大きく3つ。

ひとつめは VM レベルの隔離。Firecracker 由来で、セッションごとに専用 microVM が立ち、カーネルもリソースも他ユーザーと共有しません。だから他人や AI が書いた信用できないコードを安全に走らせられます。

ふたつめは 高速な起動・再開。Dockerfile からイメージを作る際に、アプリが起動しきった状態の Firecracker スナップショットを取っておき、実行時はそこから復元します。コールドブートしないので、ほぼ瞬時に立ち上がります。

みっつめは ステートフルな実行。メモリ・ディスク・実行中プロセスをセッション中ずっと保持し、アイドル時は状態を保ったままサスペンド、トラフィック到着で自動レジュームできます(最大8時間)。前回の検証で notesuptimeSeconds が消えずに残ったのが、まさにこれです。

加えて、ライフサイクルフック(/run/suspend/resume/terminate や、ビルド時の /ready/validate)で、起動・サスペンド・再開・終了の各タイミングに独自処理を差し込めます。提供は東京を含む5リージョン、ARM64 上で最大 16 vCPU / 32GB メモリ / 32GB ディスクです。

これは今回のリリースの大きな特徴です。Firecrackerが前面にでてきたからこそ、ユーザーは microVM の起動・凍結・解凍・破棄という各段階に自分のコードを割り込ませられるようになりました。従来の Lambda 関数では AWS がライフサイクルを握っていてユーザーは触れなかったため、こうしたフックは原理的に提供しようがなかったものです。主導権がユーザー側に移ったことが、この機能を可能にしています。

背景には、AI が生成したコードや、ユーザーが持ち込んだコードを、各人ごとに隔離して安全に実行したいというニーズの高まりがあります。AI コーディングアシスタント、対話型のコード実行環境、データ分析サンドボックス、脆弱性スキャナーなどです。

こうした用途では従来、「強い隔離(VM)」「速い起動(コンテナ)」「状態保持」の3つを同時に満たせず、どれかを諦めるか自前で複雑な基盤を組む必要がありました。Firecracker はこの3つを技術的に両立できる素地を持っていたので、それを製品として直接使えるようにしたのが Lambda MicroVMs、という流れです。実際、AWS は Anthropic の Claude 向けセルフホスト型サンドボックスの実行基盤としての利用例も公式に挙げています。

さっそくやってみる

AWS CLI のバージョンアップ

aws --version
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -o awscliv2.zip
sudo ./aws/install --update

更新後、サブコマンドが使えるか確認します。

aws lambda-microvms help

create-microvm-image などが表示されれば OK です。Invalid choice: 'lambda-microvms' が出る場合は、まだ古い CLI が 実行されています。

アプリと Dockerfile の準備

3つのファイルを同じフォルダに作ります。

const express = require("express");

const app = express();
const PORT = process.env.PORT || 5000;

// メモリ上の状態。通常の Lambda 関数ならコールドスタートのたびに消えるが、
// MicroVM ではセッション中(最大8時間)サスペンド/レジュームをまたいで残る。
const state = {
  bootedAt: new Date().toISOString(),
  requestCount: 0,
  notes: [],
};

app.use(express.json());

app.use((req, _res, next) => {
  state.requestCount += 1;
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
  next();
});

app.get("/", (_req, res) => {
  res.json({ message: "Hello from a Node.js MicroVM!" });
});

app.get("/state", (_req, res) => {
  res.json({
    bootedAt: state.bootedAt,
    uptimeSeconds: Math.round(process.uptime()),
    requestCount: state.requestCount,
    notesStored: state.notes.length,
    notes: state.notes,
  });
});

app.post("/notes", (req, res) => {
  const text = (req.body && req.body.text) || "";
  if (!text) {
    return res.status(400).json({ error: "Send JSON: { \"text\": \"...\" }" });
  }
  const note = { id: state.notes.length + 1, text, at: new Date().toISOString() };
  state.notes.push(note);
  res.status(201).json(note);
});

app.get("/health", (_req, res) => res.json({ status: "ok" }));

app.listen(PORT, "0.0.0.0", () => {
  console.log(`Server listening on http://0.0.0.0:${PORT}`);
});
{
  "name": "microvm-node-demo",
  "version": "1.0.0",
  "description": "A simple stateful Node.js app for AWS Lambda MicroVMs",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.21.2"
  }
}
FROM public.ecr.aws/lambda/microvms:al2023-minimal

RUN dnf install -y nodejs npm && dnf clean all

WORKDIR /app

COPY package.json package-lock.json* ./
RUN npm install --omit=dev

COPY app.js ./

EXPOSE 5000

CMD ["node", "app.js"]

public.ecr.aws/lambda/microvms:al2023-minimal)の上に Node を入れて、アプリを起動します。Lambda はこの「起動済みの状態」をスナップショットします。

ローカルでのテスト実行

npm install
npm start

Server listening on http://0.0.0.0:5000 が出たら、別ターミナルで以下を実行します。

curl http://localhost:5000/
{
  "message": "Hello from a Node.js MicroVM!"
}
curl -X POST http://localhost:5000/notes -H "Content-Type: application/json" -d '{"text":"hello"}'
{
  "id": 1,
  "text": "hello",
  "at": "2026-06-25T00:53:00.936Z"
}
curl http://localhost:5000/state
{
  "bootedAt": "2026-06-25T00:52:51.739Z",
  "uptimeSeconds": 9,
  "requestCount": 3,
  "notesStored": 1,
  "notes": [
    {
      "id": 1,
      "text": "hello",
      "at": "2026-06-25T00:53:00.936Z"
    }
  ]
}

/state のレスポンスに、POST したメモが入っていて requestCount が増えていれば成功です。この「状態がプロセス内に残る」挙動が、後で効いてきます。

S3 バケットを作って zip をアップロード

MicroVM イメージの作成では、コード成果物(zip)を S3 経由で渡すのが必須です(ローカルパス直接指定はできません)。

# zip 化(README は任意。Dockerfile / app.js / package.json があれば OK)
# ※ 解凍後のルート直下にこれらが並ぶように固めること
zip artifact.zip app.js package.json Dockerfile

# バケット作成 → アップロード
aws s3 mb s3://<BUCKET_NAME> --region ap-northeast-1
aws s3 cp artifact.zip s3://<BUCKET_NAME>/artifact.zip --region ap-northeast-1

IAM ロールの準備

Lambda はイメージビルド時にこのロールを引き受けて、S3 から zip を取得し、CloudWatch Logs にビルドログを書き込みます。これを先に作っておかないと、後でビルドが失敗します。

cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "lambda.amazonaws.com" },
    "Action": ["sts:AssumeRole", "sts:TagSession"]
  }]
}
EOF
aws iam create-role \
  --role-name MicroVMBuildRole \
  --assume-role-policy-document file://trust-policy.json
cat > build-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
    },
    {
      "Effect": "Allow",
      "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
      "Resource": "arn:aws:logs:*:*:*"
    }
  ]
}
EOF

aws iam put-role-policy \
  --role-name MicroVMBuildRole \
  --policy-name MicroVMBuildPolicy \
  --policy-document file://build-policy.json

以下のコマンドで作成できたか確認をおこないます。

aws iam get-role --role-name MicroVMBuildRole --query 'Role.Arn'
aws iam list-role-policies --role-name MicroVMBuildRole

MicroVM イメージを作成

zip と Dockerfile から、Firecracker スナップショットとしてのイメージを作ります。

aws lambda-microvms create-microvm-image \
  --name node-microvm-demo \
  --code-artifact uri=s3://<BUCKET_NAME>/artifact.zip \
  --base-image-arn arn:aws:lambda:ap-northeast-1:aws:microvm-image:al2023-1 \
  --build-role-arn arn:aws:iam::<ACCOUNT_ID>:role/MicroVMBuildRole \
  --region ap-northeast-1

レスポンスで "state": "CREATING" が返れば受付成功です。ビルドは非同期で進むので、完了を待ちます。確認時の --image-identifier には名前ではなくフル ARN を渡す点に注意してください。

aws lambda-microvms get-microvm-image \
  --image-identifier arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:microvm-image:node-microvm-demo \
  --region ap-northeast-1 \
  --query 'state'

"CREATED" になれば成功です。

MicroVM を起動

イメージが CREATED になったら、そこから MicroVM を起動します。受信(ingress)・送信(egress)のネットワークコネクタと、アイドルポリシー(15分無活動で自動サスペンド、トラフィックで自動レジューム)を指定します。

aws lambda-microvms run-microvm \
  --image-identifier arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:microvm-image:node-microvm-demo \
  --ingress-network-connectors "arn:aws:lambda:ap-northeast-1:aws:network-connector:aws-network-connector:ALL_INGRESS" \
  --egress-network-connectors "arn:aws:lambda:ap-northeast-1:aws:network-connector:aws-network-connector:INTERNET_EGRESS" \
  --idle-policy '{"autoResumeEnabled":true,"maxIdleDurationSeconds":900,"suspendedDurationSeconds":300}' \
  --region ap-northeast-1

レスポンスに microvmIdmicrovm-...)と endpointxxxx.lambda-microvm.ap-northeast-1.on.aws)が返ります。この2つを控えておきます。最初は PENDING で、スナップショットからの復元なので十数秒ほどで RUNNING になります。以下のコマンドでデプロイ状況を確認できます。

aws lambda-microvms get-microvm \
  --microvm-identifier <microvmId> \
  --region ap-northeast-1 \
  --query 'state'

認証トークンを発行して接続

MicroVM のエンドポイントへのリクエストには、必ず認証トークンが要ります。アプリが 5000 番で待ち受けているので、ポート 5000 を明示します。

aws lambda-microvms create-microvm-auth-token \
  --microvm-identifier <microvmId> \
  --expiration-in-minutes 30 \
  --allowed-ports '[{"port":5000}]' \
  --region ap-northeast-1

レスポンスの authToken.X-aws-proxy-auth にトークン文字列が入っています。これを使い、ヘッダーに X-aws-proxy-port: 5000 を付けてアクセスします。

curl https://<endpoint>/ \
  -H "X-aws-proxy-auth: <トークン>" \
  -H "X-aws-proxy-port: 5000"

{"message":"Hello from a Node.js MicroVM!"} が返れば、クラウド上の MicroVM で自分の Node アプリが動いていることの確認完了です。

サスペンド/レジュームで状態保持を実証

まずメモを2件書き込みます。

curl -s -X POST https://<endpoint>/notes \
  -H "X-aws-proxy-auth: <トークン>" \
  -H "X-aws-proxy-port: 5000" \
  -H "Content-Type: application/json" \
  -d '{"text":"サスペンド前に書いたメモ"}' ; echo

curl -s -X POST https://<endpoint>/notes \
  -H "X-aws-proxy-auth: <トークン>" \
  -H "X-aws-proxy-port: 5000" \
  -H "Content-Type: application/json" \
  -d '{"text":"二件目"}' ; echo

次に、サスペンド前のstageを確認します。

curl -s https://<endpoint>/state \
  -H "X-aws-proxy-auth: <トークン>" \
  -H "X-aws-proxy-port: 5000" ; echo

bootedAtuptimeSeconds の値をメモしておきます(例: bootedAt: 13:46:06, uptimeSeconds: 279)。

明示的にサスペンドします。

aws lambda-microvms suspend-microvm \
  --microvm-identifier <microvmId> \
  --region ap-northeast-1

# SUSPENDED になるまで確認
aws lambda-microvms get-microvm \
  --microvm-identifier <microvmId> \
  --region ap-northeast-1 \
  --query 'state'

レジュームして状態を確認してみます。

autoResumeEnabled:true なので、サスペンド中に /state を叩くだけで自動レジュームされます。

curl -s https://<endpoint>/state \
  -H "X-aws-proxy-auth: <トークン>" \
  -H "X-aws-proxy-port: 5000" ; echo

サスペンド前後で /state を比較すると、こうなります。

項目

サスペンド前

レジューム後

意味

bootedAt

13:46:06.137Z

13:46:06.137Z(同一)

再起動ではなく「凍結→解凍」。コールドスタートなら新時刻になる

notes

2件

2件そのまま

メモリ上の状態がサスペンドをまたいで保持された

uptimeSeconds

279

325(連続増加)

プロセスが生きたまま再開した。リセットされていない

3点すべてが保たれていれば成功です。通常の Lambda 関数ならここで bootedAt は更新され、notes は消え、uptimeSeconds はゼロに戻ります。 それが一切起きず、プロセスごと凍結・解凍されたことが数字の継続性が確認できました。アプリ自身は「自分が止められていたこと」に気づいていません。これが MicroVMs 最大の売り、ステートフルなサスペンド/レジュームです。

Written by
編集部

亀田 治伸

Kameda Harunobu

  • Facebook->
  • X->
  • GitHub->

Share

Facebook->X->
Back
to list
<-