DevOps, Tech Talk

Jak zaoszczędzić na AWS? Poznaj LocalStack

Jak powszechnie wiadomo, korzystanie z usług (nie ze wszystkich!) chmurowego giganta z Seattle jest płatne. Wysokość opłaty uzależniona jest od ilości przetwarzanych lub przesyłanych danych, wykorzystywanej mocy obliczeniowej oraz “dostarczonej” wydajności (niezależnie od tego, czy została ona wykorzystana). Zdarzają się sytuacje, w których do przetestowania prostej funkcjonalności w kodzie musimy postawić i zapłacić za całą infrastrukturę. Jak zaoszczędzić na tym procesie?

Filip Rachwalak. Senior Systems Engineer w F-Secure. Swoją programistyczną karierę rozpoczynał w 2016 roku jako Python developer, lecz szybko jego obowiązki zaczęły oscylować bardziej wokół wdrażania kodu, automatyzacji testów oraz rozwijania infrastruktury. Krótko potem – w czasie migracji do chmury Amazona – odkrył świat AWS’a, w którym już pozostał. Dziś prowadzi zespół DevOpsów opiekującym się istniejącą infrastrukturą, wdrażaniem nowych produktów oraz ich migracją do AWS. W wolnej chwili prowadzi szkolenia (w stylu „From Zero to Hero”) również z zakresu chmury Amazona.


Jeśli chcemy zaoszczędzić i współdzielić zasoby do testów z innymi developerami lub (broń Boże) działającą produkcyjną aplikacją, prędzej czy później spotkamy się z problemem wzajemnego “wpływania” na przebieg testów. Przykładem mogą być współdzielone kolejki, z których inny developer może nam podebrać wiadomość (uruchamiając swoje testy w tym samym czasie, używając tej samej kolejki) lub obiekty przechowujące stan np. w DynamoDB lub S3.

W podobnych sytuacjach z pomocą może przyjść nam LocalStack, czyli oprogramowanie, które potrafi zasymulować chmurę AWS na naszej lokalnej maszynie, bez konieczności posiadania konta Amazonu, podłączania karty płatniczej, a nawet dostępu do Internetu (poza wcześniejszym pobraniem LocalStacka). Wystarczy wskazać naszej aplikacji nowy endpoint (localhost wskazujący na własną maszynę) do komunikacji z zasobami AWS. Od tego czasu żaden pakiet nie opuści naszego komputera, a kod będzie działał. W każdym też momencie możemy ręcznie (poprzez odpowiednio zmodyfikowane zapytania AWS CLI) sprawdzać, czy działanie skryptu/programu odniosło pożądany efekt.

Jeszcze innym – choć zapewne mniej powszechnym – problemem może być niechęć do ciągłego odświeżania tokenów do konta AWS (w zależności jak to jest skonfigurowane), lub chęć przetestowania danego fragmentu kodu bez konieczności posiadania konta w AWS. Przykład: nie mam przy sobie komputera firmowego, ale mam prywatny i wolną chwilę, w której mogę napisać skrypt mający działać na koncie produkcyjnym. Dzięki LocalStackowi przetestowanie swojego rozwiązania “na szybko” jest nie tylko możliwe, ale i bardzo łatwe – po odpowiednim jego uruchomieniu.

Działanie

LocalStack działa w Dockerze – lokalnie na naszym komputerze. Aby aplikacja lub skrypt, który chcemy testować, rozmawiała z lokalnym kontenerem zamiast Amazonem, wystarczy dodać parametr endpoint-url do polecenia, wskazując odpowiedniego hosta (localhost) oraz port (w zależności od usługi będą to różne porty). Parametr ten jest wspierany przez wszystkie oficjalne biblioteki (np. pythonowe boto3) oraz AWS CLI.

Listowanie wszystkich bucketów S3 używając AWS CLI, będzie wyglądało w następujący sposób (zakładając, że mamy skonfigurowany domyślny region i klucze dostępu):

# Odpytywanie “prawdziwego” AWS




gt; aws s3 ls # Odpytywanie LocalStacka




gt; aws s3 ls --endpoint-url http://localhost:4572

