AWS Lambda i monitoring strony cz. 2

Artykuł ten jest kontynuacją wątku opisującego w jaki sposób wykorzystać AWS Lambda do monitoringu strony www.

Dla przypomnienia AWS Lambda jest rozwiązaniem serverless, czyli możemy wykonać zdefiniowany zestaw funkcji bez powoływania i zarządzania jakimikolwiek serwerami.

W pierwszej części artykułu stworzyłem rozwiązanie, które monitoruje zwracany kod HTTP i tworzy metrykę w CloudWatch jako tzw. custom metric. Następnie dla tej metryki skonfigurowany został alarm, który wysyła powiadomienia na email (przy wykorzystaniu AWS SNS) za każdym razem gdy coś jest nie tak.
Rozwiązanie składa się z dwóch funkcji Lambda:

  • Zadaniem WebsiteCheck jest aktywny monitoring strony oraz przekazanie wartości znaczników do drugiej funkcji.
  • SendMetric przesyła otrzymaną wartość do systemu monitoringu Cloudwatch.
    Idąc o krok dalej postanowiłem rozszerzyć to rozwiązanie o funkcje, która sama bez mojej ingerencji będzie leczyć środowisko. Powody są dwa, przede wszystkim to, że jestem leniwym człowiekiem i staram się zbędne rzeczy delegować, a po drugie chcę zwiekszyć dostępność strony www. Wiadomo, że maila nie czyta się non stop, nie zawsze jest dostęp do komputera, dlatego przyszedł mi pomysł na zrobienie samo naprawiającego się środowiska.

Architektura

Moje środowisko jak już wcześniej pisałem jest proste, linuksowa instancja EC2 z zainstalowanym całym zestawem potrzebnym do obsługi wordpress plus baza danych.
Zatem możliwe scenariusze, że coś na serwerze przestanie działać to apache, albo baza danych.
Pogrzebałem trochę w necie i znalazłem na blogu AWS opis funkcji Lambda, która będzie w stanie logować się po ssh na instancję EC2 i wykonywać pewien zestaw komend. Zmodyfikowałem ją trochę na swoje potrzeby i dostosowałem tak aby działała z moim dotychczasowym rozwiązaniem.

Jak to działa?

AWS_Lambda_Monitoring

Odwołując się do pierwszej cześci artykułu, funkcja WebsiteCheck bada czy jest w stanie dostać się na strone. W sytuacji kiedy coś jest nie tak, czyli strona nie zwraca kodu http 200 bądź wogóle nie można się podłączyć, wywoływana jest kolejna funkcja RepairSystem. To jej zadań należy:

  • Pobranie z S3 klucza SSH i zalogowanie się na serwer,
  • Skopiowanie z S3 na serwer skryptu sprawdzającego i naprawiającego ewentualne problemy,
  • Wywołanie z bash’u pobranego skryptu.
    Funkcja RepairSystem musi oczywiście wiedzieć na który serwer ma się zalogować. Do tego celu wykorzystałem to, że funkcja WebsiteCheck przekazuje pewien parametr, który służy do nadania nazwy metryce w CloudWatch. Ja dodatkowo stworzyłem nowy Tag dla maszyny wirtualnej z taką sama wartością. W ten sposób funkcja RepairSystem otrzymuje ten parametr i szuka maszyny która ma Tag z taką samą wartością. Następnie pobiera publiczny adres IP tej instancji oraz nazwę klucza do połączenia po SSH. Z tym zestawem informacji funkcja wykonuje pozostałe zadania.

Konfiguracja

Kluczową sprawą jest przygotowanie funkcji RepairSystem. Z racji, że zawiera dodatkowe komponenty paramiko i pycrypto, trzeba przygotować ją w postaci paczki zip, którą potem zaimportujemy do nowo tworzonej funkcji. Poniżej przedstawiam kroki przygotowania paczki (ja to robiłem na czystej amazonowej instancji ec2):

Instalacja virtualenv:

$ pip install virtualenv

Konfiguracja wirtualnego środowiska:

$ virtualenv –p /usr/bin/python2.7 path/to/my/helloworld-env

Aktywacja środowiska:

$source path/to/my/helloworld-env/bin/activate

Instalacja dodatkowych komponentów:

$pip install pycrypto
$pip install paramiko

Gdyby coś się sypało przy instalacji powyższych pakietów, doinstalować trzeba jeszcze:

$yum install python27-devel python27-pip gcc -y

Pakujemy to wszystko razem do zip-a:

$cd path/to/my/helloworld-env/lib/python2.7/site-packages
$zip -r9 /home/ec2-user/repair_function.zip 
$cd path/to/my/helloworld-env/lib64/python2.7/site-packages
$zip -r9 /home/ec2-user/repair_function.zip 

No i oczywiście do paczki dorzucamy mózg naszej funkcji, czyli plik z zestawem funkcji i akcji do wykonania.

