المستوى المطلوب: متوسط. تحتاج معرفة أساسية بـ AWS، وتشغيل سكربت Python، وقراءة صلاحيات IAM. مدة القراءة حوالي 6 دقائق.
لو خوادم التطوير والـ staging عندك شغّالة 24 ساعة في اليوم وانت بتستخدمها 8 ساعات بس، انت بتدفع فاتورة الحوسبة كاملة مقابل استخدام ثلثها. سكربت واحد يطفّي أي خادم عليه وسم محدد كل ليلة ويشغّله الصبح، فينزّل فاتورة الحوسبة لغير الإنتاج بما يقارب 65%.
المشكلة باختصار
خوادم بيئات التطوير مالهاش لازمة بالليل ولا في عطلة الأسبوع. محدش بيشتغل عليها الساعة 3 الفجر يوم الجمعة. ومع ذلك الفاتورة بتعدّ كل ثانية. الطريقة الشائعة إن كل واحد يطفّي خادمه بإيده قبل ما يمشي بتفشل دايمًا: حد بينسى، وحد بيسيب الخادم شغّال أسبوع كامل. الحل مش تذكير الناس، الحل أتمتة تشيل القرار من إيدهم.
ليه بتدفع على وقت مش بتستخدمه؟
تخيّل عدّاد تاكسي شغّال. طول ما العربية واقفة بموتور دائر، العدّاد بيزيد حتى لو انت نازل بتشتري حاجة. لو قفلت الموتور، العدّاد بيقف، بس الإيجار الشهري للجراج اللي راكنها فيه بيفضل عليك.
ده بالظبط اللي بيحصل في AWS. الخادم من نوع on-demand بتتحاسب عليه بالثانية طول ما هو في حالة running. أول ما تعمله stop، تكلفة المعالج والذاكرة (vCPU وRAM) بتقف تمامًا. لكن قرص التخزين المرتبط به (EBS) والـ Elastic IP بيفضلوا محسوبين، زي إيجار الجراج. يعني الإطفاء بيوفّر الجزء الأكبر (الحوسبة) بدون ما تفقد بياناتك على القرص.
الحل: EventBridge يشغّل Lambda يطفّي بالوسم
الفكرة في ثلاث قطع بسيطة: منبّه بمواعيد ثابتة (EventBridge Scheduler)، ودالة تنفّذ القرار (Lambda)، ووسم على الخوادم يحدد مين اللي ينطفّي. الخطوات:
- حط وسم على كل خادم تطوير تقبل إطفاءه:
AutoStop = true. الخوادم اللي من غير الوسم مش هتتلمس. - اكتب دالة Lambda تدوّر على الخوادم بالوسم وتطفّيها أو تشغّلها حسب الحدث.
- اربط قاعدتين في EventBridge Scheduler: وحدة تبعت
stopالساعة 8 مساءً، ووحدة تبعتstartالساعة 8 صباحًا، أيام العمل بس.
ده كود الدالة بلغة Python كامل وقابل للنسخ:
import boto3
ec2 = boto3.client("ec2")
def lambda_handler(event, context):
action = event.get("action", "stop") # "stop" أو "start"
filters = [{"Name": "tag:AutoStop", "Values": ["true"]}]
# اطفي اللي شغّال، وشغّل اللي واقف — بدون أخطاء
wanted_state = "running" if action == "stop" else "stopped"
filters.append({"Name": "instance-state-name", "Values": [wanted_state]})
ids = []
pages = ec2.get_paginator("describe_instances").paginate(Filters=filters)
for page in pages:
for res in page["Reservations"]:
for inst in res["Instances"]:
ids.append(inst["InstanceId"])
if not ids:
return {"action": action, "count": 0}
if action == "stop":
ec2.stop_instances(InstanceIds=ids)
else:
ec2.start_instances(InstanceIds=ids)
return {"action": action, "count": len(ids), "ids": ids}