iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0

這篇就是要講 AWS RDS 那個「七天自動復活」的都市傳說。身為工程師的你,一定有過類似的體驗:月底看到帳單的瞬間,心裡的 OS 大概就是—「蛤?我明明關掉了啊,怎麼又在跑?這個月又爆預算了啊!」

RDS 的奇怪小秘密

AWS RDS 本來是個不錯的服務,畢竟 全托管,不必自己顧 patch、備份、Failover,還幫你把 DBA 變成無用武之地(大誤)。但重點來了,它貴。真的貴。尤其對老闆來說,帳單就是比你還懂什麼叫「痛」。

所以聰明的工程師就會想:

「反正只是測試用的 RDS,沒在用的時候關掉就好啦~反正只收 Storage 費用嘛!」

結果,AWS 在你轉身去茶水間倒咖啡的時候,偷偷給你來一招:
RDS 關機超過七天,它自己會重新開機!
對,你沒看錯。七天自動復活,根本殭屍片橋段。

然後月底帳單來了,你才恍然大悟:

「靠北,原來 Server 不是我自己開的,是它自己醒來的!」

AWS 的設計動機是什麼?老實講,我也不知道。可能是怕你資料庫太久沒醒會寂寞吧?

解法:自動幫它「關回去」

既然 AWS 會幫它自動叫醒,那我們就要幫它「催眠(關機)」。方法很簡單:

  • EventBridge 當鬧鐘:每十分鐘叫一次。
  • Lambda 當殺手:檢查狀態是不是 Available,然後啪!直接 Shutdown。
  • RDS 當受害者:只要敢自己醒來,就給它關回去。

聽起來很兇殘,但這就是工程師的日常—「不管黑貓白貓,只要能抓得到老鼠的都是好貓。」,同理「RDS 沒在亂跑就是成功」。

Step 1. 建立 Lambda – 關閉 RDS

新建一隻 Lambda,取名就叫 ShutdownRDS,Runtime 選 Python 3.13。
https://ithelp.ithome.com.tw/upload/images/20250930/20141071sEFNzOZdMB.png

以下提供的程式碼是可以直接複製貼上就能用的,重點是要記得:只有 RDS 狀態是 Available 才能關機,不然就會報錯。所以我們要先檢查一下他的狀態後,確定OK,才進行關機。

我們的做法是先將所有的RDS資料取出來,逐一檢查是不是我們要關機的RDS。找到我們要關機的RDS後,再檢查它的狀態,最後才關機。

以下是AWS Lambda 的程式如下:

import json
import boto3

def lambda_handler(event, context):
    # 設定要操作的 RDS 資料庫實例識別碼
    dbID = 'testdb123'
    # 建立 RDS 服務的 boto3 客戶端
    client = boto3.client('rds')
    # 取得所有 RDS 資料庫實例的資訊
    response = client.describe_db_instances()
    # 逐一檢查每一個資料庫實例
    for db in response['DBInstances']:
        # 判斷目前的資料庫實例是否為指定的 dbID
        if db['DBInstanceIdentifier'] == dbID:
            # 如果該實例目前狀態為 available(可用)
            if db['DBInstanceStatus'] == 'available':
                # 執行停止該資料庫實例的動作
                client.stop_db_instance(DBInstanceIdentifier=dbID)
                print(f'The rds({db['DBInstanceIdentifier']}) is shutting down')
            else:
                # 若狀態不是 available,則僅顯示目前狀態
                print(f'The rds({db['DBInstanceIdentifier']}) is not available')
    # Lambda 函式的回傳結果
    return {
        'statusCode': 200,
        'body': json.dumps('execute ok')
    }

https://ithelp.ithome.com.tw/upload/images/20250930/20141071tTUs167jZk.png

Step 2. 權限地獄

寫完 Lambda 一跑,結果爆 AccessDenied
Log 大概就是長得像下面這樣:

Status: Failed
Test Event Name: test

Response:
{
  "errorMessage": "An error occurred (AccessDenied) when calling the DescribeDBInstances operation: User: arn:aws:sts::8105????????:assumed-role/ShutdownRDS-role-bmsaqf6r/ShutdownRDS is not authorized to perform: rds:DescribeDBInstances on resource: arn:aws:rds:us-east-1:8105????????:db:* because no identity-based policy allows the rds:DescribeDBInstances action",
  "errorType": "ClientError",
  "requestId": "4d892667-3ddf-4513-96c7-7bc21fbf26c6",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 10, in lambda_handler\n    response = client.describe_db_instances()\n",
    "  File \"/var/lang/lib/python3.13/site-packages/botocore/client.py\", line 602, in _api_call\n    return self._make_api_call(operation_name, kwargs)\n",
    "  File \"/var/lang/lib/python3.13/site-packages/botocore/context.py\", line 123, in wrapper\n    return func(*args, **kwargs)\n",
    "  File \"/var/lang/lib/python3.13/site-packages/botocore/client.py\", line 1078, in _make_api_call\n    raise error_class(parsed_response, operation_name)\n"
  ]
}

