この記事は、Amazon SageMaker AI のリアルタイムエンドポイントで Application Auto Scaling を使っている際に、AWS CloudFormation や AWS CDK で設定更新が失敗する問題と、その解決策を扱っています。SageMaker AI では、エンドポイントのインスタンスタイプや台数、環境変数を変更する前に、スケーラブルターゲットを Application Auto Scaling から登録解除する必要があります。しかし CloudFormation の管理外で手動登録解除すると、CloudFormation は自動スケーリングリソースを追跡できなくなります。
失敗パターンは主に三つです。スケーラブルターゲットが登録されたままエンドポイント設定を変更すると、CloudFormation はエンドポイントを更新しようとしますが、SageMaker は AllTraffic バリアントを先に登録解除しなければ削除やインスタンスタイプ更新ができないというエラーで拒否します。AWS CLI で手動登録解除した後にエンドポイント設定だけを変更すると、デプロイ自体は成功しますが、テンプレート上の自動スケーリング設定が変わっていないため CloudFormation はターゲットを再登録せず、スケーリングポリシーも再適用しません。その結果、エラー表示なしに自動スケーリングが失われます。さらに、手動登録解除後にスケーリングパラメータも変更すると、存在しない ScalableTarget を更新しようとして失敗し、ロールバックも失敗してスタックが UPDATE_ROLLBACK_FAILED になり、コンソールで手動復旧が必要になることがあります。
解決策は再利用可能な CDK construct です。ネイティブの AWS::ApplicationAutoScaling::ScalableTarget と ScalingPolicy を直接使う代わりに、三つの CloudFormation カスタムリソースで登録解除と再登録をエンドポイント更新の前後に配置します。CDK の add_dependency により順序を明示し、最初にスケーラブルターゲットを登録解除し、次に CDK がエンドポイントを更新し、エンドポイントが InService になってからスケーラブルターゲットを登録し、最後にターゲットが照会可能になってからターゲット追跡ポリシーを適用します。初回デプロイではターゲットが存在しないため、登録解除ハンドラーは ObjectNotFoundException を捕捉して処理を続けます。
登録とポリシー適用には CDK provider の非同期完了パターンを使います。on_event ハンドラーはすぐに戻り、provider の waiter が約30秒間隔で is_complete ハンドラーを再実行して準備完了を確認します。Register Scalable Target の total_timeout は1時間、Attach Scaling Policy は15分で、単一の Lambda 呼び出しのタイムアウトには縛られません。各カスタムリソースには ConfigHash プロパティがあり、エンドポイント設定またはスケーリング設定が変わると CloudFormation が Lambda を再実行します。記事は、エンドポイント設定とスケーリング設定を同じ ConfigHash で管理する必要があると強調しています。
デプロイされるスタックには三つのカスタムリソースだけでなく、より多くの Lambda 関数が含まれます。各カスタムリソースは CDK の cr.Provider に支えられ、provider フレームワークが補助関数を生成します。同期 provider の Deregister はフレームワーク関数を1つ追加します。非同期 provider の Register Scalable Target と Attach Scaling Policy は onEvent、isComplete、onTimeout の3関数と waiter を駆動する AWS Step Functions ステートマシンを追加します。結果として、スタックには3つのカスタムリソース、2つの Step Functions ステートマシン、12個の Lambda 関数が含まれます。5つは construct 自身のハンドラーコードを持ち、残り7つは provider フレームワークが生成します。これらは利用者が保守する必要はありませんが、デプロイ資源を確認する際には考慮すべきです。
ウォークスルーでは、SageMaker AI、Lambda、IAM、Application Auto Scaling、CloudWatch の権限を持つ AWS アカウント、AWS CDK v2、Python 3.12 以降、uv、S3 のモデルアーティファクトと ECR イメージまたは SageMaker 組み込みアルゴリズムコンテナが必要です。GitHub のサンプルリポジトリをクローンして uv sync を実行し、config/config.yaml に endpoint と autoscaling をまとめて設定します。エンドポイント設定は CreateModel と CreateEndpointConfig API に、スケーリング設定は RegisterScalableTarget と PutScalingPolicy API に対応します。その後 cdk bootstrap と cdk deploy InferenceStack を実行し、describe-scalable-targets と describe-scaling-policies で登録内容を確認します。
更新テストでは、config/config.yaml の値を変更します。たとえばインスタンスタイプを ml.m5.large から ml.m5.xlarge に変えて再デプロイし、完了後に再度 describe-scalable-targets を実行して、自動スケーリングが登録されたまま新しい値になっていることを確認します。既存の CDK スタックに統合する場合は、sagemaker_autoscaling/constructs/ ディレクトリをプロジェクトにコピーし、エンドポイントリソースの横で SagemakerAutoscalingConstruct をインスタンス化します。その際、エンドポイント、AutoscalingConfig、および両方の設定を含む config_hash を渡します。
実装では、Deregister カスタムリソースがエンドポイント更新前に deregister_scalable_target を呼び、初回デプロイ時の ObjectNotFoundException を処理します。CDK では endpoint.node.add_dependency(deregister) により依存関係を設定します。Register Scalable Target は、CloudFormation がエンドポイント更新を受け付けただけでは不十分で、エンドポイントが InService になるまで登録できないため、非同期完了パターンを使います。on_event は即座に戻り、is_complete がエンドポイント状態を照会し、Failed の場合は例外を発生させます。Attach Scaling Policy もスケーラブルターゲットが照会可能になるまで待ってからポリシーを適用します。元記事のコード抜粋は Register ハンドラーの条件チェック付近で途切れていますが、ここまででオーケストレーションの要点と設定上の注意点は把握できます。