
AWS Lambda Managed Instances で実行環境ごとの最大同時実行数をカスタマイズしてみた
こんにちは、製造ビジネステクノロジー部の若槻です。
AWS Lambda Managed Instances (LMI) では、単一の実行環境 (Execution Environment) 上において複数の呼び出しの同時実行が可能です。
Multi-concurrent invocations where one execution environment can handle multiple invocations simultaneously, increasing throughput especially for IO-heavy applications
そして LMI の関数の PerExecutionEnvironmentMaxConcurrency という設定を使うことで、実行環境ごとの最大同時実行数を指定できます。デフォルト値はランタイム毎に異なっており、例えば Node.js の場合は 64 です。
スケーリングの仕方に影響を与えるであろうこの設定の方法を把握しておくことにより、今後スケーリングの挙動の理解やチューニングがしやすくなりそうですね。
今回は、この LMI の PerExecutionEnvironmentMaxConcurrency の設定をカスタマイズしてみたので、その内容を共有します。
やってみた
Lambda ハンドラーコード
関数のハンドラーコードです。
import { Logger } from "@aws-lambda-powertools/logger";
import { Context } from "aws-lambda";
/**
* ロガーの初期化
*/
const logger = new Logger();
/**
* ハンドラー
*/
export const handler = async (context: Context): Promise<void> => {
logger.addContext(context);
// 2秒待機
await new Promise((resolve) => setTimeout(resolve, 2000));
logger.info("Hello, LMI");
};
ハンドラー中には2秒の待機を入れています。これにより、関数を連続で呼び出した際に同時実行が発生しやすくしています。
デフォルト設定の確認
リソースの作成には AWS CDK を使用しました。
VPC は予め作成したものを使用します。
VPC 定義コード
import * as ec2 from "aws-cdk-lib/aws-ec2";
import { Construct } from "constructs";
/**
* VPC 実装
*
* MEMO: VPC から各種 AWS サービスへのネットワーク経路確保のために NAT Gateway を配置する
*/
export class VpcConstruct extends Construct {
public readonly vpc: ec2.Vpc;
constructor(scope: Construct, id: string) {
super(scope, id);
this.vpc = new ec2.Vpc(this, "Default", {
subnetConfiguration: [
/**
* NAT Gateway を配置するためのパブリックサブネット
*/
{
name: "Public",
subnetType: ec2.SubnetType.PUBLIC,
},
/**
* Lambda 関数を配置するためのプライベートサブネット
*/
{
name: "PrivateIsolated",
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
},
],
});
}
}
LMI 作成の定義は以下となります。
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as lambda_nodejs from "aws-cdk-lib/aws-lambda-nodejs";
import { Construct } from "constructs";
import { VpcConstruct } from "./constructs/vpc";
export class SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
/**
* VPC 作成
*/
const vpcConstruct = new VpcConstruct(this, "Vpc");
const vpc = vpcConstruct.vpc;
/**
* セキュリティグループ作成
*
* MEMO: Capacity Provider 用に作成
*/
const securityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
vpc,
});
/**
* Capacity Provider 作成
*
* MEMO: LMI のコア要素となるリソース
*/
const capacityProvider = new lambda.CapacityProvider(
this,
"CapacityProvider",
{
subnets: vpc.selectSubnets({
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
}).subnets,
securityGroups: [securityGroup],
},
);
/**
* LMI 用 Lambda 関数作成
*/
const lmiFunction = new lambda_nodejs.NodejsFunction(this, "LmiFunction", {
entry: "src/handler.ts",
runtime: lambda.Runtime.NODEJS_24_X, // Lambda Managed Instances では Node.js 18.x 以上が必須
memorySize: 2048, // Lambda Managed Instances では 2048MB 以上が必須
tracing: lambda.Tracing.ACTIVE, // コールドスタート発生状況の観測のために、AWS X-Ray を有効化
});
/**
* Capacity Provider に Lambda 関数を追加
*/
capacityProvider.addFunction(lmiFunction);
}
}
作成された LMI 関数の Capacity provider configuration で、PerExecutionEnvironmentMaxConcurrency の値が 64 となっていることが確認できました。

同関数の Execution environment concurrent executions メトリクスでも期待通り Concurrent executions (maximum limit) が 64 となっていることが確認できました。

カスタマイズを反映する
AWS CDK による PerExecutionEnvironmentMaxConcurrency のカスタマイズは以下のドキュメントを参照しました。
次のように、addFunction メソッドの第 2 引数で perExecutionEnvironmentMaxConcurrency を指定します。今回は 1 に設定してみます。
/**
* Capacity Provider に Lambda 関数を追加
*/
capacityProvider.addFunction(lmiFunction, {
perExecutionEnvironmentMaxConcurrency: 1, // 同時実行数上限を 1 に設定
});
上記変更をデプロイすると、1 への設定変更が反映されていますね。

しかし、メトリクス Execution environment concurrent executions の方は 0 になりました。極端に下げ過ぎるとメトリクスが正しく表示されないのかもしれません。また Execution environment count が一瞬上振れしていますが、これはデプロイ時に一時的にスケールアウトした影響かと思われます。

連続で関数を呼び出してみる
実行環境毎の同時実行数を上回った時の挙動を確認するため、関数を5回連続で呼び出してみます。
すると、実行環境が一時的に 3 から 9 に増えました。

期待通り、実行環境が鋭敏に増えていることが確認できましたが、9 まで増えたのは想定外でした。3 の倍数で増える仕様でもあるのでしょうか。
PerExecutionEnvironmentMaxConcurrency の上限は 64 である
ちなみに PerExecutionEnvironmentMaxConcurrency の上限は 64 であるため、これを超える値を指定するとエラーとなります。
/**
* Capacity Provider に Lambda 関数を追加
*/
capacityProvider.addFunction(lmiFunction, {
perExecutionEnvironmentMaxConcurrency: 65, // 同時実行数上限を 65 に設定
});
The specified PerExecutionEnvironmentMaxConcurrency exceeds the limit of 64 per VCpu. For your configuration, the limit is 64
おわりに
AWS Lambda Managed Instances で実行環境ごとの最大同時実行数をカスタマイズしてみた内容を共有しました。
PerExecutionEnvironmentMaxConcurrency をカスタマイズできたことと、実行環境数を極端に少なくすることで、関数の連続呼び出し時に実行環境が増える様子を確認できました。
ちなみに、私は実行環境とワーカースレッドの関係について下記の認識を持っていたのですが、
実行環境(Execution Environment)
└─ Node.js プロセス(1つ)
├─ Worker Thread #1
│ ├─ リクエスト A
│ ├─ リクエスト B
│ └─ …
├─ Worker Thread #2
│ ├─ リクエスト C
│ └─ …
└─ Worker Thread #N(≈ vCPU 数)
ドキュメントには、
the default is 64 concurrent requests per vCPU
および
The default number of worker threads is determined by the number of vCPUs available
とあり、PerExecutionEnvironmentMaxConcurrency が vCPU 数に依存しているように読め、私の認識と矛盾があります。このあたりは今後もう少し調査してみたいと思います。
以上









