Nasazování AWS Lambda funkcí pomocí Lambda funkcí

Sepsáno 09.04.2016

Rád bych ukázal vlastnost, kterou mám na cloudu v AWS snad ze všech nejradši. Každá jejich služba je jednoduchý, na jednu jedinou věc zaměřený nástroj. Všechny se pak vzájemně doplňují a mají vestavěnou podporu pro spojení se s ostatními. Místo toho, aby RDS a EC2 měly každá po svém implementovaný monitoring serverů, posílají data do CloudWatch, který má jediný úkol: schovávát a zobrazovat metriky. Zručný vývojář si pak jednotlivé dílky poskládá do složitého celku podle svých představ.

V brašně vývojáře pak je nástroj pro téměr všechno potřebné: servery, fronty i pub-suby, databáze, keše, logování, hledání, machine learning, hosting statického obsahu, CDN, proxy atd. atd. Už je pak jen na fantazii jak to zkombinovat.

Minule jsem psal o Lambdách a dostal jsem dotaz jaký používáme tooling pro správu funkcí. Nemáme žádný framework jako apex nebo serverless, ale spíchli jsme dohromady několik AWS služeb a vytvořili jsme si vlastní nástroj pro buildování a nasazování.

Náš klasický způsob nasazování

foodpanda běží zhruba ve třicetí zemích světa a jednotlivé země máme rozmístěné po různých regionech. V některých AWS regionech pak máme z vlastnických důvodů dokonce víc logických business regionů, které jsou kompletně oddělené.

Podle toho vypadá náš klasický postup pro nasazení aplikací. Na Travisu se zbuilduje tag a výsledný artefakt se nahraje na Github releases nebo do S3. Jeden po druhým pak vývojář oběhne regiony a spustí deployment skript, který artefakt stáhne a udělá potřebný zbytek pro nahrazení původní verze novou.

S Lambda funkcema jsme si chtěli trochu ulechčit práci. Výsledkem byl jeden krok. Vytvořím tag v gitu a pushnu na github. Konec.

Od pushe stačí počkat asi minutu a novou Lambdu mám k dispozici ve všech našich regionech pod danou verzí. Jak jsem zmínil v posledním článku, s Lambdou můžu mít libovolný počet verzí nasazených současně, nová verze nijak neovlivňuje už běžící kód. Aplikace, které ty funkce používají pak můžu postupně přesouvat na nově nasazenou Lambda funkci.

Čáry máry fuk - kompletní automatizace

Základem automatiky je Lambda funkce, kostra je zhruba následující:
  1. jejím vstupní argumentem je odkaz na souboru v S3
  2. podívá se, jestli je to typ aplikace, co umí nasadit
  3. pomocí AWS SDK projde všechny regiony a podívá se kam je potřeba nahrát kód
  4. opět s pomocí AWS SDK vytvoří novou Lambda funkci a jako kód určí odkaz na S3, který přišel na vstupu
Jak ale funkci vyvolat? Popis Lambda v konzoli zní: "Code reacting to events". Teď příjde na řadu kombinování služeb, co jsem zmínil na začátku:
  1. nahrání souboru na S3 z Travisu vyvolá událost s3:ObjectCreated
  2. tu lze připojit k topicu na SNS, kterému budeme řikat NewArtifactTopic
  3. na SNS topic lze připojit jednu nebo více Lambda funkcí, tou už bude ta naše funkce popsaná výše
Skládání Amazoních služb jako puzzle

Infrastruktura v kódu

