👌

Lambda Tenant Isolation Mode(テナント分離モード)

に公開

はじめに

おはようございます、加藤です。
2025年11月19日にAWS Lambdaに新機能Tenant isolation modeが登場しました。

この機能はマルチテナントで提供されるサービス向けに、単一のLambda関数に対して実行時にテナントIDを指定し、それごとにLambda関数の実行環境を分離することによってテナントごとに実行環境の分離ができます。これによって、意図せずにメモリやストレージがテナント相互に参照されてしまうことを防ぎ、より強力なテナント毎の分離を行えます。

tl;dr

  • Lambda関数作成時にのみTenant isolation modeを指定可能
  • イミュータブルなパラメータであり作成後に変更不可
  • API Gateway(REST)と直接実行(同期・非同期)のみトリガー可能
  • コールドスタート時にLambda関数のCPUアーキテクチャとメモリ量に応じた追加課金
  • テナントの事前定義不要
  • テナント数無制限
  • Lambda関数でtenant_idはハンドラ関数の引数のcontextから取得可能
    • トップレベルで取得はできない
  • Lambda関数に割り当てるIAMロールは単一でテナントごとに分離不可
  • テナント毎に同時実行数の制御不可で複数テナントでLambda関数の同時実行数制限を共有

やってみた

マネジメントコンソールでのテナント分離モードのLambda関数の作成

Lambda関数の作成時にその他の設定>セキュリティとガバナンステナント分離モードの指定ができます。

tenant isolation modeのコンフィグ

ハンドラー関数のcontext.tenantId(Node.jsの場合)からテナントIDが取得できるので、試しにそのままレスポンスしてみます。

index.mjs

export const handler = async (event, context) => {
  const response = {
    statusCode: 200,
    body: {
      tenantId: context.tenantId
    },
  };
  return response;
};

テストイベントを作成する際に、テナントIDが指定可能になっています。

テストイベント

テナントIDにBlueTenantを指定して、実行すると以下のレスポンスが返却されテナントIDを取得できたことを確認できました。

{
  "statusCode": 200,
  "body": {
    "tenantId": "BlueTenant"
  }
}

他の作成方法

CLIの指定方法

aws lambda create-function \
  --function-name tenant-isolation-mode \
  --runtime nodejs22.x \
  --role XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
  --handler index.handler \
  --zip-file fileb://function.zip \
  --tenancy-config '{"TenantIsolationMode":"PER_TENANT"}'

CloudFormationの指定方法

SAMの場合もAWS::Serverless::Functionで同様にTenancyConfigを指定します。

Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: tenant-isolation-mode
      Runtime: nodejs22.x
      Handler: index.handler
      Role: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      Code:
        S3Bucket: bucket
        S3Key: function.zip
      TenancyConfig:
        TenantIsolationMode: PER_TENANT

CDKの指定方法

const fn = new lambda.Function(this, 'Function', {
  runtime: lambda.Runtime.NODEJS_22_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset(path.join(__dirname, 'function')),
  tenancyConfig: lambda.TenancyConfig.PER_TENANT,
});

実行環境分離の確認

実行環境が分離されていることを確認するために、トップレベルに変数を定義してそれをインクリメントしながらレスポンスしてみます。

index.mjs

let counter = 0

export const handler = async (event, context) => {
  const response = {
    statusCode: 200,
    body: {
      counter: counter++,
      tenantId: context.tenantId
    },
  };
  return response;
};

このように、同じテナントIDでは実行環境が共有され、異なるテナントIDでは実行環境が共有されないことを確認できました。

テストイベント

API Gateway(REST)との連携

このままのLambda関数のレスポンス形式ではエラーとなるので、ボディをJSON文字列に変更します。

index.mjs

let counter = 0

export const handler = async (event, context) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      counter: counter++,
      tenantId: context.tenantId
    }),
  };
  return response;
};
クライアント→(Header x-tenant-id: BlueTenant)→API Gateway→(Header x-amz-tenant-id: BlueTenant)→Lambda関数

クライアントからのリクエスト時にヘッダーにテナントIDを設定し、ヘッダーのキーを変換してリクエストしてみます。

GET /に対してLambda関数を統合させます。ヘッダーの設定をします。

メソッドリクエストにx-tenant-idを必須、キャッシュなしで設定します。

統合リクエストに名前X-Amz-Tenant-Idをマッピング先method.request.header.x-tenant-idに設定します。キャッシュはなしです。

デプロイしてリクエストしてみます。

curl -H "x-tenant-id:BlueTenant" ${YOUR_URL}/prod/
{"counter":0,"tenantId":"BlueTenant"}

curl -H "x-tenant-id:BlueTenant" ${YOUR_URL}/prod/
{"counter":1,"tenantId":"BlueTenant"}

curl -H "x-tenant-id:BlueTenant" ${YOUR_URL}/prod/
{"counter":2,"tenantId":"BlueTenant"}

curl -H "x-tenant-id:GreenTenant" ${YOUR_URL}/prod/
{"counter":0,"tenantId":"GreenTenant"}

curl -H "x-tenant-id:GreenTenant" ${YOUR_URL}/prod/
{"counter":1,"tenantId":"GreenTenant"}

curl -H "x-tenant-id:BlueTenant" ${YOUR_URL}/prod/
{"counter":3,"tenantId":"BlueTenant"}

期待通りにテナント分離が機能しています。

機能面の考察

マルチテナント関係なくトリッキーな使い方に関する個人的考察です。

Tenant isolation modeは名称が示すようにマルチテナントで提供されるサービスのLambda実行環境の分離を提供する機能です。これをあえて機能面にフォーカスして低レベルで説明すると、Lambda関数の実行時に任意の識別子の指定を要求し、識別子が一致しない限り実行環境を分離する機能となります。
想定される使い方として、識別子にテナントの識別子を指定するのでテナント毎の環境分離を実現することができます。

そのため、ランダムな識別子を指定することでコールドスタートを強制することができます。これによって、エンドユーザーから提供されるコードを実行する際にLambda関数が一切外部にアクセスできない前提であれば、メモリおよびストレージも事前に使用されていないので他の環境に侵害されず/を侵害せずに実行できることが担保できます。

逆に、保持したいメモリ状態、ファイルと合致する識別子を指定することによって実行環境が残っていればメモリおよび/またはファイルの復元を省略するのに使うことができます。

まとめ

Lambdaのテナント分離モードについて、一通り使用感を試してみました。実行環境が完全に分離されているので、それぞれでテナント固有のメモリやファイルの初期化、例えばDBコネクションプールを保持することなどが可能です。一方でIAMロールは共有されるため、IAMロールも分離したい場合はLambda関数自体に割り当てる権限は最小減にして動的に一時クレデンシャルを取得するような工夫が必要です。
とはいえ、登場したばかりの機能なので今後のアップデートに期待ですね。
以上でした。

アマゾン ウェブ サービス ジャパン (有志)

Discussion