以前の記事「AWS Step Functionsを使用して、自動的に開始されたRDS DBインスタンスを停止してみる」では、状態確認を“停止完了まで無限ループ”で実装していました。しかし実運用では 想定外の長時間ポーリングがコスト増・障害検知遅延につながる恐れがあります。
今回の記事では Step Functionsでポーリング回数を制限し、指定した回数以内に停止できなければ明示的に失敗させるフローを紹介します。
ステートマシンで停止実行フローをつくってみた
AWS Step Functionsを使用し、サービスをオーケストレーションすることで、運用ワークフローを自動化・負荷の低減を図ることができます。ワークフローとも呼ばれるステートマシンは、Amazon States Language(ASL)を使用して定義します。
ASLはJSON形式で定義し、各状態を一連のステップで構成することができます。ChoiceというTypeを使用したり、組み込み関数を利用したりすることで条件分岐が可能になります。今回の紹介する仕組みでは、States.MathAdd組み込み関数を使用し、ループ内の値をインクリメントすることで、フローを制御します。
■構築例紹介
今回の構築は以下のイメージです。
■IAMロール・ポリシー作成
まずはAmazon Auroraの状態を確認および停止するAWS LambdaにアタッチするIAMロールとIAMポリシーを作成します。
●IAMポリシー
状態確認用のLambdaにあたえる許可ポリシーを作成します。
IAMのダッシュボードから[ Policies ]→[ Create policy ]の順に押下してください。
以下のポリシーを貼り付けます。
※アカウントIDは、自身の環境に合わせ修正してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:eu-west-1:<ACCOUNT_ID>:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:eu-west-1:<ACCOUNT_ID>:log-group:/aws/lambda/check-status-aurora-lambda:*"
]
},
{
"Effect": "Allow",
"Action": "rds:DescribeDBClusters",
"Resource": "arn:aws:rds:eu-west-1:<ACCOUNT_ID>:cluster:*"
}
]
}
名前は、[ check-status-aurora-lambda-iampolicy ]とし、作成してください。
続いて、Auroraを停止実行するLambdaへの許可ポリシーを作成します。
先ほどと同様に、IAMのダッシュボードから[ Policies ]→[ Create policy ]の順に押下してください。
以下のポリシーを貼り付けます。
※アカウントIDは、自身の環境に合わせ修正してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:eu-west-1:<ACCOUNT_ID>:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:eu-west-1:<ACCOUNT_ID>:log-group:/aws/lambda/check-status-aurora-lambda:*"
]
},
{
"Effect": "Allow",
"Action": [
"rds:StopDBCluster",
"rds:DescribeDBClusters"
],
"Resource": "arn:aws:rds:eu-west-1:<ACCOUNT_ID>:cluster:*"
}
]
}
名前は、[ stop-aurora-lambda-iampolicy ]として、作成してください。
IAMポリシーの作成は以上です。
●IAMロール
IAMロールを作成していきます。
IAMのダッシュボードから[ Roles ]→[ Create role ]の順に押下してください。
以下の設定値を入力してください。
項目 | 設定値 |
---|---|
Trusted entity type | AWS service |
Service or use case | Lambda |
[ Permissions policies ]には、状態確認用に作成したIAMポリシー[ check-status-aurora-lambda-iampolicy ]を検索し、チェックを入れ、[ Next ]を押下します。
↓
名前は、[ check-status-aurora-lambda-iamrole ]として、作成してください。
もうひとつIAMロールを作成します。
[ Create role ]を押下してください。
以下の設定値を入力してください。
項目 | 設定値 |
---|---|
Trusted entity type | AWS service |
Service or use case | Lambda |
[ Permissions policies ]には、状態確認用に作成したIAMポリシー[ stop-aurora-lambda-iampolicy ]を検索し、チェックを入れ、[ Next ]を押下します。
↓
名前は、[ stop-aurora-lambda-iamrole ]として、作成してください。
IAMロールの作成は以上です。
■Lambda関数作成
つぎにLambda関数を作成します。
AWS Lambdaのダッシュボード画面から、[ Functions ]→[ Create function ]の順に押下してください。
最初はAuroraクラスタの状態確認用Lambda関数を作成します。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
Function name | check-status-aurora-lambda |
Runtime | Python 3.13 |
Architecture | x86_64 |
Execution role | Use an existing role |
Existing role | check-status-aurora-lambda-iamrole |
↓
[ code ]に以下のコードを貼り付けて、[Deploy]を押下します。
import os
import boto3
from botocore.exceptions import ClientError, EndpointConnectionError
rds = boto3.client("rds")
def lambda_handler(event, _):
cluster_id = event.get("CLUSTER_ID") or os.getenv("CLUSTER_ID")
if not cluster_id:
return _resp(400, "ClusterIdentifier not provided")
try:
resp = rds.describe_db_clusters(DBClusterIdentifier=cluster_id)
clusters = resp.get("DBClusters", [])
if not clusters:
return _resp(404, f"No such cluster: {cluster_id}")
status = clusters[0]["Status"]
print(f"[INFO] {cluster_id} status = {status}")
return _resp(
200,
"status retrieved",
cluster_id=cluster_id,
aurora_state=status,
)
except (EndpointConnectionError, ClientError) as e:
print(f"[ERROR] describe_db_clusters failed: {e}")
return _resp(500, "AWS API error")
def _resp(code: int, msg: str, **extra) -> dict:
"""Uniform HTTP‑like response"""
return {"statusCode": code, "body": {"message": msg, **extra}}
↓
もうひとつの、Auroraクラスタ停止用Lambdaを作成していきます。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
Function name | stop-aurora-lambda |
Runtime | Python 3.13 |
Architecture | x86_64 |
Execution role | Use an existing role |
Existing role | check-status-aurora-lambda-iamrole |
↓
こちらも同じく、以下のコードを貼り付けて、[ Deploy ]を押下してください。
import os
import boto3
from botocore.exceptions import ClientError, EndpointConnectionError
rds = boto3.client("rds")
def lambda_handler(event, _):
cluster_id = event.get("cluster_id")
if not cluster_id:
return _resp(400, "ClusterIdentifier not provided")
try:
rds.stop_db_cluster(DBClusterIdentifier=cluster_id)
print(f"[INFO] stop request sent for {cluster_id}")
return _resp(200, "stop requested", aurora_id=cluster_id)
except ClientError as e:
code = e.response["Error"]["Code"]
if code == "InvalidDBClusterStateFault":
print(f"[INFO] {cluster_id} already stopped")
return _resp(200, "already stopped", aurora_id=cluster_id)
print(f"[ERROR] AWS error {code}: {e}")
return _resp(500, code)
except EndpointConnectionError as e:
print(f"[ERROR] network error: {e}")
return _resp(500, "network error")
def _resp(code: int, msg: str, **extra) -> dict:
"""Uniform HTTP‑like response"""
return {"statusCode": code, "body": {"message": msg, **extra}}
↓
Lambda関数の作成は以上です。
■Step Functionsステートマシン作成
最後の構築工程です。Auroraを停止のワークフローをコントロールするAWS Step Functions ステートマシンを作成します。
Step Functionsのダッシュボード画面から[ Create state machine ]を押下します。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
State machine name | stop-aurora-statemachine |
State machine type | Standard |
↓[ Continue ]を押下します。
[ Code ]を押下し、以下のjsonを貼り付け、[ Create ]を押下します。
※アカウントIDやリージョンは、自身の環境に合わせ修正してください。
{
"Comment": "Stop Aurora with pre-check & max attempts",
"StartAt": "Init",
"States": {
"Init": {
"Type": "Pass",
"Parameters": {
"Attempts": 0
},
"ResultPath": "$.Meta",
"Next": "CheckBeforeStop"
},
"CheckBeforeStop": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:check-status-aurora-lambda",
"ResultSelector": {
"Status.$": "$.body.aurora_state",
"Name.$": "$.body.cluster_id"
},
"ResultPath": "$.Check",
"Next": "Avail?"
},
"Avail?": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Check.Status",
"StringEquals": "available",
"Next": "StopCluster"
}
],
"Default": "IncCounter"
},
"StopCluster": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:stop-aurora-lambda",
"Parameters": {
"cluster_id.$": "$.Check.Name"
},
"ResultPath": null,
"Next": "ResetCounter"
},
"ResetCounter": {
"Type": "Pass",
"Parameters": {
"Attempts": 0
},
"ResultPath": "$.Meta",
"Next": "CheckAfterStop"
},
"CheckAfterStop": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:check-status-aurora-lambda",
"ResultSelector": {
"Status.$": "$.body.aurora_state"
},
"ResultPath": "$.Check",
"Next": "Stopped?"
},
"Stopped?": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Check.Status",
"StringEquals": "stopped",
"Next": "Success"
}
],
"Default": "IncCounter"
},
"IncCounter": {
"Type": "Pass",
"Parameters": {
"Attempts.$": "States.MathAdd($.Meta.Attempts, 1)"
},
"ResultPath": "$.Meta",
"Next": "HasAttemptsLeft"
},
"HasAttemptsLeft": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Meta.Attempts",
"NumericLessThan": 5,
"Next": "Wait300"
}
],
"Default": "FailTimeout"
},
"Wait300": {
"Type": "Wait",
"Seconds": 300,
"Next": "Route"
},
"Route": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Check.Status",
"StringEquals": "available",
"Next": "CheckBeforeStop"
},
{
"Variable": "$.Check.Status",
"StringEquals": "stopping",
"Next": "CheckAfterStop"
}
],
"Default": "FailTimeout"
},
"Success": {
"Type": "Succeed"
},
"FailTimeout": {
"Type": "Fail",
"Error": "MaxAttemptsExceeded",
"Cause": "Cluster did not reach target state in 5 checks"
}
}
}
↓
Step Functionsステートマシン作成時に、AWS側で必要な権限をもつIAMロールを作成してくます。許可ないように問題なければ、[ Confirm ]を押下します。
↓
Step Functions ステートマシン作成は以上です。
■動作確認1
それでは動作確認をしていきます。まずは停止実行をし、複数回状態確認のポーリング実行したのち、状態が"stopped"になれば完了となるワークフローを確認します。
停止させるAuroraクラスタの状態が"available"であることを確認します。
続いて、作成したステートマシンを押下し、詳細画面から[ Start execution ]を押下します。
ステートマシンに渡すデータとして、以下を入力し、[ Start execution ]を押下します。
※CLUSTER_IDには、自身の環境のクラスタ名に置換してください。
{
"CLUSTER_ID": "saitou-database-1"
}
↓
Eventを見ると、複数回状態確認され、"stopped"になったことでワークフローが完了していることがわかります。
↓
↓
■状態確認回数修正
今回の目的は、状態確認に制限回数を設け、ワークフローの長時間稼働を防ぐことです。ですので、設定した制限回数が効いているのか確認してみます。
さきほどまでは、状態確認の回数が5回でしたので、上限を2回>とし、Step Functionsによるポーリングが正常に動作しているか検証してみます。さきほどの動作確認では、停止するまでに3回状態確認をしています。ですので、上限2回に設定し、2回状態確認してもAuroraクラスタが停止していなかったらワークフローを停止するようにします。
ステートマシンの[ Edit ]から定義を修正します。以下のjsonを貼り付けてください。
※アカウントIDやリージョンは、自身の環境に合わせ修正してください。
{
"Comment": "Stop Aurora with pre-check & max attempts",
"StartAt": "Init",
"States": {
"Init": {
"Type": "Pass",
"Parameters": {
"Attempts": 0
},
"ResultPath": "$.Meta",
"Next": "CheckBeforeStop"
},
"CheckBeforeStop": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:check-status-aurora-lambda",
"ResultSelector": {
"Status.$": "$.body.aurora_state",
"Name.$": "$.body.cluster_id"
},
"ResultPath": "$.Check",
"Next": "Avail?"
},
"Avail?": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Check.Status",
"StringEquals": "available",
"Next": "StopCluster"
}
],
"Default": "IncCounter"
},
"StopCluster": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:stop-aurora-lambda",
"Parameters": {
"cluster_id.$": "$.Check.Name"
},
"ResultPath": null,
"Next": "ResetCounter"
},
"ResetCounter": {
"Type": "Pass",
"Parameters": {
"Attempts": 0
},
"ResultPath": "$.Meta",
"Next": "CheckAfterStop"
},
"CheckAfterStop": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:check-status-aurora-lambda",
"ResultSelector": {
"Status.$": "$.body.aurora_state"
},
"ResultPath": "$.Check",
"Next": "Stopped?"
},
"Stopped?": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Check.Status",
"StringEquals": "stopped",
"Next": "Success"
}
],
"Default": "IncCounter"
},
"IncCounter": {
"Type": "Pass",
"Parameters": {
"Attempts.$": "States.MathAdd($.Meta.Attempts, 1)"
},
"ResultPath": "$.Meta",
"Next": "HasAttemptsLeft"
},
"HasAttemptsLeft": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Meta.Attempts",
"NumericLessThan": 2,
"Next": "Wait300"
}
],
"Default": "FailTimeout"
},
"Wait300": {
"Type": "Wait",
"Seconds": 300,
"Next": "Route"
},
"Route": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Check.Status",
"StringEquals": "available",
"Next": "CheckBeforeStop"
},
{
"Variable": "$.Check.Status",
"StringEquals": "stopping",
"Next": "CheckAfterStop"
}
],
"Default": "FailTimeout"
},
"Success": {
"Type": "Succeed"
},
"FailTimeout": {
"Type": "Fail",
"Error": "MaxAttemptsExceeded",
"Cause": "Cluster did not reach target state in 2 checks"
}
}
}
↓[ save ]を押下します。
■動作確認2
それでは、もう一度ステートマシンを実行しましょう。さきほど同様、作成したステートマシンを押下し、詳細画面から[ Start execution ]を押下します。
ステートマシンに渡すデータとして、以下を入力し、[ Start execution ]を押下します。
※CLUSTER_IDには、自身の環境のクラスタ名に置換してください。
{
"CLUSTER_ID": "saitou-database-1"
}
実行後、ワークフローを確認すると、5分後には上限に引っかかり、ワークフローが停止していることが確認できました。
今回の構築は以上です。
まとめ
前回の記事ではデータベースを停止するフローにタイムアウトの制限を設けず、場合によっては無限ループする仕組みでした。AWS Step Functionsで使用するAmazon States Language(ASL)には組み込み関数が用意されており、Step Functions自身で各ステップを制御することができます。
今回利用した組み込み関数はインクリメントする関数でしたが、他にも有用な関数がたくさんあるので、Step functions単体でも複雑なワークフローをデプロイすることができると感じます。運用負荷を低減するために、どんどん活用していきたいですね。
参考リンク:AWS公式ドキュメント