Jak zbudować wtykową aplikację Golang i korzystać z AWS Lambda Layers.

Golang - dlaczego warto na to zwrócić uwagę?

Golang to język programowania open source zaprojektowany i wdrożony przez Google. Jest bardzo szeroko stosowany w nowoczesnych aplikacjach, szczególnie w chmurze. Najbardziej charakterystyczne cechy to:

  • Golang jest wpisywany statycznie - zapewnia mniejszą elastyczność, ale chroni cię przed popełnianiem błędów,
  • Nie jest zorientowany obiektowo. Można jednak tworzyć struktury i interfejsy, co daje 3 z 4 zasad OOP: abstrakcja danych, enkapsulacja i polimorfizm. Brakuje tylko dziedzictwa,
  • Goroutines! - największa implementacja lekkich nici, z których kiedykolwiek korzystałem. Pozwala ci stworzyć nowy wątek w bardzo łatwy sposób za pomocą operatora go i komunikować się między różnymi goroutinami za pomocą kanałów,
  • Kompiluje się do pojedynczego pliku binarnego ze wszystkimi zależnościami - koniec konfliktów pakietów!

Osobiście uważam Golang za najlepszy język, którego używam na co dzień. Jednak ten artykuł nie będzie dotyczył tworzenia pierwszej funkcji ani drukowania „Hello World”. Pokażę Ci trochę bardziej zaawansowane rzeczy. Jeśli jesteś początkującym i chcesz dowiedzieć się więcej o Golang, odwiedź jego stronę główną.

AWS Lambda i Golang

AWS Lambda to jedna z najpopularniejszych usług obliczeniowych bez serwerów w chmurze publicznej, wydana w listopadzie 2014 r. Przez Amazon Web Services. Pozwala na uruchamianie kodu w odpowiedzi na zdarzenia takie jak wyzwalacze DynamoDB, SNS lub HTTP bez obsługi administracyjnej lub zarządzania serwerami! Czy wiesz, co jest naprawdę świetne? Od stycznia 2018 r. Obsługuje środowisko uruchomieniowe Golang. Praca z AWS Lambda jest naprawdę prosta - wystarczy załadować spakowany pakiet z kodem i wszystkimi zależnościami (pojedynczy plik binarny podczas korzystania z Golanga).

Szybkie przewijanie do przodu, 4 lata później w 2018 r. Odp .: Invent AWS wydaje Lambda Layers, który pozwala przechowywać i zarządzać danymi, które są współużytkowane przez różne funkcje na jednym lub wielu kontach AWS! Na przykład, używając Pythona, możesz umieścić wszystkie zależności w dodatkowej warstwie, z której później mogą korzystać inne Lambdy. Nie ma już potrzeby umieszczania różnych zależności w każdym spakowanym pakiecie! W świecie Golang sytuacja jest inna, ponieważ AWS Lambda wymaga przesłania skompilowanego pliku binarnego. Jak możemy skorzystać z AWS Lambda Layers? Odpowiedź jest prosta - zbuduj modułową aplikację za pomocą wtyczek Golang!

Wtyczki Golang - sposób na zbudowanie aplikacji modułowej

Wtyczki Golang to funkcja wydana w wersji Go1.8, która umożliwia dynamiczne ładowanie bibliotek współdzielonych (plików .so). Daje to możliwość wyeksportowania części kodu do osobnej biblioteki lub skorzystania z wtyczki przygotowanej i skompilowanej przez kogoś innego. Obiecujące jest jednak kilka ograniczeń:

  • Twoja wtyczka musi być pojedynczym modułem głównym,
  • Możesz załadować tylko funkcje i zmienne, które są eksportowane jako symbole ELF,
  • Ze względu na pisanie statyczne musisz rzutować każdy załadowany symbol na poprawny typ. W najgorszym scenariuszu musisz zdefiniować poprawny interfejs w kodzie,
  • Działa tylko w systemach Linux i MacOS. Osobiście nie uważam tego za wadę :)

Budowanie i testowanie pierwszej wtyczki

Teraz stwórzmy naszą pierwszą wtyczkę. Jako przykład stworzymy prosty moduł do szyfrowania łańcucha. Wróćmy do podstaw i zaimplementuj 2 proste algorytmy szyfrowania - Ceasar i Verman.

  • Szyfr Cezara to algorytm zastosowany po raz pierwszy przez Juliusa Ceasesa. Przesuwa każdą literę w tekście o ustaloną liczbę pozycji. Na przykład, jeśli chcesz zaszyfrować słowo golang kluczem 4, otrzymasz ktpek. Deszyfrowanie działa w ten sam sposób. Wystarczy przesunąć litery w przeciwnym kierunku.
  • Szyfr Vermana jest podobny do Ceasera, oparty na tej samej idei przesunięcia, z tą różnicą, że przesuwasz każdą literę o inną liczbę pozycji. Aby odszyfrować tekst, musisz mieć klucz zawierający pozycje używane do szyfrowania tekstu. Na przykład, jeśli chcesz zaszyfrować słowo golang za pomocą klucza [-1, 4, 7, 20, 4, -2], otrzymasz przyszłość.

