{"id":1589,"date":"2020-08-10T10:15:50","date_gmt":"2020-08-10T08:15:50","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=1589"},"modified":"2021-03-17T15:22:30","modified_gmt":"2021-03-17T14:22:30","slug":"come-eseguire-qualsiasi-linguaggio-di-programmazione-su-aws-lambda-i-custom-runtimes","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/come-eseguire-qualsiasi-linguaggio-di-programmazione-su-aws-lambda-i-custom-runtimes\/","title":{"rendered":"Come eseguire qualsiasi linguaggio di programmazione su AWS Lambda: i Custom Runtimes."},"content":{"rendered":"\n
Le Funzioni AWS Lambda (FaaS) sono diventate rapidamente un tool estremamente versatile del cloud AWS poich\u00e9 \u00e8 possibile utilizzarle per una moltitudine di compiti: dal backend di un’applicazione Web all’ingestion di un’applicazione AWS IoT, dalla semplice automazione dell’infrastruttura all’analisi in tempo reale dei messaggi inviati tramite AWS SQS o AWS Kinesis. Inoltre, sono economiche, funzionali, scalabili e molto semplici da installare e mantenere. <\/p>\n\n\n\n
A renderle ancor pi\u00f9 attraenti agli occhi di sviluppatori e devops, \u00e8 il numero sempre crescente di runtime Lambda offerti da AWS, che consentono di scrivere il codice in svariati linguaggi di programmazione. Al momento della stesura di questo articolo AWS Lambda supporta nativamente Java, Go, PowerShell, Node.js, C #, Python e Ruby. <\/p>\n\n\n\n
Tuttavia ci sono molti altri linguaggi di programmazione che potremmo voler usare in una funzione Lambda, per esempio per migrare alle AWS Lambda Functions applicazioni gi\u00e0 esistenti ed attualmente deployate in locale o su istanze EC2. Poich\u00e9 la riscrittura di codice esistente \u00e8 spesso impossibile a causa della mancanza di tempo o della mancanza di librerie e funzionalit\u00e0, AWS ha recentemente fornito una nuova possibilit\u00e0: i Runtime personalizzati per Lambda che permettono di utilizzare qualsiasi linguaggio di programmazione!<\/p>\n\n\n\n
Una funzione Lambda con un ambiente di runtime personalizzato differisce da una normale funzione lambda perch\u00e9 contiene non solo il codice che eseguir\u00e0 quando verr\u00e0 invocata la funzione, ma anche tutte le librerie compilate necessarie per eseguire il codice e, se il linguaggio scelto \u00e8 interpretato come PHP o compilato just in time come Julia, \u00e8 necessario includere anche il binario dell’interprete. Per la maggior parte dei linguaggi di programmazione, un runtime personalizzato preparato da terze parti \u00e8 generalmente disponibile su github ed \u00e8 spesso utilizzabile direttamente oppure pu\u00f2 fornire una buona base per una soluzione personalizzata.<\/p>\n\n\n\n
Nella sezione seguente descriveremo in dettaglio come creare un runtime generico e presenteremo due esempi creati da AWS: bash e C ++. Infine, confronteremo il tempo di risposta delle lambda dei runtime personalizzati con quello di un runtime nativo (Python 3.8). La creazione di un runtime personalizzato ci d\u00e0 anche l’opportunit\u00e0 di capire come funziona davvero il servizio lambda.<\/p>\n\n\n\n
AWS Lambda \u00e8 costituito da due parti principali: il servizio Lambda<\/strong> che gestisce le richieste di esecuzione e le micro virtual machines Amazon Linux <\/strong>deployate tramite AWS Firecracker che eseguono effettivamente il codice. Una VM Firecracker viene avviata la prima volta che una determinata funzione Lambda riceve una richiesta di esecuzione (il cosiddetto “Cold Start”) e non appena la VM completa il boot, inizia a eseguire il polling del servizio Lambda per ricevere i messaggi. Quando un messaggio viene ricevuto dalla VM, essa esegue il codice della funzione handler <\/em>passandogli il messaggio JSON ricevuto nell\u2019invocazione. <\/p>\n\n\n\n Pertanto, ogni volta che il servizio Lambda riceve una richiesta di esecuzione, verifica se \u00e8 disponibile una microVM Firecracker per gestire la richiesta di esecuzione e, in tal caso, recapita il messaggio alla VM da eseguire. Al contrario, se non viene trovata nessuna VM disponibile, Firecracker avvia una nuova macchina virtuale per gestire il messaggio. <\/p>\n\n\n\n Ogni VM esegue un messaggio alla volta, quindi se molte richieste simultanee vengono inviate al servizio Lambda, ad esempio a causa di un picco di traffico ricevuto da un gateway API, verranno accese diverse nuove VM Firecracker per gestire le richieste. Per questo motivo, la latenza media delle richieste sar\u00e0 maggiore poich\u00e9 ogni VM impiega all’incirca un secondo per avviarsi (lambda Cold Start). <\/p>\n\n\n\n In una funzione lambda che utilizza un runtime nativo non \u00e8 necessario preoccuparsi di come la funzione eseguir\u00e0 il polling dei messaggi dal servizio lambda e l\u2019invio dei rapporti di esecuzione, il runtime nativo si occuper\u00e0 di tutto ci\u00f2 senza alcuna modifica necessaria da parte dello sviluppatore. Tuttavia, questo non avviene nel caso di un runtime personalizzato. Infatti, quando viene creata una funzione Lambda con un runtime personalizzato, AWS Lambda Service avvia una VM AmazonLinux di base senza librerie e pacchetti installati ad eccezione di bash e alcuni comandi unix di base (ad esempio ls, curl) . A differenza di una normale Lambda, oltre al codice e alle librerie esterne, \u00e8 necessario includere nel pacchetto di distribuzione anche uno script o un eseguibile chiamato “bootstrap” che gestir\u00e0 l’interazione tra la VM della funzione e il servizio Lambda. AWS Lambda Service espone una semplice interfaccia HTTP affinch\u00e8 i runtime possano usarla per ricevere gli eventi di invocazione e inviare l\u2019esito delle esecuzioni.<\/p>\n\n\n\n Il programma bootstrap deve perci\u00f2 implementare le seguenti funzioni:<\/p>\n\n\n\n Lo script \/ eseguibile bootstrap e altre librerie ed interpreti a livello di linguaggio (ad es. Interprete PHP) possono essere incluse in un layer lambda dedicato al fine di generare un runtime personalizzato generico e portatile che pu\u00f2 essere utilizzato con diverse funzioni Lambda.<\/p>\n\n\n\n Il modo pi\u00f9 semplice per iniziare a lavorare con i runtime personalizzati \u00e8 direttamente tramite la Console di AWS: dalla dashboard del servizio Lambda basta selezionare \u201cCrea Lambda\u201d e nella sezione runtime selezionare Runtime personalizzato con Usa bootstrap predefinito e fare clic su Crea funzione<\/p>\n\n\n\n Usando queste impostazioni predefinite il servizio Lamba creer\u00e0 una Lambda Bash di base con uno script bootstrap predefinito. Diamo un’occhiata allo script bootstrap pregenerato:<\/p>\n\n\n\n Esaminiamo rapidamente questo script: <\/p>\n\n\n\n La variabile d’ambiente _HANDLER viene valorizzata all’avvio della VM e contiene il nome del file e della funzione del nostro gestore lambda nel formato <script_name>. <bash_function_name><\/p>\n\n\n\n Va notato che in un runtime personalizzato reale sar\u00e0 anche necessario gestire errori ed eccezioni chiamando l’API per notificare il servizio lambda dell\u2019errore (\/ runtime \/ invocation \/ AwsRequestId \/ error) quando viene sollevata un’eccezione dal metodo Handler:<\/p>\n\n\n\n Passiamo ora a un esempio pi\u00f9 complesso: il calcolo delle prime n cifre di pi in Lambda usando una versione banale e inefficiente dell’algoritmo di Spigot. <\/p>\n\n\n\n L’esecuzione di calcoli complicati nelle funzioni Lambda \u00e8 spesso non banale sia per la mancanza di potenza computazionale riservata alla VM Firecracker (almeno per quelle a bassa memoria; memoria e CPU di una Vm lambda infatti scalano proporzionalmente) sia per la natura dei linguaggi dei runtime nativi, che non sono adatti a calcoli ad alte prestazioni (tranne Go). Al contrario, C++ ha una storia lunga e di successo nel calcolo ad alte prestazioni con molte librerie disponibili, dall’aritmetica di precisione arbitraria ai calcoli con matrici, dalla fluidodinamica alle collisioni di particelle.<\/p>\n\n\n\n Inoltre, questo linguaggio \u00e8 un \u201cfirst class citizen\u201d in AWS con un AWS SDK completo e un generatore di Lambda Runtimes sviluppato e gestito direttamente da AWS.<\/p>\n\n\n\n Per creare il nostro esempio, possiamo semplicemente clonare il repository git<\/a> AWS del generatore del runtime Lambda e creare la libreria usando i comandi (su unix):<\/p>\n\n\n\n Dopo di che passiamo all’esempio api-gateway nella cartella degli esempi e cambiamo il codice nel main.cpp con:<\/p>\n\n\n\n L'algoritmo di Spigot utilizzato qui \u00e8 una versione in C ++ di quello proposto qui<\/a>.<\/a><\/p>\n\n\n\n A questo punto dobbiamo compilare il \u201ccore\u201d della libreria C ++ dell\u2019AWS SDK per ottenere gli strumenti di parsing JSON usati nel codice. Per fare ci\u00f2 vi consiglio caldamente di usare questo comando (testato su Linux):<\/p>\n\n\n\n Ora si pu\u00f2 tornare alla cartella dell'esempio e compilare la app lambda usando i comandi:<\/p>\n\n\n\n A questo punto \u00e8 finalmente possibile caricare sul servizio Lambda il file zip generato dal comando build in modo da deployare finalmente la nostra applicazione Lambda (compatibile con Api Gateway) che calcola le cifre di pi greco in meno di un millisecondo.<\/p>\n\n\n\n Infine, collegando Lambda ad Api Gateway, \u00e8 possibile ottenere un endpoint in grado di calcolare il pi greco usando C ++ ed il numero di cifre pu\u00f2 essere specificato come query parameter (\u201cnumber\u201d).<\/p>\n\n\n\n Come nota finale questo script-giocattolo pu\u00f2 calcolare solo cifre di pi fino a 10 prima dell\u2019overflow dei contatori. Lasciamo a voi la sfida<\/strong> del miglioramento del codice per utilizzare GMP per l'aritmetica arbitraria al fine di ottenere Pi con un numero arbitrario di cifre. Divertitevi! \ud83d\ude42<\/p>\n\n\n\n Per concludere, in questo articolo abbiamo spiegato come funzionano i runtime Lambda Custom e abbiamo presentato due semplici esempi in bash e C ++. L'uso di runtime personalizzati aggiunge molti usi possibili per il gi\u00e0 utilissimo servizio AWS Lambda, aggiungendo la possibilit\u00e0 di eseguire calcoli rapidi utilizzando linguaggi ad alte prestazioni come C, C ++, Rust e Julia. Inoltre, i runtime personalizzati consentono anche di utilizzare Lambda per eseguire semplici script bash o anche per migrare al paradigma serverless Api PHP esistenti.<\/p>\n\n\n\nCome creare una Lambda Bash con un runtime personalizzato<\/strong><\/h2>\n\n\n\n
<\/figcaption><\/figure>\n\n\n\n
<\/figcaption><\/figure>\n\n\n\n#!\/bin\/sh\nset -euo pipefail\n\n# Handler format:
<\/figcaption><\/figure>\n\n\n\nREQUEST_ID=156cb537-e2d4-11e8-9b34-d36013741fb9\nERROR=\"{\\\"errorMessage\\\" : \\\"Error parsing event data.\\\", \\\"errorType\\\" : \\\"InvalidEventDataException\\\"}\"\ncurl -X POST \"http:\/\/${AWS_LAMBDA_RUNTIME_API}\/2018-06-01\/runtime\/invocation\/$REQUEST_ID\/error\" -d \"$ERROR\" --header \"Lambda-Runtime-Function-Error-Type: Unhandled\"\n<\/pre>\n\n\n\n
Un semplice esempio c ++: il calcolo delle prime n cifre di pi in Lambda<\/strong><\/h2>\n\n\n\n
$ git clone https:\/\/github.com\/awslabs\/aws-lambda-cpp.git\n$ cd aws-lambda-cpp\n$ mkdir build\n$ cd build\n$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~\/lambda-install\n$ make && make install<\/pre>\n\n\n\n
#include
$ mkdir ~\/install\n$ git clone https:\/\/github.com\/aws\/aws-sdk-cpp.git\n$ cd aws-sdk-cpp\n$ mkdir build\n$ cd build\n$ cmake .. -DBUILD_ONLY=\"core\" \\\n -DCMAKE_BUILD_TYPE=Release \\\n -DBUILD_SHARED_LIBS=OFF \\\n -DENABLE_UNITY_BUILD=ON \\\n -DCMAKE_INSTALL_PREFIX=~\/install \\\n -DENABLE_UNITY_BUILD=ON\n$ make\n$ make install\n<\/pre>\n\n\n\n
$ mkdir build\n$ cd build\n$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=~\/install\n$ make\n$ make aws-lambda-package-api\n<\/pre>\n\n\n\n
<\/figcaption><\/figure>\n\n\n\nLo stesso codice eseguito in Python richiede quasi 4 volte di pi\u00f9 per essere completato!<\/h2>\n\n\n\n