วิธีการใช้ EventBridge Scheduler ในการตั้งเวลา start/stop EC2

การเปิดตัวของฟังก์ชัน EventBridge Scheduler ทำให้เราสามารถ start/stop EC2 instance ผ่าน EventBridge ได้โดยตรง เรามาปิด instance ในช่วงเวลาที่ไม่ได้ใช้งานเพื่อลดค่าใช้จ่ายกันเถอะครับ

บทความนี้ได้รับการแปลมาจากบทความภาษาญี่ปุ่นที่มีชื่อว่า EventBridgeスケジューラを使ってEC2の定期起動/停止を行う方法 โดยเจ้าของบทความนี้คือ คุณ Yagi Yusei ซึ่งเป็นชาวญี่ปุ่น และในบทความนี้จะมีการปรับสำนวนการเขียน รวมถึงมีการเรียบเรียงเนื้อหาใหม่ให้เข้าใจง่ายและมีความเหมาะสมมากยิ่งขึ้น

สวัสดีทุกท่านครับ Yagi จากฝ่าย Data Analytics ครับ

มีความจำเป็นต้องใช้ EC2 แค่ไม่กี่ชั่วโมง ส่วนตอนที่ไม่ได้ใช้ก็อยากจะ stop instance เอาไว้เพื่อลดค่าใช้จ่ายจัง... ผมคิดว่าน่าจะมีคนที่เคยหรือกำลังคิดมากเกี่ยวกับเรื่องแบบนี้อยู่เหมือนกันนะครับ

ถ้ามีความจำเป็นต้องใช้งาน EC2 ไม่กี่นาที ให้เปลี่ยนไปใช้ Lambda แทน หรือถ้าใช้งานในหลักชั่วโมง ก็ให้เปลี่ยนไปใช้ ECS แทน วิธีแบบนั้นก็มีอยู่หรอกครับ แต่การจะย้าย application จาก service หนึ่งไปยังอีก service หนึ่งก็ไม่ใช่เรื่องที่ทำกันได้ง่าย ๆ แถมในบางกรณีก็ต้องใช้ทั้งแรงคนและเวลาพอสมควรด้วยครับ ในเวลาแบบนี้ ไม่คิดอยากจะตั้งค่า start/stop EC2 ตามเวลาที่กำหนดไว้บ้างเหรอครับ

ก่อนหน้านี้ การจะตั้งค่า start/stop EC2 ตามกำหนดเวลาจะต้องใช้ EventBridge + Lambda [1] หรือไม่ก็ EventBridge + Systems Manager [2] ครับ ซึ่งถ้าเลือกใช้ Lambda ก็จะมีภาระงานในการเขียนโค้ดเพิ่มขึ้นมาอีก

ในเวลาแบบนี้ EventBridge ก็ได้เปิดตัวฟังก์ชันใหม่ EventBridge Scheduler ออกมาครับ ผมก็เคยคิดอยู่ครับว่าถ้า EventBridge สามารถ start/stop EC2 ได้โดยตรงก็คงจะสะดวกไม่น้อย แต่ไม่นึกเลยว่าจะมีฟังก์ชันนี้ออกมาจริง ๆ

EventBridge Scheduler มีความสามารถเหมือนกับ EventBridge Rule แทบทุกอย่าง แต่สามารถทำในสิ่งที่ EventBridge Rule ทำไม่ได้อย่างเช่นการ start/stop EC2 ตรง ๆ ได้ด้วย

ในครั้งนี้ผมจึงจะมาลองใช้ฟังก์ชันนี้ในการตั้งเวลา start/stop EC2 ดูครับ

ลองทำดู

เงื่อนไข

  • มี EC2 instance ที่อยู่ในสถานะ stopped อยู่แล้ว

สร้าง IAM Role สำหรับ EventBridge Schedule