Pełna implementacja tego przykładu jest dostępna tutaj.

Implementacja wtyczki

Poniższy fragment kodu zawiera implementację dwóch algorytmów wymienionych powyżej. Dla każdego wdrażamy 2 metody szyfrowania i deszyfrowania naszego tekstu:

Jak widać, wyeksportowaliśmy tutaj 3 różne symbole (Golang eksportuje tylko te identyfikatory, które zaczynają się od dużej litery):

  • EncryptCeasar - ciąg func (int, string), który szyfruje tekst za pomocą algorytmu Ceasar,
  • DecryptCeaser - ciąg func (int, string), który odszyfrowuje tekst za pomocą algorytmu Caeser,
  • VermanCipher - zmienna typu vermanCipher implementująca 2 metody: Szyfruj: ciąg func (ciąg) i Odszyfruj: ciąg func () (* ciąg, błąd)

Aby skompilować tę wtyczkę, musisz uruchomić następujące polecenie:

go build -buildmode = plugin -o plugin / cipher.so plugin / cipher.go

Na razie nie ma nic specjalnego - utworzono kilka prostych funkcji, a moduł skompilowano jako wtyczkę, dodając argument -buildmode = plugin.

Załaduj i przetestuj wtyczkę

Zabawa zaczyna się, gdy chcemy użyć skompilowanej wtyczki w naszej aplikacji. Stwórzmy prosty przykład:

Najpierw musisz zaimportować pakiet wtyczek golang. Zawiera tylko dwie funkcje - pierwsza służy do ładowania biblioteki współdzielonej, a druga do znajdowania wyeksportowanego symbolu. Aby załadować bibliotekę, musisz użyć funkcji Open, która wymaga podania ścieżki do udostępnionej wtyczki i zwraca zmienną typu Plugin. Jeśli załadowanie biblioteki nie jest możliwe (np. Niepoprawna ścieżka lub uszkodzony plik), funkcja zwraca błąd, który należy obsłużyć.

Następnym krokiem jest załadowanie każdego wyeksportowanego symbolu przy użyciu metody odnośnika. Niewielką niedogodnością jest to, że musisz załadować każdą wyeksportowaną funkcję osobno. Można jednak łączyć wiele funkcji razem w taki sam sposób, jak w przypadku symbolu VermanCipher. Po załadowaniu wszystkich symboli, których chcesz użyć, musisz rzucić je na właściwy typ. Golang to język pisany statycznie, więc nie ma innego sposobu na użycie tych symboli bez rzucania. Pamiętaj, że kiedy eksportujesz zmienną, która implementuje kilka metod, musisz rzutować ją na poprawny typ interfejsu (musiałem zdefiniować interfejs szyfrowania, aby to obsłużyć). \ Newline \ newline

Aby skompilować i uruchomić aplikację, użyj następującego polecenia:

przejdź na kompilację app.go
./app

Na wyjściu powinieneś zobaczyć zaszyfrowany i odszyfrowany tekst jako dowód, że algorytm działa poprawnie.

Użyj wtyczki w AWS lambda

Aby korzystać z naszej wtyczki w AWS Lambda, musimy wprowadzić kilka modyfikacji w naszej aplikacji:

  • AWS Lambda montuje warstwy do katalogu / opt w kontenerze lambda, więc musimy załadować naszą wtyczkę z tego katalogu.
  • Musimy stworzyć funkcję obsługi, która będzie używana przez silnik Lambda do obsługi naszego zdarzenia testowego.

Poniższy fragment zawiera naszą aplikację dostosowaną do użycia przez Lambda:

Jak widać implementacja jest bardzo podobna do poprzedniej. Zmieniliśmy tylko katalog, z którego załadowaliśmy naszą wtyczkę i dodaliśmy odpowiedź funkcji zamiast drukowania wartości. Jeśli chcesz dowiedzieć się więcej o pisaniu Lambdas w golangu, sprawdź dokumentację AWS.

Wdrożenie AWS Lambda

Istnieją dwa sposoby wdrażania funkcji i warstw AWS Lambda. Możesz utworzyć i przesłać spakowany pakiet ręcznie lub użyć bardziej zaawansowanego frameworka, co znacznie ułatwia i przyspiesza. W większości moich projektów korzystam ze środowiska Serverless, więc przygotowałem już prosty plik konfiguracyjny serverless.yml przy użyciu tego narzędzia:

