跳至内容

如何使用 Step Functions 将 Amazon RDS 实例停止 7 天以上?

6 分钟阅读
0

我想使用 AWS Step Functions 将 Amazon Relational Database Service (Amazon RDS) 停止 7 天以上。

简短描述

默认情况下,您一次最多可以将 Amazon RDS 数据库实例停止 7 天时间。7 天后,实例会重新启动,以免错过任何维护更新。

要将您的实例停止超过 7 天,您可以使用 Step Functions 来自动执行工作流,这样您就不会错过维护时段。

**注意:**有关替代解决方案,请参阅如何使用 AWS Lambda 函数将 Amazon RDS 实例停止七天以上?

解决方法

配置 IAM 权限

要创建允许 Step Functions 启动和停止 RDS 实例的 AWS Identity and Access Management (IAM) 策略,请完成以下步骤:

  1. 打开 IAM 控制台。

  2. 在导航窗格中,选择 Policies(策略),然后选择 Create policy(创建策略)。

  3. 选择 JSON 选项卡,然后输入以下语句:

    {    "Version": "2012-10-17",
        "Statement":
        [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "rds:DescribeDBInstances",
                    "rds:StopDBInstance",
                    "rds:StartDBInstance"
                ],
                "Resource": "*"
            }
        ]
    }
    
  4. 选择 Next(下一步)。

  5. 对于 Policy name(策略名称),输入您的策略的名称。例如,输入 step-functions-start-stop-rds-policy。

  6. (可选)要添加标签,请选择 Add new tag(添加新标签),然后为 Key(键)和 Value(值)字段输入相应的值。

  7. 选择 Create policy(创建策略)。

有关更多信息,请参阅使用 JSON 编辑器创建策略。

创建 IAM 角色并附加所需的策略

完成以下步骤:

  1. 打开 IAM 控制台。
  2. 在导航窗格中,选择 Roles(角色),然后选择 Create role(创建角色)。
  3. 对于 Select type of trusted entity(选择可信实体类型),选择 AWS service(AWS 服务)。
  4. 在 Use cases for other AWS service(其他 AWS 服务的使用案例)下拉列表中,选择 Step Functions。
  5. 选择 Next(下一步)。
    **注意:**请勿在 Add permissions(添加权限)页面上执行任何操作。首先创建角色,然后编辑默认权限。
  6. 选择 Next(下一步)。
  7. 对于 Role name(角色名称),输入角色的名称。例如,输入 step-functions-start-stop-rds-role。
    (可选)输入角色描述。
    (可选)要添加标签,请选择 Add new tag(添加新标签),并为 Key(键)和 Value(值)字段输入相应的值。
  8. 选择 Create role(创建角色)。这样会让您返回到 Roles(角色)页面。
  9. 在搜索框中,输入您创建的角色的名称,然后选择该角色以查看详细信息。
  10. 在 Permissions(权限)选项卡中,选择 Add permissions(添加权限)下拉列表,然后选择 Attach policies(附加策略)。
  11. 在搜索框中,输入您在 Configure IAM permissions(配置 IAM 权限)部分中创建的策略的名称。例如,输入 step-functions-start-stop-rds-policy,然后选择该策略。
  12. 在 Permissions(权限)选项卡中,选择 AWSLambdaRole AWS 托管式策略,然后选择 Remove(移除)。

有关更多信息,请参阅创建用于 AWS 服务的角色(控制台)。

为数据库实例添加标签

完成以下步骤:

  1. 打开 Amazon Aurora 和 RDS 控制台。
  2. 在导航窗格中,选择 Databases(数据库)。
  3. 选择要自动启动和停止的数据库实例。
  4. 选择 Tags(标签)选项卡。
  5. 选择 Add(添加)。对于 Tag key(标签键),输入 autostart。对于 Value(值),输入 yes。
  6. 选择 Add another Tag(添加其他标签)。对于 Tag key(标签键),输入 autostop。对于 Value(值),输入 yes。
  7. 选择 Add(添加)。

有关更多信息,请参阅在 Amazon RDS 中添加和删除标签。

