Jak na bieżąco monitorować koszty w AWS
Często pracując z chmurą AWS korzystamy więcej niż z jednego konta. Więcej, prawie zawsze korzystamy więcej niż z jednego konta. Podejść do tematu jest dużo. O użyciu AWS Organizations i wielu kontach pisał już Łukasz Dorosz. W dużych organizacjach istotna jest bieżąca kontrola kosztów na kontach w AWS. A przyda się nawet w tych mniejszych. Pisałem już o tym za co tak naprawdę płacimy w chmurze. Dałem też kilka wskazówek jak oszczędzać w chmurze. A jeżeli chcesz wiedzieć jak na bieżąco monitorować koszty w AWS w przypadku włączonego Consolidated billing to zapraszam.
Jak na bieżąco monitorować koszty w AWS
Można oczywiście zaglądać codziennie do dashboardu z billingiem, można (a nawet trzeba) do każdego konta dodać tzw. billing alarm, ale nie do końca o to chodzi. Chcemy znać na bieżąco koszty chmury na poszczególnych kontach wchodzących w skład naszej organizacji.
Jako zwolennik automatyzacji i rozwiązań Cloud Native, a szczególnie serverless, zaproponuję rozwiązanie, które codziennie poinformuje nas o bieżących kosztach i przewidywanym koszcie na koniec miesiąca. Ale do rzeczy.
Całość jest dostępna na moim GitHub-ie.
Komponenty
Będziemy potrzebowali:
- funkcji Lambda, która sprawdzi bieżące koszty,
- reguły w usłudze CloudWatch, która raz dziennie wywoła naszą funkcję,
- topica SNS,, na który zostaną wysłane informacje o kotach.
Do topica SNS będziemy musieli dodać subskrybentów, czyli adresy email, na które będą przesyłane informacje o kosztach.
Za orkiestrację całości będzie odpowiadał Serverless Framework.
Funkcja Lambda
Będziemy potrzebowali dostać się poprzez API do AWS Cost Explorera. Tak naprawdę będzie interesowała nas jedna metoda, która pozwoli na pobranie kosztów w bieżącym miesiącu. Chodzi o metodę GetCostAndUsage. Dodatkowo pobierzemy sobie listę kont w organizacji za pomocą metody ListAccounts Co prawda przy kosztach dostajemy też listę kont, ale przy jakichkolwiek zmianach w organizacji, ta metoda potrafi zwracać „zwariowane” wyniki jeżeli chodzi o tą listę.
To co musimy wysłać do metody GetCostAndUsage to:
- TimePeriod – czyli czas, który nas interesuje pod kątem kosztów
- Granulatiry – czyli rozdzielczość, w naszym przypadku będzie to miesiąc
- Metrics – czyli o co nam tak naprawdę chodzi, u nas to będzie NET_UNBLENDED_COST
-
GroupBy – dla nas crème de la crème, w końcu chcemy poznać koszty dla poszczególnych kont wchodzących w skład naszej organizacji.
OK, podsumowując, u nas wywołanie metody będzie wyglądało następująco:
response = ce_client.get_cost_and_usage( TimePeriod={ 'Start': str(first_day), 'End': str(last_day) }, Granularity='MONTHLY', Metrics=[ 'NET_UNBLENDED_COST', ], GroupBy=[ { 'Type': 'DIMENSION', 'Key': 'LINKED_ACCOUNT' }, ] )
To, na co trzeba zwrócić uwagę to daty przekazywane do API. Szczególnie na wartość End.
Nasza funkcja potrafi trochę więcej. Zwraca także forecast do końca miesiąca oraz porównuje przyrost kosztów w stosunku do dnia wczorajszego. i jeżeli ten przyrost jest znaczny, wysyła wiadomość na dodatkowy topic SNS. W końcu nie każdy chce dostawać codzienne podsumowanie. Mogą interesować nas tylko „nagłe” przypadki.
Nie można oczywiście pominąć roli dla funkcji. W naszym przypadku musimy oczywiście nadać uprawnienia do wykonywania wykorzystywanych przez nas metod API, do wysyłania wiadomości do usługi SNS oraz użycia klucza szyfrującego.
iamRoleStatements: - Effect: "Allow" Action: - "ce:GetCostForecast" - "ce:GetCostAndUsage" - "organizations:ListAccounts" Resource: '*' - Effect: "Allow" Action: - "sns:Publish" Resource: - '*' - Effect: "Allow" Action: - "kms:Decrypt" - "kms:GenerateDataKey*" Resource: - !GetAtt SNSKey.Arn
Topic SNS
Tu nie ma filozofii. Tworzymy topic w usłudze SNS, na który będziemy wysyłali wiadomości o bieżących kosztach i od którego będziemy mogli dodać sybskrybentów. Po naszemu, odbieraczy powiadomień o kosztach.
W przypadku Serverlsess Framework i Cloud Formation wygląda to następująco
CostsNotificationTopic: Type: AWS::SNS::Topic Properties: TopicName: !Sub '${AWS::StackName}-CostsNotificationTopic' KmsMasterKeyId: !Ref SNSKey DisplayName: !Sub '${AWS::StackName}-CostsNotificationTopic'
SNSKey szyfrujący wiadomości, tworzymy osobno
SNSKey: Type: AWS::KMS::Key Properties: Description: 'The key used for SNS topic encryption' Enabled: true EnableKeyRotation: true KeyPolicy: Version: '2012-10-17' Statement: - Sid: Enable IAM User Permissions Effect: Allow Principal: AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root' Action: kms:* Resource: '*' KeySpec: SYMMETRIC_DEFAULT KeyUsage: ENCRYPT_DECRYPT PendingWindowInDays: 7
EventBridge Rule
Na koniec zostawiłem zasadę w EventBridge, która raz dziennie, w naszym przypadku o godzinie 6 rano (UTC) wywoła naszą funkcję.
Taką zasadę definiujemy za pomocą Serverless Framework jako event wywołujący funkcję Lambda.
events: - schedule: rate: cron(0 6 * * ? *)
Stack CloudFormation
Jeżeli ściągnęliście źródła z mojego repozytorium i macie zainstalowany Serverless Framework to tak naprawdę wystarczy teraz zdeployować stack na Wasze główne konto płacące za pomocą komendy sls deploy
i po chwili wszystkie zasoby powinny zostać utworzone
i całość powinna być gotowa do działania.
Jak na bieżąco monitorować koszty w AWS – Raporty
Aby otrzymać codzienny bądź alarmowy raport należy oczywiście dodać swój adres email jako sybskrybenta do odpowiedniego topica SNS. Nie będę już opisywał tego procesu, jest on prosty. W razie potrzeby pomoże dokumentacja AWS. Pamiętajcie o potwierdzeniu subskrypcji.
Od teraz każdego poranka powinniście otrzymać maila o treści podobnej do
[ { "account_id": "123456789012", "name": "ORG-SBX-1", "email": "email-1@adres.com", "cost": 91.7825892871, "previous_cost": 91.7825892871, "forecast": 0.0, "error": "An error occurred (DataUnavailableException) when calling the GetCostForecast operation: Insufficient amount of historical data." }, { "account_id": "123456789013", "name": "ORG-SBX-2", "email": "email-2@adres.com", "cost": 22.5223533266, "previous_cost": 22.5223533266, "forecast": "77.34154705532922", "error": "" }, { "account_id": "123456789014", "name": "ORG-SBX-3", "email": "email-3@adres.com", "cost": 22.3125702711, "previous_cost": 22.3125702711, "forecast": "71.99064409697596", "error": "" }, .... ]