Su Amazon Web Services<\/strong> il servizio computazionale Serverless per eccellenza rimane AWS Lambda<\/strong>, quasi immancabile in un\u2019architettura che utilizza questo paradigma.<\/p>\n\n\n\n
AWS Lambda permette di utilizzare potenza computazionale liberi dal pensiero di dover gestire i server sottostanti e senza doversi preoccupare di gestione di patch, update software o shutdown imprevisti della macchina. Utilizzando questo paradigma, possiamo concentrarci interamente sulla nostra applicazione.<\/p>\n\n\n\n
AWS Lambda oggi offre numerosi runtime engine<\/strong>, oltre che la possibilit\u00e0 di creare il proprio se si vuole utilizzare un linguaggio di programmazione non ancora supportato in modalit\u00e0 managed da AWS<\/a>.<\/p>\n\n\n\n
Andremo ad analizzare la soluzione proposta per poi deployarla sul nostro account AWS. Il progetto \u00e8 consultabile e scaricabile da GitHub<\/a>! <\/p>\n\n\n\n
Iniziamo col proporre lo schema architetturale<\/strong> della nostra applicazione Serverless.<\/p>\n\n\n
Il codice verr\u00e0 rilasciato su una AWS Lambda function, la cui invocazione sar\u00e0 possibile solamente attraverso un API Gateway<\/strong>.<\/p>\n\n\n\n
In un applicativo backend non pu\u00f2 ovviamente mancare un Data Source. Sfruttando a pieno il paradigma Serverless, andremo ad utilizzare un Aurora Serverless<\/strong> come database relazionale dove salvare e recuperare i nostri dati.<\/p>\n\n\n\n
Ricapitolando, andremo ad utilizzare i seguenti servizi cloud:<\/p>\n\n\n\n
Gli strumenti disponibili per la gestione di una infrastruttura di questo tipo sono numerosissimi: la CLI di AWS, la console o uno dei framework presenti oggi in rete come Troposphere, Terraform, o AWS SAM.<\/p>\n\n\n\n
Come spiegato nell\u2019introduzione, il nostro obiettivo \u00e8 realizzare un\u2019applicazione backend velocemente, per questo motivo abbiamo scelto Serverless framework<\/strong>!<\/p>\n\n\n\n
Serverless \u00e8 il framework scritto in Node.js che permette di gestire la lifecycle degli applicativi serverless. Ad oggi supporta numerosi Cloud Provider e funzionalit\u00e0.<\/p>\n\n\n\n
Per quanto riguarda AWS, Serverless ci permetter\u00e0 di creare e gestire le risorse di cui abbiamo bisogno sul nostro account utilizzando uno stack di Cloudformation<\/strong>.<\/p>\n\n\n\n
Prima di iniziare a lavorare sul progetto, \u00e8 necessario aver soddisfatto i seguenti requisiti:<\/p>\n\n\n\n
Siccome andremo a scrivere un\u2019applicazione in TypeScript<\/strong>, Node.js sar\u00e0 il runtime environment su cui andremo ad eseguire il nostro codice compilato in JavaScript.<\/p>\n\n\n\n
Per poter deployare lo stack di Cloudformation su AWS, \u00e8 necessario aver installato il Serverless framework sulla nostra macchina, tramite il comando npm install -g serverless, <\/em>e aver configurato correttamente le credenziali della AWS CLI.<\/p>\n\n\n\n
Ora che abbiamo dato le opportune premesse ed elencato i requisiti, possiamo scaricare il progetto dal nostro repository GitHub<\/strong> e vedere come \u00e8 strutturato.<\/p>\n\n\n\n
Iniziamo clonando il progetto:<\/p>\n\n\n\n
git clone <\/em>https:\/\/github.com\/besharpsrl\/blog-serverless-backend-nodejs<\/em><\/a><\/p>\n\n\n\n
Andiamo ad elencare quelle che meritano alcune precisazioni:<\/p>\n\n\n\n
Nel file package.json <\/em>\u00e8 possibile trovare queste e altre dipendenze di utility, <\/em>oltre ai comandi che ci saranno utili per eseguire e testare le nostre API in locale e poi rilasciarle sull\u2019account AWS.<\/p>\n\n\n\n
Aprendo il progetto sul vostro IDE noterete uno scaffolding di questo tipo:<\/p>\n\n\n
Entriamo ora nel dettaglio delle logiche applicative.<\/p>\n\n\n\n
Descriveremo un caso d\u2019uso molto semplice: delle semplici API REST che offrono delle CRUD functions per gestire dei libri di una biblioteca.<\/strong><\/p>\n\n\n\n
La logica risiede sotto la directory book; <\/em>qui \u00e8 definito il Controller di tsoa<\/em>, il Service con la business logic e i modelli di sequelize.<\/em><\/p>\n\n\n\n
Nel file app.ts <\/em>andremo invece a registrare le rotte Express, autogenerate da tsoa a partire dai Controller, per poi esportare un modulo che verr\u00e0 utilizzato come handler <\/em>per la nostra Lambda function.<\/p>\n\n\n\n
Il tutto \u00e8 reso possibile dallo statement:<\/p>\n\n\n\n
module.exports.handler = sls(app)<\/em><\/p>\n\n\n\n
Con questo comando stiamo invocando una funzione del Serverless framework<\/strong> che andr\u00e0 a creare un wrapper attorno alle rotte Express. L\u2019handler della nostra AWS Lambda function sapr\u00e0 quindi a quale rotta Express dirottare le chiamate API REST.
Infine, non ci resta che analizzare il file serverless.yml<\/em>:<\/p>\n\n\n\n
service: express-serverless\nprovider:\n name: aws\n runtime: nodejs12.x\n region: eu-west-1\n stage: ${env:NODE_ENV}\n environment:\n NODE_ENV: ${env:NODE_ENV}\n iamRoleStatements:\n - Effect: 'Allow'\n Action:\n - 'secretsmanager:GetSecretValue'\n Resource:\n - '*'\n\npackage:\n exclude:\n - node_modules\/**\n - defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" defer\" src\/**\n\nlayers:\n nodeModules:\n path: layer\n compatibleRuntimes:\n - nodejs12.x\n\n\u2026..\n\nfunctions:\n app:\n handler: dist\/app.handler\n layers:\n - {Ref: NodeModulesLambdaLayer}\n events:\n - http:\n path: \/api\n method: ANY\n cors: true\n - http:\n path: \/api\/{proxy+}\n method: ANY\n cors: true\n vpc:\n securityGroupIds:\n - {Ref: LambdaSecurityGroup}\n subnetIds:\n - \"subnet-1\"\n - \"subnet-2\"\n - \"subnet-3\"\n\nplugins:\n - serverless-offline<\/pre>\n\n\n\nProprio qui stiamo andando a definire le parti essenziali dell\u2019infrastruttura, come il Cloud Provider su cui andremo a deployare la nostra Lambda function, il suo handler (il modulo che abbiamo esportato nel file app.ts<\/em>) e i Lambda Layer da agganciare alla function.<\/p>\n\n\n\n
Con questa configurazione, infatti, andremo a creare un Lambda Layer contenente tutti i node modules. Cos\u00ec facendo, alleggeriremo notevolmente le dimensioni della nostra funzione ottenendo vantaggi in performance<\/strong> durante la sua esecuzione.<\/p>\n\n\n\n
Ma come verr\u00e0 invocata la Lambda function? Tramite gli events<\/em> di tipo http <\/em>definiti nel file di Serverless!<\/em> Questo permetter\u00e0 la creazione di un API Gateway con cui sar\u00e0 possibile richiamare la nostra funzione.<\/p>\n\n\n\n
La risorsa col path \/api\/{proxy+}<\/em> sar\u00e0 quella che far\u00e0 da proxy alle rotte di backend. Abbiamo cos\u00ec creato un\u2019unica risorsa lato API Gateway che ci permetter\u00e0 di invocare tutte le REST API.<\/p>\n\n\n\n
Local testing<\/h2>\n\n\n\n
Vi piacerebbe poter testare le API senza doverle necessariamente rilasciare su AWS Lambda ad ogni modifica? Il pacchetto di Node.js serverless-offline <\/em><\/strong>\u00e8 ci\u00f2 che fa al caso nostro!<\/strong><\/p>\n\n\n\n
Serverless-offline<\/em><\/strong> \u00e8 un plugin di Serverless che emula AWS Lambda e API Gateway sulla nostra macchina per velocizzare le attivit\u00e0 di sviluppo.<\/p>\n\n\n\n
Prima di poterlo usare, per\u00f2, sar\u00e0 necessario creare un database Postgres<\/strong> in locale e lanciare le migrazioni di Sequelize:<\/p>\n\n\n\n
Dalla root del progetto eseguiamo il comando<\/p>\n\n\n\n
docker-compose up -d<\/pre>\n\n\n\nE poi:<\/p>\n\n\n\n
npm run migrate-db-local<\/pre>\n\n\n\nOra che abbiamo il database in locale, non ci resta che provare le API! Lanciamo il comando npm run start <\/em>per compilare il nostro codice TypeScript in JavaScript ed emulare la Lambda tramite il plugin serverless-offline<\/em>.<\/p>\n\n\n\n
Non appena il comando avr\u00e0 completato la sua esecuzione, otterremo il seguente output sul terminale:<\/p>\n\n\n\n
<\/figure>\n\n\n\n
A questo punto siamo pronti per testare le API col nostro tool preferito (Postman, Curl). Utilizzando, ad esempio, il comando curl <\/em>possiamo eseguire da terminale il comando: <\/p>\n\n\n\n
curl http:\/\/localhost:3000\/dev\/api\/book\/<\/em><\/p>\n\n\n\n
Deploy <\/h2>\n\n\n\n
Ci siamo: \u00e8 il momento di deployare l\u2019infrastruttura sul nostro account!<\/p>\n\n\n\n
Lanciando il comando npm run deploy-dev<\/em> inizier\u00e0 il processo di rilascio<\/strong>. La prima volta apparir\u00e0 il seguente output:<\/p>\n\n\n\n
<\/figure>\n\n\n\n
Sull\u2019account AWS potremo vedere che \u00e8 stato generato uno stack Cloudformation contenente le risorse di cui abbiamo bisogno.<\/p>\n\n\n\n
Accedendo alla console, tra la lista delle Lambda functions troveremo anche quella appena creata:<\/p>\n\n\n\n
<\/figure>\n\n\n\n
Prima di poter testare le API sar\u00e0 necessario lanciare le migrazioni di Sequelize<\/em> per creare le tabelle sul database Aurora Serverless.
Dobbiamo quindi poterci connettere al database su AWS. Creiamo una macchina bastion sull\u2019account e dirottiamo il traffico dalla nostra macchina al bastion. Utilizziamo il comando sshuttle:<\/em><\/p>\n\n\n\nsshuttle --dns -r ubuntu@EC2_BASTION_IP YOUR_VPC_CIDR --ssh-cmd 'ssh -i YOUR_PEM_KEY'<\/pre>\n\n\n\nA questo punto possiamo lanciare la migrazione tramite il comando:<\/p>\n\n\n\n
npm run migrate-dev<\/pre>\n\n\n\nUna volta completate le migrazioni su Aurora Serverless, proviamo le API attraverso API Gateway.<\/p>\n\n\n\n
Nell\u2019output del comando npm run deploy-dev <\/em>\u00e8 gia stato stampato l\u2019endpoint dell\u2019API Gateway, proviamolo subito:<\/p>\n\n\n\n
curl https:\/\/sxfd74jes5.execute-api.eu-west-1.amazonaws.com\/dev\/api\/book\/<\/em><\/p>\n\n\n\n
Il rilascio dell\u2019infrastruttura Serverless sull\u2019account AWS \u00e8 finito!<\/p>\n\n\n\n
Conclusioni<\/h2>\n\n\n\n
Per concludere, in questo articolo abbiamo visto come creare un\u2019applicazione Serverless su AWS Lambda utilizzando Node.js come runtime engine.<\/strong><\/p>\n\n\n\n
Per scrivere il progetto abbiamo scelto TypeScript<\/strong> per avere il vantaggio di usare un linguaggio trascompilato, ampiamente supportato e conosciuto.<\/p>\n\n\n\n
La scelta di Node.js come runtime engine ci ha permesso di avere un Cold Start time<\/strong><\/a> molto contenuto<\/strong> in quanto JavaScript viene direttamente interpretato dall\u2019engine. Inoltre, grazie alla configurazione utilizzata, il codice sorgente \u00e8 stato separato dalle dipendenze, salvate su Lambda Layer, abbattendo drasticamente le dimensioni del nostro sorgente e migliorando ulteriormente le performance di avvio.<\/p>\n\n\n\n
Soddisfatti? \ud83d\ude42<\/p>\n\n\n\n
A presto su #Proud2beCloud<\/a> per il prossimo articolo!<\/p>\n","protected":false},"excerpt":{"rendered":"