Blog

Ge din startup en flygande start med serverless

MAY 10, 2020

När startups väljer tech stack söker de kostnadseffektiva och skalbara lösningar. Serverless kan vara ett utmärkt alternativ, men hur kommer du igång?

Severi Haverila

Severi works as a DevOps consultant by day, and runs a startup company at night. His main interests are working with state-of-the-art technologies and solving problems for the customers in a pragmatic way.

Vad är det startups i ett tidigt skede inte har? Just det, servrar! Ursäkta, jag menade pengar, och därför bör de glömma servrar och få fart på verksamheten med serverless. Och med servrar menar jag inte bara era föråldrade serverrack eller EC2-instanser som kör er LAMP-stack, utan även era tjusiga GKE Kubernetes-kluster. Betala gärna de extra 50 dollar det kostar att köra ett minimalt Kubernetes-kluster som bara hanterar er frontend och några NodeJS-mikrotjänster som svarar på ganska sällsynt trafik, om ni verkligen vill. Men varför göra det när ni kan göra allt ovanstående till en bråkdel av kostnaden och samtidigt minska underhållsarbetet?

I det här blogginlägget visar jag hur du kommer i gång med allt detta på 10 minuter. Mer specifikt fokuserar vi på att få dig och övriga utvecklare att koda tillsammans på er världsförändrande produkt. När du har läst klart har du ett repository med ett serverless API som körs på AWS och en React-frontend som använder API:et.

Serverless Framework

Vi använder Serverless Framework för att skapa vårt serverless API på AWS. Serverless Framework är inte begränsat till AWS, utan har stöd för alla stora molnleverantörer och leverantörsoberoende tekniker som Kubeless.

När du arbetar med AWS använder Serverless Framework CloudFormation i bakgrunden för att sätta upp vår serverless-stack. Med Serverless Framework är det enkelt att lägga till egna CloudFormation-resurser i vår serverless-deployment. Vi kan till exempel skapa en CloudFormation-mall som skapar en DynamoDB-tabell som våra lambdas kan använda, och driftsätta den tillsammans med våra lambdas genom att bara köra kommandot ”serverless deploy”.

Nog pratat – nu kör vi i gång lambdas!

Kom i gång

Projektet är konfigurerat så att det är enkelt att samarbeta med andra. I praktiken innebär det att du enkelt kan skapa egna kopior av det serverless API:et, utveckla nya funktioner mot det och automatiskt konfigurera frontend så att den använder ditt eget serverless REST API.

Klona detta repository: https://github.com/severi/serverless-react-getting-started

och checka ut taggen step1

I repot har jag lagt in ett grundläggande boilerplate-projekt från create-react-app för frontend (mappen frontend) och ett enkelt exempel med Serverless Framework för backend (mappen api).

Frontend-delen skapades genom att köra (läs mer):

Köra frontend

Först kontrollerar vi att frontend kan köras korrekt. Kör följande kommandon:

Och vi är i gång! Nu när vår frontend körs utan problem kan du stoppa den, om du inte vill beundra den fantastiska startsidan från create-react-app en liten stund till. Vi fokuserar på serverless-delen och återvänder till frontend senare.

Få i gång vårt serverless API

Nu behöver vi rätt verktyg! Installera CLI:t för Serverless Framework

Vi behöver konfigurera våra AWS-uppgifter och skapa ett AWS-konto om du inte redan har ett. Som tur är har serverless.com utförliga instruktioner för detta, så vi behöver inte kopiera in dem här. Följ bara den här länken och kom tillbaka när du är redo.

Driftsätta funktionen

Kör sls deploy från api-mappen för att få i gång vår funktion på AWS. Resultatet bör se ut så här:

Nu när vår funktion körs på AWS kan vi försöka anropa den. Vi använder curl för det. Funktionens URL finns i resultatet från kommandot sls deploy. Titta i avsnittet endpoints:. Som vi kan se körs den verkligen:

Förklaring av vad som händer

API-strukturen är enkel:

De intressanta delarna här är filerna serverless.yml och functions/myFunction.js. I följande avsnitt beskriver vi dem båda i detalj.

serverless.yml

Vår serverless.yml består av tre avsnitt:

  1. service – definierar namnet på vår serverless-stack

  2. provider – AWS-relaterade konfigurationer

  3. functions – våra serverless-funktioner som vi vill driftsätta

Vi driftsätter en funktion som heter myFunction. Handler-avsnittet anger var koden för den specifika funktionen finns och motsvarar ”funktionen main i filen functions/myFunction.js”.

functions/myFunction.js

Funktionen vi driftsätter är enkel. Den returnerar bara ett { hello: "world" }-JSON-objekt.

I underavsnittet events anger vi dessutom att myFunction ska svara på HTTP GET-förfrågningar till följande sökväg: /myfunction.