Function Logs:
START RequestId: 4d892667-3ddf-4513-96c7-7bc21fbf26c6 Version: $LATEST
[ERROR] ClientError: An error occurred (AccessDenied) when calling the DescribeDBInstances operation: User: arn:aws:sts::8105????????:assumed-role/ShutdownRDS-role-bmsaqf6r/ShutdownRDS is not authorized to perform: rds:DescribeDBInstances on resource: arn:aws:rds:us-east-1:8105????????:db:* because no identity-based policy allows the rds:DescribeDBInstances action
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 10, in lambda_handler
    response = client.describe_db_instances()
  File "/var/lang/lib/python3.13/site-packages/botocore/client.py", line 602, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/var/lang/lib/python3.13/site-packages/botocore/context.py", line 123, in wrapper
    return func(*args, **kwargs)
  File "/var/lang/lib/python3.13/site-packages/botocore/client.py", line 1078, in _make_api_call
    raise error_class(parsed_response, operation_name)
END RequestId: 4d892667-3ddf-4513-96c7-7bc21fbf26c6
REPORT RequestId: 4d892667-3ddf-4513-96c7-7bc21fbf26c6	Duration: 2465.13 ms	Billed Duration: 2769 ms	Memory Size: 128 MB	Max Memory Used: 88 MB	Init Duration: 303.06 ms

Request ID: 4d892667-3ddf-4513-96c7-7bc21fbf26c6

Error Message 超長
"errorMessage": "An error occurred (AccessDenied) when calling the DescribeDBInstances operation: User: arn:aws:sts::8105????????:assumed-role/ShutdownRDS-role-bmsaqf6r/ShutdownRDS is not authorized to perform: rds:DescribeDBInstances on resource: arn:aws:rds:us-east-1:8105????????:db:* because no identity-based policy allows the rds:DescribeDBInstances action"
反正重點就是:
「你沒有權限。」

沒錯,Lambda 要操作 RDS,當然要給它 RDS 權限。於是我們走一趟 IAM,幫 Role 加上 AmazonRDSFullAccess
設定權限的方式,如下圖的紅框123依照順序點選。先到 Configurations -> Permissions -> Role name ,點擊之後,就可以看到 IAM Role 的設定界面
https://ithelp.ithome.com.tw/upload/images/20250930/20141071j6wXbGyeRm.png

可以看到現在的Role只有一組自定的Policy,依照下圖紅框的順序點選來加入需要的權限。
https://ithelp.ithome.com.tw/upload/images/20250930/201410718FVU57gHDD.png

接下來會看到可以選擇的權限,現在搜尋欄內輸入RDS。接下來將所有有RDS的三個字的 Policy 列出來,將清單中的 AmazonRDSFullAccess 選起來。
https://ithelp.ithome.com.tw/upload/images/20250930/20141071YleRlBBbxM.png

最後點選右下角的 Add permissions 黃色按鈕,就可以完成權限的賦予。
https://ithelp.ithome.com.tw/upload/images/20250930/201410716R1yEUtM5K.png

然後你就會心裡想:

「Holly XXXX,這權限也太大了吧!老闆看到一定問:『你是想讓 Lambda 當 DBA 嗎?』」

所以呢,測試完記得要縮小權限,否則以後出事就是「這不是 bug,是 feature」的最佳實例。

完成權限設定後,再看一下 Role 的下方Policy 清單中有出現剛剛選擇的 AmazonRDSFullAccess 後,再回去執行一次 Lambda 看看。
https://ithelp.ithome.com.tw/upload/images/20250930/20141071rRyZH9LcjX.png

Step 3. 測試與調整

再跑一次,如果成功,你會看到 RDS 被乖乖關掉,Log 顯示任務完成。
如果失敗,理由通常是「Timeout」。解法也很工程師:

「把 Timeout 拉長就好啦!」
這就跟平常 Debug 一樣——「重開機就好了啦。」

如果看到類似以下的結果就表示執行成功了。

Status: Succeeded
Test Event Name: test

Response:
{
  "statusCode": 200,
  "body": "\"execute ok\""
}

Function Logs:
START RequestId: 01a0ad34-a64e-4661-9d09-ea756994d9a9 Version: $LATEST
The rds(testdb123) is shutting down
END RequestId: 01a0ad34-a64e-4661-9d09-ea756994d9a9
REPORT RequestId: 01a0ad34-a64e-4661-9d09-ea756994d9a9	Duration: 2988.49 ms	Billed Duration: 3302 ms	Memory Size: 128 MB	Max Memory Used: 87 MB	Init Duration: 312.65 ms

Request ID: 01a0ad34-a64e-4661-9d09-ea756994d9a9

看到執行成功的訊息之後,可以去看一下RDS有沒有順利被催眠(關機)。如果有順利被催眠的話,之後就不用擔心它每七天自動甦醒。一醒來,每隔十分鐘就可以一棒把他打暈。

那今天就先收工,工程師的人生就是這樣:截圖截到懷疑人生,最後只好跟自己說—「明天再弄啦,反正今天有確定把RDS關機就好了」。

明天再來聊 AWS EventBridge 的設定。


上一篇
Day 19 - aws 的神經中樞 EventBridge
下一篇
Day 21 - 拆解 RDS 七天自動開機的神秘炸彈 - Part 2
系列文
最適合小型工作室精打細算的服務使用法25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言