创建状态机以启动和停止已标记的数据库实例

完成以下步骤:

  1. 打开 Step Functions 控制台,然后选择 Get started(开始)。

  2. 从 Create your state machine(创建状态机)中,选择 Create your own(创建自己的状态机)。

  3. 输入状态机名称。例如,输入 step-functions-start-stop-rds-state-machine。

  4. 对于 State machine type(状态机类型),选择 Standard(标准),然后选择 Continue(继续)。

  5. 选择 Code(代码)选项卡,然后输入以下状态机定义:

    {  "Comment": "State Machine Definition to start and stop RDS DB instances",
      "StartAt": "Describe DBInstances to Start",
      "States": {
        "Describe DBInstances to Start": {
          "Type": "Task",
          "Parameters": {},
          "Resource": "arn:aws:states:::aws-sdk:rds:describeDBInstances",
          "Next": "Iterate on Instances to Start",
          "Retry": [
            {
              "ErrorEquals": [
                "Rds.InternalFailure",
                "Rds.ServiceUnavailable",
                "Rds.ThrottlingException",
                "Rds.SdkClientException"
              ],
              "BackoffRate": 2,
              "IntervalSeconds": 1,
              "MaxAttempts": 2
            }
          ]
        },
        "Iterate on Instances to Start": {
          "Type": "Map",
          "ItemProcessor": {
            "ProcessorConfig": {
              "Mode": "INLINE"
            },
            "StartAt": "Format Array before Start",
            "States": {
              "Format Array before Start": {
                "Type": "Pass",
                "Next": "Check If Instance stopped, if no Tags or if Tags contains 'autostart=yes'",
                "Parameters": {
                  "DbInstanceStatus.$": "$.DBInstance.DbInstanceStatus",
                  "DbInstanceIdentifier.$": "$.DBInstance.DbInstanceIdentifier",
                  "TagList.$": "$.DBInstance.TagList",
                  "TagsArrayLength.$": "States.ArrayLength($.DBInstance.TagList)",
                  "TagContainsKey.$": "States.ArrayContains($.DBInstance.TagList,$.LookingFor)"
                }
              },
              "Check If Instance stopped, if no Tags or if Tags contains 'autostart=yes'": {
                "Type": "Choice",
                "Choices": [
                  {
                    "Not": {
                      "Variable": "$.DbInstanceStatus",
                      "StringEquals": "stopped"
                    },
                    "Next": "Instance is not in 'stopped' status"
                  },
                  {
                    "Variable": "$.TagsArrayLength",
                    "NumericEquals": 0,
                    "Next": "No Tags found to Start"
                  },
                  {
                    "Variable": "$.TagContainsKey",
                    "BooleanEquals": true,
                    "Next": "Tags found Start DBInstance"
                  }
                ],
                "Default": "No Tags found to Start"
              },
              "Tags found Start DBInstance": {
                "Type": "Task",
                "Parameters": {
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                },
                "Resource": "arn:aws:states:::aws-sdk:rds:startDBInstance",
                "Retry": [
                  {
                    "ErrorEquals": [
                      "Rds.InternalFailure",
                      "Rds.ServiceUnavailable",
                      "Rds.ThrottlingException",
                      "Rds.SdkClientException"
                    ],
                    "BackoffRate": 2,
                    "IntervalSeconds": 1,
                    "MaxAttempts": 2
                  }
                ],
                "Catch": [
                  {
                    "ErrorEquals": [
                      "States.ALL"
                    ],
                    "Next": "Failed to Start DBInstance"
                  }
                ],
                "ResultSelector": {
                  "message": "Instance Started",
                  "DbInstanceIdentifier.$": "$.DbInstance.DbInstanceIdentifier"
                },
                "End": true
              },
              "Failed to Start DBInstance": {
                "Type": "Pass",
                "Parameters": {
                  "message": "Failed to start instance",
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                },
                "End": true
              },
              "No Tags found to Start": {
                "Type": "Pass",
                "End": true,
                "Parameters": {
                  "message": "No Tags found to Start",
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                }
              },
              "Instance is not in 'stopped' status": {
                "Type": "Pass",
                "End": true,
                "Parameters": {
                  "message": "Instance is not in 'stopped' status",
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                }
              }
            }
          },
          "InputPath": "$.DbInstances",
          "Next": "Wait for 1 hour and 30 minutes",
          "ItemSelector": {
            "LookingFor": {
              "Key": "autostart",
              "Value": "yes"
            },
            "DBInstance.$": "$$.Map.Item.Value"
          }
        },
        "Wait for 1 hour and 30 minutes": {
          "Type": "Wait",
          "Seconds": 5400,
          "Next": "Describe DBInstances to Stop"
        },
        "Describe DBInstances to Stop": {
          "Type": "Task",
          "Parameters": {},
          "Resource": "arn:aws:states:::aws-sdk:rds:describeDBInstances",
          "Retry": [
            {
              "ErrorEquals": [
                "Rds.InternalFailure",
                "Rds.ServiceUnavailable",
                "Rds.ThrottlingException",
                "Rds.SdkClientException"
              ],
              "BackoffRate": 2,
              "IntervalSeconds": 1,
              "MaxAttempts": 2
            }
          ],
          "Next": "Iterate on Instances to Stop"
        },
        "Iterate on Instances to Stop": {
          "Type": "Map",
          "ItemProcessor": {
            "ProcessorConfig": {
              "Mode": "INLINE"
            },
            "StartAt": "Format Array before Stop",
            "States": {
              "Format Array before Stop": {
                "Type": "Pass",
                "Next": "Check If Instance available, if no Tags or if Tags contains 'autostop=yes'",
                "Parameters": {
                  "DbInstanceStatus.$": "$.DBInstance.DbInstanceStatus",
                  "DbInstanceIdentifier.$": "$.DBInstance.DbInstanceIdentifier",
                  "TagList.$": "$.DBInstance.TagList",
                  "TagsArrayLength.$": "States.ArrayLength($.DBInstance.TagList)",
                  "TagContainsKey.$": "States.ArrayContains($.DBInstance.TagList,$.LookingFor)"
                }
              },
              "Check If Instance available, if no Tags or if Tags contains 'autostop=yes'": {
                "Type": "Choice",
                "Choices": [
                  {
                    "Not": {
                      "Variable": "$.DbInstanceStatus",
                      "StringEquals": "available"
                    },
                    "Next": "Instance is not in 'available' status"
                  },
                  {
                    "Variable": "$.TagsArrayLength",
                    "NumericEquals": 0,
                    "Next": "No Tags found to Stop"
                  },
                  {
                    "Variable": "$.TagContainsKey",
                    "BooleanEquals": true,
                    "Next": "Tags found Stop DBInstance"
                  }
                ],
                "Default": "No Tags found to Stop"
              },
              "Tags found Stop DBInstance": {
                "Type": "Task",
                "Parameters": {
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                },
                "Resource": "arn:aws:states:::aws-sdk:rds:stopDBInstance",
                "Retry": [
                  {
                    "ErrorEquals": [
                      "Rds.InternalFailure",
                      "Rds.ServiceUnavailable",
                      "Rds.ThrottlingException",
                      "Rds.SdkClientException"
                    ],
                    "BackoffRate": 2,
                    "IntervalSeconds": 1,
                    "MaxAttempts": 2
                  }
                ],
                "Catch": [
                  {
                    "ErrorEquals": [
                      "States.ALL"
                    ],
                    "Next": "Failed to Stop DBInstance"
                  }
                ],
                "ResultSelector": {
                  "message": "Instance Stopped",
                  "DbInstanceIdentifier.$": "$.DbInstance.DbInstanceIdentifier"
                },
                "End": true
              },
              "Failed to Stop DBInstance": {
                "Type": "Pass",
                "Parameters": {
                  "message": "Failed to stop instance",
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                },
                "End": true
              },
              "No Tags found to Stop": {
                "Type": "Pass",
                "End": true,
                "Parameters": {
                  "message": "No Tags found to Stop",
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                }
              },
              "Instance is not in 'available' status": {
                "Type": "Pass",
                "End": true,
                "Parameters": {
                  "message": "Instance is not in 'available' status",
                  "DbInstanceIdentifier.$": "$.DbInstanceIdentifier"
                }
              }
            }
          },
          "InputPath": "$.DbInstances",
          "Next": "Workflow Finished",
          "ItemSelector": {
            "LookingFor": {
              "Key": "autostop",
              "Value": "yes"
            },
            "DBInstance.$": "$$.Map.Item.Value"
          }
        },
        "Workflow Finished": {
          "Type": "Succeed"
        }
      }
    }

    **注意:**在 Wait for 1 hour and 30 minutes(等待 1 小时 30 分钟)参数中,您可以更改 Seconds(秒)字段值以用于测试目的。例如,您可以将 Seconds(秒)字段值更改为更长或更短的维护时段。

  6. 选择 Config(配置),然后选择 Create(创建)。

  7. 对于 Confirm role creation(确认角色创建),选择 Confirm(确认)。

  8. 选择 Start execution(开始执行)。