Jednou z mých nejoblíbenějších služeb je CloudFormation. Pomocí JSON šablon kompletně popíšete služby co chcete vytvořit a můžete je svázat dohormady. Podporuje to aktualizace stacku, odstraňování dílčích částí, rollbacky i přejmenování. Dá se na to dělat code review jak na aplikační kód. Vrhnem se na to:
"ArtifactsBucket" : {
 "Type" : "AWS::S3::Bucket",
 "Properties" : {
   "NotificationConfiguration" : {
     "TopicConfigurations" : [ {
       "Event" : "s3:ObjectCreated:*",
       "Topic" : { "Ref": "NewArtifactTopic" }
     } ]
   }
 }
}
Tímhle jsme vytvořili náš repozitář artefaktů, obyčejný S3 bucket, kam budem nahrávat zazipované archivy. Rovnou jsem mu přidal odkaz na SNS topic, který upozorní pokud přijde nový souboru, vytvořme ho tedy:
"NewArtifactTopic": {
  "Type" : "AWS::SNS::Topic",
  "Properties" : {
    "DisplayName" : "New artifact uploaded to bucket",
    "Subscription" : [
      {
        "Endpoint": { "Fn::GetAtt" : [ "CreateLambdaFunctionOnNewArtifact", "Arn" ] },
        "Protocol": "lambda"
      },
      ... tady můžu přidat víc než jednu funkci ... 
    ],
    "TopicName" : "NewArtifactTopic"
  }
}
V Properties.Subscription.Endpoint je zmíněná Lambda, která bude vytvářet nové Lambda funkce:
"CreateLambdaFunctionOnNewArtifact" : {
 "Type" : "AWS::Lambda::Function",
 "Properties" : {
   "Code" : {
     "S3Bucket" : "s3 bucket s funkcí",
     "S3Key" : { "Fn::Join" : [ "/", [
       "%TEMPLATE-VERSION%",
       "createLambda.zip"
     ] ] }
   },
   "Description" : "Creates a new Lambda function when an artifact is uploaded to S3",
   "Handler" : "createLambda.handler",
   "MemorySize" : "128",
   "Role" : { "Fn::GetAtt" : [ "LambdaLambdaCreator", "Arn" ] },
   "Runtime" : "nodejs",
   "Timeout" : "60"
 }
}
Jak je vidět v Properties.Role, vynechal jsem jednu důležitou věc: oprávnění. Je to opět super vlastnost CloudFormation, lze nastavit jen ty úplně nejnutnější IAM (univerzální AWS služba, která spravuje oprávnění pro všechny Amazoní služby bez výjimky) pravidla pro vytvořené služby. Budeme potřebovat následující pravidla:
  1. NewArtifactTopicPolicy bude IAM policy, která dá S3 bucketu oprávnění sns:Publish pro NewArtifactTopic.
  2. InvokeLambdaCreationLambdaBySnsPermission dá SNS oprávnění spustit funkci, která je na ten topic přihlášená (SNS topic nebude smět spustit žádnou jinou funkci)
  3. LambdaLambdaCreator je IAM role, která je přiřazená k nasazovací funkci. Umožní ji ukládát logy a procházet artefakty z S3 bucketu. Dostane iam:PassRole aby mohla nové funkci přidělit jeji vlastni roli, dále dostane ec2:DescribeRegions aby mohla projít všechny dostupné regiony a nakonec lambda:CreateFunction aby mohla vůbec novou funkci vytvořit.
  4. V neposlední řadě jsme taky vytvořili v CloudFormation šabloně uživatele s klíčema pro Travis, který má pouze oprávnění nahrávat nové artefakty.

Nenasazujeme tím jenom Lambda funkce

Výsledky jsou na screenshotu, uvidíte dvě automaticky vytvořené funkce (začínající na logistics-matching) a funkci, která je vytvořila (global-CreateMatchingLambdaFunction...). Naše drobné dílo

Na obrázku je ještě jedna Lambda funkce. Jak už jsem zmínil dříve v článku, k SNS topicu lze přihlásit více než jednu funkci.

My do stejného bucketu nahráváme i zdrojové kódy pro ElasticBeanstalk aplikace. Ty rozpozná jiná Lambda a vytvoří novou dostupnou verzi v administraci EB. Tu už na rozdíl od Lambdy je nutné nasadit ručně a nahrazuje předchozí běžící verzi. Stejně ale jako u Lambdy nám stačí pouze push z lokální konzole a novou verzi k nasazení máme dispozici v každém našem regionu.

Příjemným bonusem je, že celá tahle hříčka téměř nic nestojí. U S3 bucketu platíme pár centů za uložený prostor pro artefakty, SNS je v takhle miniaturním objemu zadarmo a Lamda funkce stojí opět jen pár centů za měsíc.

V dalším článku ukážu další drobnost co máme v Lambdě, vlastní checky pro Github pull requesty, tentokrát s úplným kódem funkce i CloudFormation stacku.

comments powered by Disqus