Som en påminnelse: så här testar vi vår funktion. Observera hur URL:en slutar:

I provider stage definierar vi också standardmiljön som ”dev”.

Detta kan skrivas över genom att lägga till flaggan  --stage stagename  i kommandot sls deploy.

Miljöer är användbara för att skapa separata utvecklings- eller testmiljöer. Se fullständig referens för serverless.yml.

AWS

Så, vad händer på AWS-sidan? Serverless Framework använder Cloudformation i bakgrunden för att driftsätta vår serverless-stack. För att verifiera detta kan du öppna Cloudformation-avsnittet i AWS Web UI: https://eu-north-1.console.aws.amazon.com/cloudformation/home

Ser du hur serverless skapar en Cloudformation-stack utifrån de service- och stage-namn vi har angett?

Om du går till fliken Resources i vår nyss skapade Cloudformation-stack kan du se vilka AWS-resurser som skapades under driftsättningen.

serverless-api-dev screen shot

Dessa resurser omfattar:

  • ServerlessDeploymentBucket

  • API Gateway

  • IAM Role

  • MyFunctionLambdaFunction

  • S3 Bucket som vår lambda-kod kopieras till

  • det är detta som https://XXXXXXXX.execute-api.eu-north-1.amazonaws.com/-delen av vår lambda-URL syftar på

  • Gör det möjligt för vår lambda-funktion att skriva sina loggar till Cloudwatch

  • Själva lambda-funktionen

Enhetlig kodstil

En av fördelarna med att använda Node.js är att vi kan använda samma programmeringsspråk och bibliotek för både frontend och backend i vår applikation. Vår frontend använder redan de senaste ES6-funktionerna i Javascript. För att kunna använda samma funktioner i backend-tjänsterna och få en enhetlig kodstil mellan frontend och backend-funktionerna behöver vi lägga till vårt första plugin i serverless.yml: serverless-bundle. Du hittar mer information om skillnaderna som ES6 innebär för Javascript-syntaxen här.

Som skaparna av serverless-bundle rekommenderar i sin egen serverless-guide kommer vi också att aktivera individuell paketering för våra funktioner.

Genom att aktivera individuell paketering tillsammans med pluginet serverless-bundle kan vi skapa separata, optimerade paket för var och en av våra funktioner – okej, just nu finns det bara en, men i en verklig applikation blir det många.

Nu kan vi konvertera myFunction.js så att den använder ES6-syntax. Eftersom vår exempelfunktion är ganska enkel är skillnaden mellan ES5- och ES6-implementationen begränsad till hur funktionen deklareras, men i en större kodbas skulle detta naturligtvis få större påverkan.

Låt oss driftsätta vår funktion igen:

Serverless-bundle aktiverar bland annat även eslint för vårt serverless-projekt. Du kan testa detta genom att ta bort det avslutande semikolonet från vår funktion och sedan försöka driftsätta den igen. Du bör se något i stil med detta:

Vad är det bästa sättet att åtgärda de här problemen? Vi kan förstås rätta dem manuellt, men på sikt kan det bli tidskrävande. Om möjligt vill vi definitivt åtgärda dem automatiskt.

Tyvärr har jag inte hittat något sätt att:

  1. visa linting-felen i Visual Studio Code

  2. köra eslint med flaggan --fix direkt med eslint-konfigurationerna som serverless-bundle lägger till.

Som en workaround har jag gjort följande:

och lagt till följande script i API:ets package.json

Detta åsidosätter eslint-konfigurationerna med samma konfiguration som pluginet använder. Eftersom vi har filen .eslintrc.json i roten av API-mappen fungerar linting därför både via CLI och VSCode.

Vi kan nu automatiskt åtgärda de linting-problem som kan fixas automatiskt genom att köra:

I praktiken är det rimligt att standardisera din linting så att både frontend-koden och serverless-koden lintas med samma inställningar. På så sätt får du en enhetlig kodstil i alla dina Javascript-filer.

För en komplett lösning för det här steget, se taggen step2

Anropa serverless-funktionen från vår webbplats

Låt oss redigera filen `App.js` i vår frontend och lägga till kod som anropar vår lambda.

Detta räcker inte i sig, eftersom vi får ett CORS-fel – klicka på knappen, öppna konsolen i din webbläsare så ser du felmeddelandet. CORS står för ”Cross-Origin Resource Sharing” och gör det möjligt att göra förfrågningar till andra domäner än den där din webbplats körs. Du kan läsa mer om CORS här.

Om vi tittar på referensen för serverless.yml på https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/ ser vi att vi kan lägga till följande alternativ i vår funktion:

Som kommentarerna anger behöver vi även ändra vårt svar. Genom att lägga till denna header kan vi göra cross-origin-förfrågningar från vår fantastiska webbapplikation under utvecklingen, det vill säga från http://localhost:3000.

