اعمل تنبيه ميزانية AWS يومي قبل ما الفاتورة تفلت
هتخرج من المقال بسكربت عملي يقيس صرف AWS كل صباح، ويبعث Slack alert لما الـ burn-rate يقول إنك هتعدي الميزانية.
مستوى القارئ: متوسط
المشكلة باختصار
فاتورة AWS غالبًا مش بتضرب مرة واحدة. اللي بيحصل فعلاً إن خدمة صغيرة تزيد 20 دولار يوميًا، وبعد أسبوع تلاقي الفرق بقى 140 دولار. الطريقة الشائعة الغلط إنك تستنى نهاية الشهر أو تعتمد على شخص يفتح Billing Dashboard يدويًا. الطريقة دي بتفشل وقت الضغط، وخصوصًا لو عندك أكثر من بيئة أو أكثر من حساب.
الافتراض هنا إن عندك حساب AWS واحد أو management account، وميزانية شهرية واضحة، وفريقك بيستخدم Slack. لو عندك 10 خدمات فقط وميزانية 750 دولار، تنبيه يومي واحد كفاية كبداية. تكلفة الاستعلام اليومي من Cost Explorer API حوالي 0.01 دولار لكل طلب paginated حسب تسعير AWS، يعني 30 طلب في الشهر تقريبًا يساووا 0.30 دولار قبل أي pagination إضافي.
الفكرة بمثال قبل التعريف
ركز في المثال ده. عندك SaaS صغير، وميزانية AWS الشهرية 750 دولار. في يوم 26 من الشهر الصرف الفعلي وصل 690 دولار. لو حسبت المتوسط اليومي، هتلاقيه 26.54 دولار في اليوم. التوقع البسيط لنهاية شهر 31 يوم يساوي تقريبًا 823 دولار. هنا أنت مش محتاج تستنى الفاتورة. أنت محتاج رسالة تقول: “الصرف الحالي 92% من الميزانية، والتوقع 109%”.
ده اسمه burn-rate alert. بدل ما تسأل: “صرفنا كام؟” بتسأل: “لو كملنا بنفس السرعة، هنقفل الشهر على كام؟”. الفرق مهم. الأول رقم تاريخي. الثاني قرار تشغيلي.
الخطوات
- فعّل Cost Explorer في حساب AWS لو مش مفعّل. أول تجهيز للبيانات ممكن يحتاج وقت.
- اعمل IAM user أو role بصلاحية قراءة محدودة لـ Cost Explorer فقط.
- جهّز Slack Incoming Webhook من إعدادات Slack App.
- حدد الميزانية الشهرية في متغير بيئة، مثل
MONTHLY_BUDGET=750. - اجعل السكربت يقرأ التكلفة من أول الشهر حتى تاريخ اليوم.
- احسب النسبة الحالية والتوقع لنهاية الشهر.
- ارسل Slack message فقط عند تجاوز 80% أو عند توقع تجاوز 100%.
- شغّل السكربت يوميًا بـ cron أو GitHub Actions أو أي scheduler داخلي.
السكربت العملي
استخدم Node.js لو فريقك عنده JavaScript بالفعل. المكسب إن الكود صغير وسهل تشغيله في CI أو VM. التكلفة إنك محتاج تضبط AWS credentials وSlack webhook كـ secrets، ومينفعش تسيبهم داخل الملف.
import { CostExplorerClient, GetCostAndUsageCommand } from "@aws-sdk/client-cost-explorer";
import https from "node:https";
const budget = Number(process.env.MONTHLY_BUDGET || "750");
const slackUrl = process.env.SLACK_WEBHOOK_URL;
const threshold = Number(process.env.ALERT_THRESHOLD || "0.8");
if (!slackUrl) throw new Error("SLACK_WEBHOOK_URL is required");
const now = new Date();
const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
const daysPassed = Math.max(1, now.getUTCDate());
const daysInMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 0)).getUTCDate();
function isoDay(d) {
return d.toISOString().slice(0, 10);
}
const client = new CostExplorerClient({ region: "us-east-1" });
const result = await client.send(new GetCostAndUsageCommand({
TimePeriod: { Start: isoDay(start), End: isoDay(end) },
Granularity: "MONTHLY",
Metrics: ["UnblendedCost"]
}));
const amount = Number(result.ResultsByTime?.[0]?.Total?.UnblendedCost?.Amount || "0");
const currentRatio = amount / budget;
const forecast = (amount / daysPassed) * daysInMonth;
const forecastRatio = forecast / budget;
if (currentRatio >= threshold || forecastRatio >= 1) {
const text = [
`AWS budget alert`,
`Actual: $${amount.toFixed(2)} / $${budget.toFixed(2)} (${(currentRatio * 100).toFixed(1)}%)`,
`Forecast: $${forecast.toFixed(2)} (${(forecastRatio * 100).toFixed(1)}%)`,
`Next action: افتح Cost Explorer وشوف أعلى خدمة زادت آخر 7 أيام.`
].join("\n");
const body = JSON.stringify({ text });
await new Promise((resolve, reject) => {
const req = https.request(slackUrl, { method: "POST", headers: { "Content-Type": "application/json" } }, res => {
res.resume();
res.on("end", resolve);
});
req.on("error", reject);
req.write(body);
req.end();
});
}
تشغيله يوميًا
ثبت الحزم، ثم شغّل السكربت يدويًا مرة واحدة قبل الجدولة. لو الرسالة وصلت Slack، انقله إلى cron. لو ما وصلتش، راجع صلاحيات IAM والـ webhook.
npm init -y
npm install @aws-sdk/client-cost-explorer
export MONTHLY_BUDGET=750
export ALERT_THRESHOLD=0.8
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/REPLACE/ME"
node budget-check.js
# تشغيل يومي الساعة 8 صباحًا
0 8 * * * cd /opt/aws-budget-alert && /usr/bin/node budget-check.jsقياس قبل وبعد
قبل السكربت، السيناريو الواقعي عند فريق صغير إن المراجعة اليدوية تحصل مرة أسبوعيًا وتأخذ 10 دقائق. ده يعني 40 دقيقة شهريًا، وغالبًا التنبيه يأتي متأخرًا. بعد السكربت، عندك طلب API يومي ورسالة عند الخطر فقط. القياس العملي هنا: 30 طلب API تقريبًا في الشهر، بتكلفة تبدأ من 0.30 دولار للطلبات العادية، مقابل تقليل مراجعة يدوية من 40 دقيقة إلى أقل من 5 دقائق شهريًا.
الـ trade-off هنا
أفضل طريقة كبداية هي تنبيه يومي بسيط، مش dashboard كاملة. بتكسب سرعة تنفيذ ووضوح. بتخسر التفاصيل الدقيقة مثل توزيع التكلفة حسب الخدمة أو tag. لو عايزها تدعم أكثر من فريق، أضف GroupBy على SERVICE أو tags، لكن خليك فاكر إن الاستعلامات paginated ممكن تزيد عدد الطلبات، وبالتالي تزيد تكلفة API.
فيه trade-off تاني: Slack alert سهل وسريع، لكنه ممكن يتحول لضوضاء. لذلك خلي التنبيه يظهر عند 80% من الميزانية أو عند توقع تجاوز 100%. متبعتش رسالة كل يوم لو الأرقام طبيعية.
متى لا تستخدم هذه الطريقة
لا تستخدمها كبديل كامل لـ AWS Budgets لو عندك compliance أو approval workflow رسمي. لا تستخدمها لو تحتاج forecast دقيق مبني على seasonal traffic. الحساب هنا بسيط: average spend حتى اليوم مضروبًا في عدد أيام الشهر. كمان لا تستخدم webhook مكشوف أو محفوظ داخل repo؛ ده secret فعلي ويجب تخزينه في environment أو secret manager.
المصادر
الخطوة التالية
اعمل نسخة من السكربت، واضبط MONTHLY_BUDGET على رقمك الحقيقي، وشغّله يدويًا مرة. لو وصلتك رسالة Slack صحيحة، أضف cron. لو الرسالة قالت إن التوقع فوق 100%، افتح Cost Explorer ورتّب أعلى الخدمات حسب آخر 7 أيام قبل ما تغيّر أي إعداد.