{"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

Come funziona Lambda?<\/strong><\/h2>\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

  1. Ottieni un evento<\/strong>: Invocare l’API di invocazione per ottenere l’evento successivo. Il corpo della risposta contiene i dati dell’evento. Le intestazioni di risposta contengono l’ID richiesta e altre informazioni.<\/li><\/ol>\n\n\n\n
    1. Propagare<\/strong> l\u2019header di xray<\/strong>: Ottenere l’header della trace di X-Ray dall’header Lambda-Runtime-Trace-Id nella risposta API. Impostare la variabile di ambiente _X_AMZN_TRACE_ID localmente con lo stesso valore. X-Ray SDK utilizza questo valore per connettere i dati di tracing tra servizi.<\/li><\/ol>\n\n\n\n
      1. Creare un oggetto di contesto<\/strong>: crea un oggetto con informazioni di contesto da variabili di ambiente e intestazioni nella risposta API.<\/li><\/ol>\n\n\n\n
        1. Richiamare il gestore funzioni<\/strong>: passare l’evento e l’oggetto contestuale all\u2019handler.<\/li><\/ol>\n\n\n\n
          1. Gestire la risposta<\/strong>: chiamare l’API di risposta dell’invocazione per pubblicare la risposta dell\u2019handler.<\/li><\/ol>\n\n\n\n
            1. Gestisci errori<\/strong>: se si verifica un errore, chiamare l’API di errore.<\/li><\/ol>\n\n\n\n
              1. Pulizia<\/strong>: eliminare le risorse non utilizzate, inviare dati ad altri servizi o eseguire attivit\u00e0 aggiuntive prima di ottenere il prossimo evento.<\/li><\/ol>\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

                Come creare una Lambda Bash con un runtime personalizzato<\/strong><\/h2>\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

                \"create

                <\/figcaption><\/figure>\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

                \"

                <\/figcaption><\/figure>\n\n\n\n
                #!\/bin\/sh\nset -euo pipefail\n\n# Handler format: .\n#\n# The script file .sh  must be located at the root of your\n# function's deployment package, alongside this bootstrap executable.\nsource $(dirname \"$0\")\/\"$(echo $_HANDLER | cut -d. -f1).sh\"\n\nwhile true\ndo\n    # Request the next event from the Lambda runtime\n    HEADERS=\"$(mktemp)\"\n    EVENT_DATA=$(curl -v -sS -LD \"$HEADERS\" -X GET \"http:\/\/${AWS_LAMBDA_RUNTIME_API}\/2018-06-01\/runtime\/invocation\/next\")\n    INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id \"$HEADERS\" | tr -d '[:space:]' | cut -d: -f2)\n\n    # Execute the handler function from the script\n    RESPONSE=$($(echo \"$_HANDLER\" | cut -d. -f2) \"$EVENT_DATA\")\n\n    # Send the response to Lambda runtime\n    curl -v -sS -X POST \"http:\/\/${AWS_LAMBDA_RUNTIME_API}\/2018-06-01\/runtime\/invocation\/$INVOCATION_ID\/response\" -d \"$RESPONSE\"\ndone\n<\/pre>\n\n\n\n

                Esaminiamo rapidamente questo script: <\/p>\n\n\n\n