Uruchamiając LocalStacka na naszej maszynie, możemy zdefiniować, które usługi Amazona chcemy postawić. Aby oszczędzić trochę zasobów oraz przyspieszyć jego uruchomienie (na przykład przy startowaniu testów automatycznych), ograniczamy liczbę serwisów tylko do tych, które wykorzystuje nasza aplikacja. Każdy podniesiony serwis będzie miał wystawiony własny port – jego wartość można skonfigurować podczas startu kontenera albo pozostawić domyślne.

Używanie LocalStacka od strony kodu

Każdy developer chce (mam nadzieję), aby jego aplikacja była elastyczna i konfigurowalna, a z drugiej strony, żeby miała identyczny kod zarówno na produkcji, jak i w środowisku testowym. Jedyne różnice powinny być w konfiguracji.

def get_s3_client(s3_endpoint_url=None):
    if s3_endpoint_url:
        return boto3.client(‘s3’, endpoint_url=s3_endpoint_url)
    else:
        return boto3.client(‘s3’)

config = get_config_from_file_or_env_variables()
client = get_s3_client(config[‘s3_endpoint_url’])

#/bin/bash

# Read S3 endpoint URL from first argument (empty by default)
S3_ENDPOINT_URL=${1:-}

# Set AWS CLI command suffix (--endpoint-url)
[ -z $S3_ENDPOINT_URL ] || CMD_SUFFIX=”--endpoint-url=$S3_ENDPOINT_URL”

aws s3 ls $CMD_SUFFIX

Obsługiwane usługi

LocalStack nie obsługuje wszystkich usług Amazona i – z uwagi na tempo zmian i dodawania nowych serwisów – nie należy się spodziewać, że kiedykolwiek będzie. Zapewnione jest działanie wszystkich sztandarowych serwisów, jednak też nie można brać za pewnik, że każdy najdrobniejszy feature w API danej usługi będzie natychmiast uwzględniony w LocalStacku. Niemniej pokrycie funkcjonalności jest bardzo duże i większość kodu aplikacji będzie mogła zostać przetestowana.

Oto lista głównych usług, które LocalStack obsługuje:

  • API Gateway,
  • Kinesis,
  • DynamoDB,
  • S3,
  • Lambda,
  • SNS,
  • SQS,
  • Route53,
  • CloudFormation,
  • CloudWatch,
  • SSM,
  • SecretsManager,
  • IAM.

Instalacja LocalStacka

Użytkowanie oprogramowania sprowadza się do uruchomienia Dockera, w którym LocalStack działa. Można zainstalować małą aplikację, która to zrobi (pip install localstack), lub zrobić to samemu, po prostu pobierając obraz z repozytorium Dockera (docker pull localstack/localstack) i uruchamiając go. Najlepszym sposobem jednak będzie sporządzenie prostego pliku docker-compose – możemy wówczas skonfigurować całego localstacka w jednym pliku i widzimy, co zostanie uruchomione, które usługi, na jakich portach itp.

version: '2.1'
services:
...
  localstack:
    image: localstack/localstack
    ports:
      - "4567-4584:4567-4584"
      - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
    environment:
      - SERVICES=${SERVICES- }
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"

Gdy tworzymy aplikację osadzoną w Dockerze, docker-compose dla developerów, czyli stawiający testową aplikację w kontenerze tuż obok LocalStacka, mógłby wyglądać mniej więcej tak:

version: '2.1'
services:
  localstack:
    image: localstack/localstack
    ports:
      - "4567-4583"
      - "8080"
    environment:
      - SERVICES=kinesis,sqs,s3,lambda
      - DEFAULT_REGION=us-east-1
      - AWS_DEFAULT_REGION=us-east-1

  some_application:
    build: .
    depend_on:
      - localstack
    environment:
      AWS_DEFAULT_REGION: us-east-1
      AWS_ACCESS_KEY_ID: foo
      AWS_SECRET_ACCESS_KEY: bar
      S3_ENDPOINT_URL: http://localstack:4572
  	SQS_ENDPOINT_URL: http://localstack:4576
  	KINESIS_ENDPOINT_URL: http://localstack:4568

