AWS Savings Plans - spadek utilization poniżej 80% i marnowanie commitment
Diagnoza i naprawa spadku utilization AWS Savings Plans poniżej 80%: identyfikacja przyczyn (scale-down, migracja, wrong region, sezonowość) i strategie odzyskania wartości commitment.
Ten runbook opisuje diagnozę spadku wykorzystania Savings Plans i strategie odzyskania wartości zakupionego zobowiązania. Szczegółowe porównanie Savings Plans z Reserved Instances oraz strategie zakupowe znajdziesz w artykule AWS Savings Plans vs Reserved Instances - zniżki bez ryzyka lock-in.
Objaw
Savings Plans Utilization Report w Cost Explorer pokazuje spadek poniżej progu 80%:
# Sprawdź utilization Savings Plans za ostatnie 30 dni
aws ce get-savings-plans-utilization \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--output json | jq '.Total'
# Przykładowy output wskazujący na problem:
# {
# "Utilization": {
# "TotalCommitment": "7200.00",
# "UsedCommitment": "5040.00",
# "UnusedCommitment": "2160.00",
# "UtilizationPercentage": "70.0" <-- poniżej 80%
# },
# "Savings": {
# "NetSavings": "1260.00",
# "OnDemandCostEquivalent": "8820.00"
# },
# "AmortizedCommitment": {
# "AmortizedRecurringCommitment": "0.00",
# "AmortizedUpfrontCommitment": "7200.00",
# "TotalAmortizedCommitment": "7200.00"
# }
# }
# Sprawdź dzienny trend utilization
aws ce get-savings-plans-utilization \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY \
--output json | jq '.SavingsPlansUtilizationsByTime[] | {Date: .TimePeriod.Start, Utilization: .Utilization.UtilizationPercentage}'
# Sprawdź szczegóły zakupionych Savings Plans
aws savingsplans describe-savings-plans \
--output table \
--query 'savingsPlans[*].{ID:savingsPlanId,Type:savingsPlanType,Commitment:commitment,State:state,Region:region,Start:start,End:end}'
W konsoli AWS problem jest widoczny w: Cost Explorer → Savings Plans → Utilization Report. Czerwony lub żółty wskaźnik wykorzystania poniżej 80% sygnalizuje marnowanie zobowiązania.
Przyczyna
Spadek wykorzystania Savings Plans wynika z czterech głównych scenariuszy:
-
Infrastruktura została przeskalowana w dół. Środowiska dev/test zostały wyłączone, projekt zakończony, lub zespół zredukował zasoby po optymalizacji. Zużycie compute spadło poniżej poziomu zakupionego zobowiązania. Jest to najczęstsza przyczyna w organizacjach, które kupują SP na podstawie szczytowego zużycia (peak usage) zamiast bazowego zużycia (baseline).
-
Workload przeniesiony na inny serwis niepokryty przez typ SP. EC2 Instance Savings Plan nie pokrywa Fargate ani Lambda. Jeśli zespół zmigrował aplikację z EC2 na Fargate - instancyjny SP przestaje być wykorzystywany. Tylko Compute Savings Plan pokrywa EC2 + Fargate + Lambda jednocześnie.
-
Savings Plan zakupiony na nieprawidłowy region (EC2 Instance SP). EC2 Instance Savings Plans są powiązane z konkretnym regionem i rodziną instancji. Jeśli workload został przeniesiony do innego regionu (np. DR failover, migracja bliżej użytkowników) - SP w starym regionie nie jest wykorzystywany.
-
Sezonowy spadek ruchu poniżej bazowego zobowiązania. Biznes ma naturalne wahania sezonowe (e-commerce po świętach, EdTech poza semestrem, turystyka poza sezonem). Jeśli SP zakupiono na poziomie szczytowego/średniego zużycia zamiast minimum - w okresach niskiego ruchu wykorzystanie spada.
Rozwiązanie
Krok 1: Zidentyfikuj, które Savings Plans mają niskie wykorzystanie
# Sprawdź utilization per Savings Plan
aws ce get-savings-plans-utilization-details \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--output json | jq '.SavingsPlansUtilizationDetails[] | {
SavingsPlanArn: .SavingsPlanArn,
Utilization: .Utilization.UtilizationPercentage,
UnusedCommitment: .Utilization.UnusedCommitment,
Type: .Attributes.SavingsPlansType,
Region: .Attributes.Region,
InstanceFamily: .Attributes.InstanceFamily
}'
# Przykładowy output:
# {
# "SavingsPlanArn": "arn:aws:savingsplans::123456789012:savingsplan/abcd1234",
# "Utilization": "62.5",
# "UnusedCommitment": "2700.00",
# "Type": "EC2InstanceSavingsPlans",
# "Region": "eu-west-1",
# "InstanceFamily": "m5"
# }
# Sprawdź historię zużycia EC2 w tym regionie/family
aws ce get-cost-and-usage \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY \
--metrics "UsageQuantity" "UnblendedCost" \
--filter '{
"And": [
{"Dimensions": {"Key": "SERVICE", "Values": ["Amazon Elastic Compute Cloud - Compute"]}},
{"Dimensions": {"Key": "REGION", "Values": ["eu-west-1"]}}
]
}' \
--group-by Type=DIMENSION,Key=INSTANCE_TYPE \
--output json | jq '.ResultsByTime[-1].Groups[] | select(.Keys[0] | startswith("m5"))'
Krok 2: Zdiagnozuj przyczynę spadku zużycia
# Porównaj zużycie compute w okresie przed i po spadku utilization
# Ostatnie 60 dni - szukaj momentu spadku
aws ce get-cost-and-usage \
--time-period Start=$(date -d '60 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY \
--metrics "UnblendedCost" \
--filter '{"Dimensions": {"Key": "SERVICE", "Values": ["Amazon Elastic Compute Cloud - Compute"]}}' \
--output json | jq '[.ResultsByTime[] | {Date: .TimePeriod.Start, Cost: .Total.UnblendedCost.Amount}]'
# Sprawdź czy workload przeniesiono na Fargate/Lambda
aws ce get-cost-and-usage \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--filter '{"Dimensions": {"Key": "SERVICE", "Values": [
"Amazon Elastic Compute Cloud - Compute",
"AWS Fargate",
"AWS Lambda"
]}}' \
--group-by Type=DIMENSION,Key=SERVICE \
--output table
# Sprawdź zmiany w running instances
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query 'Reservations[*].Instances[*].{ID:InstanceId,Type:InstanceType,AZ:Placement.AvailabilityZone,Launch:LaunchTime}' \
--output table --region eu-west-1 | grep "m5"
Krok 3: Strategie odzyskania wartości - w zależności od przyczyny
Scenariusz A: Infrastruktura przeskalowana w dół - dodaj workload
# Jeśli masz niewykorzystany commitment na m5 w eu-west-1
# Przenieś inne workloady (dev, staging, batch) na instancje m5
# Sprawdź jakie on-demand instancje działają w INNYCH regionach
# które mogłyby być przeniesione do eu-west-1
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query 'Reservations[*].Instances[*].{Type:InstanceType,AZ:Placement.AvailabilityZone,Name:Tags[?Key==`Name`].Value|[0]}' \
--output table --region us-east-1
# Zidentyfikuj workloady batch/dev które mogą działać w eu-west-1
# i pokryć niewykorzystany commitment
Scenariusz B: Migracja na Fargate - zobowiązanie nie pokrywa nowego serwisu
# Jeśli masz EC2 Instance SP ale workload przeniósł się na Fargate
# Opcje:
# 1. Poczekaj na wygaśnięcie SP (sprawdź datę końca)
aws savingsplans describe-savings-plans \
--savings-plan-ids "abcd1234-..." \
--query 'savingsPlans[0].{End:end,RemainingMonths:end}' \
--output table
# 2. Nie kupuj kolejnego EC2 Instance SP
# 3. Przy następnym zakupie - wybierz Compute SP (pokrywa EC2+Fargate+Lambda)
# Sprawdź ile kosztuje Fargate - czy Compute SP by to pokrył
aws ce get-cost-and-usage \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--filter '{"Dimensions": {"Key": "SERVICE", "Values": ["AWS Fargate"]}}' \
--output json | jq '.ResultsByTime[0].Total.UnblendedCost'
Scenariusz C: Nieprawidłowy region - przenieś workload lub zaakceptuj stratę
# Sprawdź faktyczne zużycie compute per region
aws ce get-cost-and-usage \
--time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--filter '{"Dimensions": {"Key": "SERVICE", "Values": ["Amazon Elastic Compute Cloud - Compute"]}}' \
--group-by Type=DIMENSION,Key=REGION \
--output table
# Jeśli workload jest teraz w us-east-1 a SP jest w eu-west-1:
# Opcja 1: przenieś non-latency-sensitive workloady do eu-west-1
# Opcja 2: zaakceptuj stratę do końca SP i kup nowy w prawidłowym regionie
Scenariusz D: Spadek sezonowy - ustaw budżety i alerty
# Ustaw alert na spadek utilization poniżej 80%
aws ce create-anomaly-monitor \
--anomaly-monitor '{
"MonitorName": "SavingsPlansUtilizationDrop",
"MonitorType": "CUSTOM",
"MonitorSpecification": {
"Dimensions": {
"Key": "SERVICE",
"Values": ["Amazon Elastic Compute Cloud - Compute"]
}
}
}'
# Przy sezonowości: kupuj SP na poziomie MINIMUM baseline (nie average)
# Przykład: jeśli zużycie waha się $8/h - $15/h
# Kup SP na $7/h (baseline) - reszta pokrywana on-demand
# To daje 100% utilization SP + elastyczność na wahania
Krok 4: Prewencja - konfiguracja alertów FinOps
# Utwórz budżet z alertem na SP utilization
# (wymaga AWS Budgets - Savings Plans Utilization budget type)
# W konsoli: AWS Budgets → Create budget → Savings Plans utilization budget
# Threshold: alert gdy utilization < 80%
# Notify: finops-team@company.com
# Alternatywnie - CloudWatch metric (dostępne z Cost Explorer API)
# Ustaw alarm na SavingsPlanUtilization < 80
aws cloudwatch put-metric-alarm \
--alarm-name "SP-Utilization-Below-80" \
--alarm-description "Savings Plans utilization dropped below 80%" \
--metric-name "SavingsPlanUtilization" \
--namespace "AWS/CostExplorer" \
--statistic Average \
--period 86400 \
--threshold 80 \
--comparison-operator LessThanThreshold \
--evaluation-periods 3 \
--alarm-actions "arn:aws:sns:eu-west-1:123456789012:finops-alerts"
Walidacja
Po wdrożeniu zmian (dodanie workload, zmiana regionu, aktualizacja pipeline) zweryfikuj poprawę wykorzystania:
# 1. Sprawdź bieżące utilization (potrzeba 24-48h na propagację danych)
aws ce get-savings-plans-utilization \
--time-period Start=$(date -d '7 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY \
--output json | jq '.SavingsPlansUtilizationsByTime[-1] | {
Date: .TimePeriod.Start,
Utilization: .Utilization.UtilizationPercentage,
Unused: .Utilization.UnusedCommitment
}'
# Expected: UtilizationPercentage >= 80
# 2. Potwierdź że net savings są pozytywne
aws ce get-savings-plans-utilization \
--time-period Start=$(date -d '7 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--output json | jq '.Total.Savings.NetSavings'
# Expected: wartość > 0 (oszczędności netto)
# 3. Sprawdź coverage - ile on-demand spend jest pokryte przez SP
aws ce get-savings-plans-coverage \
--time-period Start=$(date -d '7 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--output json | jq '.Total.Coverage.CoveragePercentage'
# Expected: wartość w zakresie docelowego pokrycia (np. 70-85%)
# 4. Zweryfikuj rekomendacje AWS - czy sugerują zmianę
aws ce get-savings-plans-purchase-recommendation \
--savings-plans-type "COMPUTE_SP" \
--term-in-years "ONE_YEAR" \
--payment-option "NO_UPFRONT" \
--lookback-period-in-days "THIRTY_DAYS" \
--output json | jq '.SavingsPlansPurchaseRecommendation.SavingsPlansPurchaseRecommendationDetails[0]'
Savings Plans, którego nie wykorzystujesz, jest gorszą inwestycją niż on-demand - płacisz zobowiązanie i nie dostajesz rabatu. Kluczowa zasada: kupuj SP na bazowe zużycie (minimum gwarantowanego zużycia), nie na szczytowe zużycie ani average. Lepiej mieć 100% wykorzystania mniejszego SP i resztę on-demand, niż 60% wykorzystania dużego SP. Przy następnym zakupie rozważ Compute SP zamiast EC2 Instance SP - elastyczność jest warta niewielkiej różnicy w rabacie.
Savings Plans nie przynoszą oczekiwanych oszczędności?
Umów bezpłatną 30-minutową rozmowę. Przeanalizujemy Twoje zobowiązanie, zidentyfikujemy luki w wykorzystaniu i zaproponujemy strategię FinOps dopasowaną do Twojego profilu zużycia.