对处于“已停止”状态的已标记的数据库实例运行功能测试

完成以下步骤:

  1. 打开 Step Functions 控制台。
  2. 在导航窗格中,选择 State machines(状态机)。
  3. 选择您为启动数据库实例而创建的状态机。例如,step-functions-start-stop-rds-state-machine。
  4. 选择 Start execution(开始执行)。
    **注意:**此解决方法不需要事件有效载荷。在 Start execution(开始执行)对话框下,您可以移除事件有效载荷或保留默认事件。
  5. 选择 Start execution(开始执行)。

创建计划

要为已标记的数据库实例计划每周维护时段,请创建 Amazon EventBridge 规则。此规则会在维护时段前 30 分钟自动启动数据库实例。

在以下示例中,维护时段是星期日的 22:00-22:30。示例规则会在每个星期日的 21:30 启动数据库实例。

要创建计划,请完成以下步骤:

  1. 打开 EventBridge 控制台。
  2. 选择 EventBridge Schedule(EventBridge 计划),然后选择 Create schedule(创建计划)。
  3. 输入计划名称。例如,**step-function-start-stop-schedule。**将 Schedule group(计划组)保留为 default(默认)。
  4. 对于 Schedule pattern(计划模式),选择 Recurring schedule(定期计划)。
  5. 对于 Schedule type(计划类型),选择 Cron-based schedule(基于 Cron 的计划)。
  6. 为自动计划添加 cron 表达式。例如,输入 cron(30 21 ? * SUN *)。
  7. 对于 Flexible time window(弹性时间范围),选择 Off(关闭)。
  8. 选择 Next(下一步)。
  9. 对于 Target detail(目标详细信息),选择 Templated targets(模板化目标),然后选择 StartExecution。
  10. 在 StartExecution 下,选择您在 Create a state machine to start and stop the tagged database instances(创建状态机以启动和停止已标记的数据库实例)部分中创建的状态机。例如,选择 step-functions-start-stop-rds-state-machine。保留输入的默认值 {}。
  11. 选择 Next(下一步)。
  12. 在 Retry policy and dead-letter queue (DLQ)(重试策略和死信队列 (DLQ))下,将 Retry policy(重试策略)开关设置为关闭。
  13. 对于 Dead-letter queue (DLQ)(死信队列 (DLQ)),选择 None。
  14. 在 Permissions(权限)下,选择: Create new role for this schedule(为此计划创建新角色),然后选择 Next(下一步)。
    **注意:**EventBridge 会创建一个要代入的新角色,并为您的工作流启动 StartExecution API。
  15. 在 Schedule detail(计划详细信息)下,验证接下来的 10 个调用日期是否与您的预期计划日期相符,然后选择 Create schedule(创建计划)。

Step Functions 工作流会在 EventBridge 调用该规则时启动。这会在维护时段前 30 分钟启动数据库实例。然后,工作流会在维护时段完成 30 分钟后停止数据库实例。例如,工作流在 21:30 启动您的数据库实例。维护时段是在 22:00–22:30。然后,工作流会在 23:00 停止您的实例。