ก่อนอื่น เราจะเริ่มจากการสร้าง IAM Role สำหรับใช้งาน EventBridge Schedule กันก่อนครับ โดย role นี้จะอนุญาตให้ EventBridge Schedule สามารถ start/stop EC2 ได้

เริ่มจากการเปิดหน้า IAM ขึ้นมา เลือก Roles จากแถบเมนูด้านซ้ายแล้วคลิกที่ Create role

เลือก Trusted entity type เป็น Custom trust policy แล้วใส่ JSON ด้านล่างลงไป

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

ในหน้า Add permissions ให้เลือก AmazonEC2FullAccess (จริง ๆ แล้วจะเลือกอะไรก็ได้ ขอแค่มี ec2:StartInstances กับ ec2:StopInstances ก็พอครับ)

ในหน้า Name, review, and create ให้ตั้งชื่อ role ตามต้องการ ตรวจสอบรายละเอียดต่าง ๆ หลังจากแน่ใจแล้วก็กดสร้าง role ได้เลยครับ

สร้าง EventBridge Scheduler สำหรับ start EC2

จากนี้เราจะมาสร้าง EventBridge Scheduler สำหรับ start EC2 instance กันครับ เริ่มจากการไปที่หน้า console ของ EventBridge แล้วเลือก Schedules จากแถบเมนูด้านซ้าย จากนั้นคลิกที่ Create schedule

ที่บริเวณ Schedule name and description ให้ตั้งชื่อ schedule ตามใจชอบ

ในส่วนของ Schedule pattern ให้เลือก Recurring schedule แล้วทำการตั้งกำหนดการตามต้องการ ในครั้งนี้ผมจะตั้งให้ start EC2 ทุกวันตอน 10:50 น.

มาถึงตรงนี้ผมขอแวะออกนอกเรื่องซักครู่ครับ ก่อนหน้าที่ EventBridge Scheduler จะเปิดตัวออกมา ถ้าหากใครเคยใช้ EventBridge Rule ในการตั้งค่า start/stop EC2 ก็จะพอทราบว่าตอนที่เราทำการตั้งเวลาเพื่อ start/stop EC2 เราจำเป็นจะต้องตั้งเวลาเผื่อไว้ 7 ชั่วโมง เนื่องจาก EventBridge Rule จะใช้ UTC time zone (ช้ากว่าประเทศไทย 7 ชั่วโมง) แต่ใน EventBridge Scheduler เราไม่จำเป็นต้องเผื่อเวลา 7 ชั่วโมงแล้วครับ เพราะ EventBridge Scheduler จะใช้ time zone เดียวกันกับ local time zone ของเรา (ในที่นี้คือ GMT+7) ทำให้การตั้งค่าง่ายขึ้นเยอะเลยครับ

เปรียบเทียบเพิ่มเติมอีกนิดก็คือ ถ้าหากผมอยากตั้งเวลา start EC2 ตอน 10:50 น. ถ้าผมใช้ EventBridge Scheduler ผมจะสามารถตั้งค่าเป็น 10:50 น. ตรง ๆ ได้เลย แต่ถ้าหากผมใช้ EventBridge Rule ผมจะต้องตั้งค่าเป็น 3:50 น. ครับ

ต่อมา Target detail เลือก All APIs แล้วเลือก API เป็น Amazon EC2StartInstances

ที่บริเวณ Input เราสามารถกำหนดค่าของ parameter [3] ให้กับ StartInstances API ได้ ให้ทำการกำหนด ID ของ EC2 instance ที่จะทำการ start ตามด้านล่างนี้ (สามารถเลือก instance หลายตัวได้)

{
  "InstanceIds": [
    "i-yyyyyyyyyyyyyy"
  ]
}

ในส่วนของ Action after schedule completion จะมีค่าให้เลือก 2 อย่าง คือ NONE และ DELETE ในกรณีที่เลือก DELETE หลังจากที่ schedule ทำงานแล้ว schedule จะทำการลบตัวเองโดยอัตโนมัติ เหมาะกับ schedule แบบใช้ครั้งเดียวครับ แต่ในที่นี้เราต้องการให้ schedule ทำงานทุกวัน จึงเลือก NONE