Driftsätt funktionen igen. Nu bör ett klick på knappen skriva ut svaret korrekt i webbläsarens konsol.

Mindre hårdkodning, mer samarbete

Att hårdkoda API-URL:en i vår frontend-kod är inte optimalt. I produktionsbygget av din applikation vill du rikta dig mot produktionsversionen av ditt API. Under utvecklingen bör du och dina kollegor använda era egna versioner.

För att möjliggöra detta flyttar vi den hårdkodade URL:en från koden till en miljövariabel genom att ändra raden med fetch i vår App.js till följande:

Som några av er kanske har märkt kommer vi att använda funktionen för anpassade miljövariabler som create-react-app erbjuder för att bädda in vår tjänsteendpoint som en miljövariabel vid build-tillfället (mer information).

Nu definierar vi variabeln REACT_APP_ServiceEndpoint. För detta använder vi filen .env.development.local (mer information om olika .env-filer och deras prioritet finns här).

Vi kan skapa filen manuellt och lägga till följande rad i den:

Det fungerar bra med bara en utvecklare och en miljövariabel, men vad händer om vi senare behöver mer information om vår AWS-stack i frontend? Du kanske vill låta användare ladda upp filer direkt till S3 från sin webbläsare utan att anropa några lambdas. Hur kommunicerar vi dessutom dessa manuella steg till andra utvecklare så att de kan konfigurera sina egna utvecklingsmiljöer?

Som tur är kan skapandet av .env-filen automatiseras.

För att automatisera detta använder vi en anpassad fork av ett plugin som heter "Serverless Stack Output Plugin". Enligt pluginets README gör det följande:

lagrar output från din AWS CloudFormation-stack i JSON-/YAML-/TOML-filer eller skickar outputen till en JavaScript-funktion för vidare bearbetning.

Med vår fork av pluginet kan vi automatiskt generera miljöfiler för create-react-app under driftsättningssteget för vår serverless-stack.

Installera vår anpassade version av serverless-stack-pluginet genom att köra:

och ändra vår serverless.yml så här:

Om vi driftsätter API:et igen genom att köra sls deploy ser du att vi även får en fil som heter .stack.dev.react_app. Kopiera den till frontend-katalogen och byt namn på den:

och sedan är vi klara!

Starta om frontend och kontrollera att hello-world-objektet fortfarande skrivs ut i konsolen. Med detta tillvägagångssätt kan en enskild utvecklare enkelt skapa en ny serverless-miljö genom att ange ett anpassat stage vid driftsättning (t.ex. sls deploy --stage myenv), använda den genererade filen för att definiera miljövariablerna för frontend och koppla den till den nya serverless-backenden. Detta är inte begränsat till serverless-funktionerna och deras API Gateway-URL, utan gäller även andra AWS-resurser som skapas med serverless-frameworket.

För produktionsbygget kan miljövariablerna placeras i .env.production och lagras i versionshantering.

För en komplett lösning för detta steg, se taggen step3:

Kör följande för att ta bort serverless-stacken från AWS:

Om du vill vara extra noggrann kan du kontrollera CloudFormation-sidan i AWS-konsolen och verifiera att alla stackar som skapats av serverless-frameworket har tagits bort.

Slutsats

Att komma igång med serverless behöver inte vara svårt. Det är fortfarande ett nytt område, och därför utvecklas tekniken och de beprövade metoderna kontinuerligt. Jag hoppas att den här guiden har gjort din resa med serverless lite mer lättförståelig och att du nu kan starta nya serverless-projekt och utveckla dem tillsammans med dina kollegor.

Teknisk skuld är en verklighet i alla företag, och även om det inte är hela världen bör beslutet att ta på sig den alltid vara medvetet. Att ha din infrastruktur som kod och automatisera provisioneringen bör stå högt upp på din att-göra-lista redan från början. Som tur är hjälper serverless-frameworket tillsammans med CloudFormation oss att uppnå detta.

Serverless är ett bra verktyg i en utvecklares verktygskedja, men serverless är inte alltid den mest kostnadseffektiva lösningen (mer information finns i den här bloggen och den här bloggen om att använda serverless). När det används för rätt ändamål kan det dock hjälpa oss att skapa kostnadseffektiva tjänster som skalar automatiskt. Och som du har sett i det här blogginlägget är det ganska enkelt att komma igång.

Om du vill lära dig mer om ämnet finns här en bra guide att börja med. Kanske tar vi också upp andra aspekter av serverless-utveckling i framtida blogginlägg. Men tills dess är det dags att sluta slösa pengar på underutnyttjade servrar och påbörja din serverless-resa!

  • Moln

Subscribe to our newsletter