$zip /home/ec2-user/repair_function.zip /home/ec2-user/function.py

Dopiero tak przygotowaną paczkę można zaimplementować do Lambda:

AWS_Lambda_Repair_Function_Conf

Pamiętać należy o dobrym opisie pola Handler*, “function” to nazwa naszego pliku z konfiguracją wszystkich zadań (function.py), zaś “worker_handler” to nazwa głównej funkcji jaką wywołujemy. Bardzo ważne jest również przypisanie odpowiednich Policy, w mojej roli RepairSystem_Fuction znajdują się poniższe polityki:

AWS_Lambda_RepairSystem_Role

Pierwsza potrzebna jest do odczytania informacji o instancji (adres IP, nazwa klucza), druga uprawnia do dostępu do S3 bucketu z kluczami, a trzecia pozwala utworzyć log z wykonania funkcji.

Na koniec wszystko zapisujemy i możemy na szybko przetestować. Najpierw jednak trzeba zdefiniować parametry testu, czyli tzw. input event, czyli to co przekazujemy do funkcji. Aby zadziałało to prawidłowo, trzeba zasymulować to co przekazuje funkcja WebsiteCheck.

{
  “MNAM”: “TESTLUKADOEU”
}

Dzięki temu funkcja RepairSystem odszuka instancję EC2, która posiada Tag z wartością TESTLUKADOEU. Jeśli wszystko jest dobrze, funkcja podłączy się do instancji pobierze skrypt i go wykona. Funkcja jest gotowa do używania.

Wszystkie źródła, czyli zmodyfikowana funkcja WebsiteCheck, RepairSystem oraz Skrypt udostępniam na GitHub.

W repozytorium macie wszystkie niezbędne pliki:

AWS Lambda monitoring strony github resources

RepairSystem.zip to już przygotowane przez mnie cała paczka, więc możecie pominąć budowanie całej funkcji i użyć mojej. Jedynie po pobraniu trzeba zajrzeć do pliku function.py i wprowadzić swoje parametry jak nazwa s3 bucket w który wszystko jest przechowywane. Do edycji są dwie poniższe linie:

…
s3_client.download_file(‘name_of_the_bucket’,’keys/‘+sshkey,’/tmp/‘+sshkey)
…
“aws s3 cp s3://path_to_S3_Bucket/check_services.sh /home/ec2-user/check_services.sh”,
…

WebsiteCheck.py jest już w zmodyfikowanej wersji z obsługą funkcji RepairSystem. Oraz skrypt check_services, którzy trzeba umieścić na S3.

Z pozostałych kwesti to oczywiście utworzenie i zabezpieczenie bucketu S3 w którym wszystko jest przechowywane. Wrzucić klucze i skrypt. Pamiętać też trzeba, że instancje EC2 muszą mieć prawo do czytania z S3 aby móc skopiować skrypt check_services.sh.

Do rozważenia

Można zastanowić się nad modyfikacją tak, aby Lambda łączyła się do instancji z sieci prywatnej, czyli z wewnątrz VPC. Jest to wprowadzona jakiś czas temu opcja dzięki której, jeśli wrzucimy funckję do konkretnego VPC to Lambda przypisze sobie prywatny adres IP z wybranych sieci i będzie się wykonywać niejako z wnętrza VPC. Testowałem to i nawet działa, trzeba tylko zrobić trochę dodatkowej roboty.
Aby to dobrze zadziałało, trzeba po pierwsze zrobić endpoint do S3, bo inaczej Lambda nie będzie w stanie pobrać plików. Kolejną kwestią jest to, że wykorzystany przeze mnie moduł boto3 i wywołana funkcja describe_instance, której zadaniem jest pobranie danych o maszynie EC2, robi to poprzez publiczna sieć. W związku z tym musimy umożliwić naszej funkcji Lambda komunikacje z internetem poprzez NAT instancję, lub NAT Gateway. W moim przypadku nie ma to uzasadnienia, takie komplikowanie środowiska, ale wyobrażam sobie, że niektórzy np. mogą nie chcieć otwierać ssh na świat.

Zastanowić się można jeszcze czy nie dałoby się użyć funkcjonalności Commands na EC2. Można by wtedy wykluczyć trzymanie kluczy do ssh gdzieś z boku u kopiowania ich do Lambda.

Jak widać na powyższym przykładzie rozwiązania serverless nie muszą być stosowane tylko i wyłącznie do budowania webserwisów. Mogą być wykorzystywane również jako narzędzie do automatyzacji pewnych procesów czy zadań administracyjnych.

Mam do Ciebie prośbę, jeśli spodobał Ci się cały pomysł i widzisz zastosowanie dla siebie czy kogoś innego, śmiało się nim podziel. Bądź jeśli widzisz pole do optymalizacji po prostu napisz w komentarzu. Będę Ci wdzięczny.



Zbuduje swoje bezpieczne środowisko AWS.