Permissions ให้เลือก Use existing role แล้วเลือก role ที่เราสร้างไว้ก่อนหน้านี้

เท่านี้การตั้งค่าก็เสร็จสิ้นครับ ตรวจสอบรายละเอียดทุกอย่าง เมื่อแน่ใจแล้วก็กดสร้าง schedule ได้เลยครับ

พอถึงเวลาที่เรากำหนดไว้ เมื่อเราลองไปเช็ค EC2 ดู ก็จะพบว่า EC2 ถูก start เรียบร้อยแล้ว

เมื่อลองตรวจสอบใน CloudTrail ดูอีกทาง ก็จะเห็นว่า instance status เปลี่ยนจาก stopped เป็น pending

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAxxxxxxxxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxx",
        "arn": "arn:aws:sts::123456789012:assumed-role/peerapon_eventbridge_scheduler_test/xxxxxxxxxxxxxxxx",
        "accountId": "123456789012",
        "accessKeyId": "ASIAxxxxxxxxxxxxx",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAxxxxxxxxxxxxxxxx",
                "arn": "arn:aws:iam::123456789012:role/peerapon_eventbridge_scheduler_test",
                "accountId": "123456789012",
                "userName": "peerapon_eventbridge_scheduler_test"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2023-12-15T03:50:49Z",
                "mfaAuthenticated": "false"
            }
        }
    },
    "eventTime": "2023-12-15T03:50:49Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "StartInstances",
    "awsRegion": "ap-southeast-1",
    "sourceIPAddress": "13.215.239.244",
    "userAgent": "AmazonEventBridgeScheduler, aws-sdk-java/2.21.38 Linux/5.10.201-191.748.amzn2.x86_64 OpenJDK_64-Bit_Server_VM/11.0.21+11-LTS Java/11.0.21 kotlin/1.6.21-release-334(1.6.21) vendor/Amazon.com_Inc. md/internal exec-env/AWS_ECS_FARGATE io/async http/NettyNio cfg/retry-mode/legacy",
    "requestParameters": {
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-yyyyyyyyyyyyyy"
                }
            ]
        }
    },
    "responseElements": {
        "requestId": "86c035b9-701a-4544-b205-6ecf89748fc3",
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-yyyyyyyyyyyyyy",
                    "currentState": {
                        "code": 0,
                        "name": "pending"
                    },
                    "previousState": {
                        "code": 80,
                        "name": "stopped"
                    }
                }
            ]
        }
    },
    "requestID": "86c035b9-701a-4544-b205-6ecf89748fc3",
    "eventID": "3c78d7ac-7941-42c1-ba87-038876183951",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "ec2.ap-southeast-1.amazonaws.com"
    }
}

สร้าง EventBridge Scheduler สำหรับ stop EC2

จากนั้นเราก็จะมาสร้าง EventBridge Scheduler สำหรับ stop EC2 instance กันต่อครับ

ขั้นตอนเริ่มต้นเหมือนกับตอนสร้าง schedule สำหรับ start EC2 เลยครับ

ในส่วนของ API ให้เลือกเป็น Amazon EC2StopInstances

ที่บริเวณ Input เราสามารถกำหนดค่าของ parameter [4] ให้กับ StopInstances API ได้ ให้ทำการกำหนด ID ของ EC2 instance ที่จะทำการ stop ตามด้านล่างนี้ (สามารถเลือก instance หลายตัวได้)

{
  "InstanceIds": [
    "i-yyyyyyyyyyyyyy"
  ]
}

ส่วนของ Permissions ก็เลือก IAM Role อันเดิมเลยครับ

หลังจากตรวจสอบการตั้งค่าทุกอย่างเสร็จสิ้น ก็สามารถกดสร้าง schedule ได้เลยครับ