service: cipherService
frameworkVersion: "> = 1.28.0 <2.0.0"
dostawca:
  nazwa: aws
  środowisko wykonawcze: go1.x
warstwy:
  cipherLayer:
    ścieżka: bin / wtyczka
    Kompatybilne środowiska wykonawcze:
      - go1.x
Funkcje:
  silnik:
    handler: bin / cipherEngine
    pakiet:
      wykluczać:
        - ./**
      zawierać:
        - ./bin/cipherEngine
    warstwy:
      - {Ref: CipherLayerLambdaLayer}

W sekcji warstw zdefiniowaliśmy pojedynczą warstwę ze ścieżką do już utworzonej wtyczki - zostanie ona wdrożona wraz z funkcją lambda. Możesz zdefiniować do 5 różnych warstw, których kolejność jest naprawdę ważna. Są one zamontowane w tym samym katalogu / opt, więc warstwy o większej liczbie mogą przesłonić pliki z poprzednio zamontowanych warstw. Dla każdej warstwy musisz podać co najmniej 2 parametry: ścieżkę do katalogu zawierającego źródło warstwy (ścieżkę do pliku binarnego wtyczki w twoim przypadku) oraz listę kompatybilnych środowisk uruchomieniowych.

Kolejna sekcja funkcji to miejsce, w którym określasz listę funkcji do wdrożenia. Dla każdej funkcji musisz podać przynajmniej ścieżkę do skompilowanej aplikacji. Ponadto musimy zdefiniować parametr layer w odniesieniu do zdefiniowanej powyżej warstwy. Spowoduje to automatyczne dołączenie warstwy do naszej funkcji Lambda podczas wdrażania. Zabawne jest to, że musisz przekonwertować nazwę warstwy lambda na TitleCased i dodać sufiks LambdaLayer, jeśli chcesz odwoływać się do tego zasobu. Wygląda na to, że zespół Serverless wdrożył go w ten sposób, aby rozwiązać konflikt w odniesieniu do różnego rodzaju zasobów.

Gdy nasz plik konfiguracyjny serverless.yml jest gotowy, ostatnią rzeczą do zrobienia jest skompilowanie naszej aplikacji, wtyczka i wdrożenie. Możemy do tego użyć prostego pliku Makefile:

.PHONY: build buildPlugin clean deploy
budować:
 dep zapewniają -v
 env GOOS = linux go build -ldflags = "- s -w" -o bin / cipherEngine cipherEngine / main.go
buildPlugin:
 env GOOS = linux go build -ldflags = "- s -w" -buildmode = wtyczka -o bin / plugin / cipher.so ../plugin/cipher.go
czysty:
 rm -rf ./bin ./vendor Gopkg.lock
Wdróż: czysta kompilacja BuildPlugin
 sls wdrożyć --verbose

Możesz zbudować i wdrożyć swoją funkcję, uruchamiając następujące polecenie:

dokonać wdrożenia

Przetestuj AWS Lambda

Jak wspomniałem wcześniej, AWS Lambda wykonuje kod w odpowiedzi na zdarzenie. Nie skonfigurowaliśmy jednak żadnych wyzwalaczy zdarzeń, więc nie będziemy go wywoływać bez naszej pomocy. Musimy to zrobić ręcznie za pomocą frameworka Serverless lub narzędzia awscli:

sls wywołuje -f nazwa_funkcji
aws lambda invoke - nazwa-funkcji nazwa_funkcji plik_wyjściowy

W odpowiedzi powinieneś zobaczyć to samo wyjście, co poprzednio, co dowodzi, że nasza funkcja lambda działa poprawnie i ładuje wtyczkę z dodatkowej warstwy. Teraz możesz tworzyć inne funkcje, które będą korzystać z tej samej warstwy lub nawet udostępniać ją innym kontom AWS.

Podsumowanie

Korzystanie z modułów Golang i testowanie ich integracji z nowo wydanymi warstwami AWS Lambda było bardzo przyjemne. Biblioteka wtyczek jest naprawdę niesamowita, jednak ze względu na swoje ograniczenia i specyfikację Golang można jej używać tylko w niektórych specjalnych scenariuszach. Myślę, że dla większości programistów, którzy pracują nad standardowymi projektami, korzystanie z wtyczek nie będzie potrzebne ani nawet możliwe. Przychodzą mi na myśl tylko dwa powody:

  • Implementowanie skomplikowanych algorytmów, z których mogą korzystać inne aplikacje np. algorytmy kodowania lub szyfrowania wideo.
  • Udostępnianie algorytmu innym osobom bez publikowania jego kodu.