Testowanie z LocalStackiem

Przypuśćmy, że głównym zadaniem naszej aplikacji z pliku docker-compose zamieszczonego powyżej, jest sprawdzanie kolejki SQS czy przyszła jakaś wiadomość i – jeśli przyszła – kopiowanie wiadomości do S3. Wówczas możemy napisać prosty skrypt (albo po prostu testy integracyjne), który będzie umieszczał wiadomość testową (poprzez AWS CLI albo boto3) w kolejce i następnie po krótkim czasie sprawdzał, czy pojawiła się w S3. Jeśli tak, oznacza to, że aplikacja działa – do przetestowania pełnej funkcjonalności aplikacji, która w produkcyjnych realiach działa w całości w chmurze, możemy nawet odłączyć wtyczkę od Internetu i nie zapłacić ani grosza. Rysunek obrazujący przykładowy setup testujący:

Uważaj na LocalStacka!

Pomimo wielu zalet i możliwości, jakie oferuje nam LocalStack, należy być bardzo ostrożnym w jego używaniu – zdecydowanie odradzam stosowanie go jako jedyna metoda testowania swojego oprogramowania. Zdarza się, że niektóre “sieciowe” parametry zasobów AWSowych nie będą brane pod uwagę w LocalStacku. Mieliśmy przypadek, że testowaliśmy integrację naszego komponentu z Kinesisem i po pełnym przetestowaniu (póki co wyłącznie LocalStackiem) okazało się, że były dość spore problemy z wdrożeniem go do prawdziwej chmury.

Powodem był fakt, że w usłudze Kinesis należy zadeklarować ilość shardów w swoim streamie, która odpowiada maksymalnej przepustowości wpływających do strumienia danych (1MB/s na jeden shard – więcej informacji tutaj). Nasz komponent potrafił bardzo szybko wrzucać dane do strumienia, czego LocalStack nie był w stanie wychwycić, a “prawdziwy” Kinesis już tak – otrzymywaliśmy błąd zbyt dużej ilości danych wpływających do streama i cała aplikacja się wysypywała.

Być może od tego czasu zostało już to zmienione w LocalStacku, ale nie zmienia to faktu, że podobnych szczegółów na pewno jest i będzie więcej. Ideałów nie ma i ta technologia to potwierdza – nie wszystkie zmockowane serwisy będą miały pełną i najświeższą funkcjonalność, nie sposób nadążyć za tempem zmian Amazonu we własnym kodzie aplikacji, który używa tych feature’ów, a co dopiero w narzędziu, które ma imitować chmurę AWS.

Dalszy rozwój i przyszłość LocalStacka

Od dłuższego czasu LocalStack jest dynamicznie rozwijany – od początku października 2019 r. mamy dostęp do wersji Pro oraz Enterprise (płatne), w których są dodatkowe usługi (RDS, Athena, ElastiCache, Cognito i więcej), oraz wiele dodatków (Personalized Support, WEB UI do zarządzania zasobami w lokalnej chmurze, itp) – szczegóły na tej stronie.

Warto spróbować używać LocalStacka na co dzień, gdy potrzebujemy albo całej infrastruktury, od której zależy nasza aplikacja, albo tylko jej wycinka. Jeśli nawet środowisko, w którym ją testujesz nie jest zbyt kosztowne (kilka wiadomości w kolejce, kilka obiektów w S3, kilka wierszy w bazie danych), to postawienie zasobów na “bezkontowym” środowisku, bez potrzeby posiadania żadnych kluczy dostępowych ani komputera firmowego, może czasami być bezcenne.


Zdjęcie główne artykułu pochodzi z unsplash.com.

Wraz z Tomaszem Gańskim jestem współtwórcą justjoin.it - największego job boardu dla polskiej branży IT. Portal daje tym samym największy wybór spośród branżowych stron na polskim rynku. Rozwijamy go organicznie, serdecznie zapraszam tam również i Ciebie :)

Podobne artykuły

[wpdevart_facebook_comment curent_url="https://justjoin.it/blog/aws-localstack" order_type="social" width="100%" count_of_comments="8" ]