และเมื่อลองไปเช็ค EC2 ดูหลังจากถึงเวลาที่กำหนดไว้ ก็พบว่า instance ถูก stop เรียบร้อยแล้ว ซึ่งก็เป็นไปตามคาดครับ

เมื่อไปตรวจสอบดูใน CloudTrail ก็พบว่ามีการเรียกใช้ StopInstances API ถูกบันทึกไว้อยู่ด้วย

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAxxxxxxxxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxx",
        "arn": "arn:aws:sts::123456789012:assumed-role/peerapon_eventbridge_scheduler_test/xxxxxxxxxxxxxxxx",
        "accountId": "123456789012",
        "accessKeyId": "ASIAxxxxxxxxxxxxx",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAxxxxxxxxxxxxxxxx",
                "arn": "arn:aws:iam::123456789012:role/peerapon_eventbridge_scheduler_test",
                "accountId": "123456789012",
                "userName": "peerapon_eventbridge_scheduler_test"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2023-12-15T03:55:33Z",
                "mfaAuthenticated": "false"
            }
        }
    },
    "eventTime": "2023-12-15T03:55:33Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "StopInstances",
    "awsRegion": "ap-southeast-1",
    "sourceIPAddress": "52.76.181.18",
    "userAgent": "AmazonEventBridgeScheduler, aws-sdk-java/2.21.38 Linux/5.10.201-191.748.amzn2.x86_64 OpenJDK_64-Bit_Server_VM/11.0.21+11-LTS Java/11.0.21 kotlin/1.6.21-release-334(1.6.21) vendor/Amazon.com_Inc. md/internal exec-env/AWS_ECS_FARGATE io/async http/NettyNio cfg/retry-mode/legacy",
    "requestParameters": {
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-yyyyyyyyyyyyyy"
                }
            ]
        },
        "force": false
    },
    "responseElements": {
        "requestId": "b2a7e9c7-b454-45cf-ac7e-60b46dc1cfd1",
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-yyyyyyyyyyyyyy",
                    "currentState": {
                        "code": 64,
                        "name": "stopping"
                    },
                    "previousState": {
                        "code": 16,
                        "name": "running"
                    }
                }
            ]
        }
    },
    "requestID": "b2a7e9c7-b454-45cf-ac7e-60b46dc1cfd1",
    "eventID": "3fb56471-8a67-4db8-bf1c-d3b74116a32b",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "ec2.ap-southeast-1.amazonaws.com"
    }
}

สุดท้ายนี้

ในครั้งนี้ผมได้มาแนะนำการใช้ EventBridge Scheduler ในการตั้งเวลา start/stop EC2 instance ให้ได้ดูกันครับ ก่อนหน้านี้จำเป็นจะต้องใช้ Lambda ร่วมด้วย แต่จากนี้ไปเราสามารถทำได้โดยใช้แค่ EventBridge เพียงอย่างเดียว ซึ่งถือเป็นเรื่องที่ดีมากครับ ทุกคนไม่ลองถือโอกาสนี้ พิจารณาการตั้งค่า start/stop EC2 ที่ใช้อยู่เป็นประจำบ้างเหรอครับ

แล้วพบกันใหม่ในโอกาสหน้าครับ

เอกสารอ้างอิง

  1. Use Lambda to stop and start Amazon EC2 instances at regular intervals
  2. EC2 インスタンスの起動と停止を自動化することは出来ますか? (ภาษาญี่ปุ่น)
  3. StartInstances - Amazon Elastic Compute Cloud
  4. StopInstances - Amazon Elastic Compute Cloud
  5. 【Update】วิธีตั้งค่ากำหนดการ Start EC2 Instance อัตโนมัติด้วย Amazon EventBridge
  6. 【Update】วิธีตั้งค่ากำหนดการ Stop EC2 Instance อัตโนมัติด้วย Amazon EventBridge