{"id":1300,"date":"2020-04-03T11:53:30","date_gmt":"2020-04-03T09:53:30","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=1300"},"modified":"2021-03-17T15:13:20","modified_gmt":"2021-03-17T14:13:20","slug":"come-realizzare-una-pipeline-di-continuous-delivery-per-unapplicazione-front-end-angular","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/come-realizzare-una-pipeline-di-continuous-delivery-per-unapplicazione-front-end-angular\/","title":{"rendered":"Come realizzare una pipeline di continuous delivery per un\u2019applicazione front-end Angular."},"content":{"rendered":"
Abbiamo gi\u00e0 scritto pi\u00f9 volte di continuous delivery<\/strong>, solitamente concentrandoci sulla parte di backend di un’applicazione. Naturalmente, le applicazioni web richiedono anche un front-end<\/strong>, quindi rendere il processo di implementazione della parte front-end agile<\/strong> e veloce \u00e8 cruciale per il successo di un progetto.<\/span><\/p>\n In questo articolo, esporremo il \u200b\u200bprogetto completo per un’infrastruttura AWS in grado di supportare l\u2019hosting di un\u2019applicazione Angular<\/strong> completa di CDN, una funzione personalizzata per invalidare la cache e una pipeline di CD completamente automatizzata.<\/span><\/p>\n Questo progetto mira a soddisfare le best practises e fornir\u00e0 una soluzione solida, altamente scalabile e completamente gestita<\/strong> per l’hosting di applicazioni front-end su AWS.<\/span><\/p>\n Questa \u00e8 anche la soluzione pi\u00f9 ottimizzata in termini di costi che abbiamo trovato finora e consente di servire i contenuti statici in un modo altamente affidabile, performante ed economico.<\/span><\/p>\n La nostra architettura \u00e8 suddivisa logicamente in 2 parti, l’hosting rivolto al cliente<\/strong> e l’infrastruttura di continuous delivery rivolta agli sviluppatori<\/strong>.<\/span><\/p>\n Per l’hosting utilizzeremo i seguenti servizi AWS:<\/span><\/p>\n <\/p>\n Per l’infrastruttura rivolta agli sviluppatori saranno impiegati:<\/span><\/p>\n Il diagramma mostra l’infrastruttura completa<\/strong>, mettendo in evidenza i trigger e le azioni che svolger\u00e0 la pipeline.<\/span><\/p>\n L’hosting della soluzione \u00e8 basato su CloudFront, che consente di servire l’applicazione Angular in modo efficiente e affidabile. L’uso di CloudFront \u00e8 inoltre necessario per poter utilizzare HTTPS<\/strong> ed un dominio custom.<\/span><\/p>\n Le risorse compilate dell’applicazione front-end verranno archiviate in un bucket S3, utilizzato come origine per la distribuzione CloudFront. Consigliamo vivamente di mantenere privato il bucket S3, in questo modo nessun utente esterno sar\u00e0 in grado di accedere direttamente ai file, limitando i costi e le possibilit\u00e0 di exploit.<\/span><\/p>\n Abbiamo incluso Route53 nell’architettura perch\u00e9 \u00e8 il modo pi\u00f9 semplice per gestire i record DNS per il dominio, comunque, \u00e8 possibile utilizzare qualsiasi servizio DNS, purch\u00e9 sia \u200b\u200bpossibile aggiungere un record CNAME per l’applicazione front-end.<\/span><\/p>\n La parte davvero interessante dell’architettura \u00e8 quella rivolta agli sviluppatori. Il cuore della soluzione \u00e8 CodePipeline<\/strong>, che usiamo come orchestratore per reagire alle modifiche apportate nel repository e passare i dati tra tutti i servizi utilizzati durante l’esecuzione della pipeline.<\/span><\/p>\n Abbiamo anche usato CodeCommit come repository per il codice dell’applicazione. Al momento della scrittura, CodePipeline supporta CodeCommit e GitHub. Per supportare altre configurazioni di repository Git sarebbe necessaria un’ulteriore personalizzazione della pipeline.<\/span><\/p>\n Per creare la soluzione Angular, sfruttiamo CodeBuild per eseguire automaticamente il provisioning di un container per il processo di compilazione.<\/span><\/p>\n Per accelerare l\u2019aggiornamento della distribuzione, ed evitare di attendere la scadenza di ciascun oggetto nella rete CDN, abbiamo incluso un passaggio Invoke<\/strong> alla fine della pipeline per eseguire una funzione Lambda che crea una richiesta di invalidazione per la distribuzione CloudFront.<\/span><\/p>\n La nostra pipeline sar\u00e0 composta da 4 passaggi, analizziamo il meccanismo della pipeline.<\/span><\/p>\n Questo passaggio \u00e8 completamente gestito da AWS CodePipeline. Questo step \u00e8 configurato per avviare automaticamente la pipeline quando viene rilevato un push in un ramo specifico, scaricare il codice sorgente, creare un archivio e inviarlo come artefatto per il passaggio successivo.<\/span><\/p>\n Lo step di compilazione richiede come input l’artefatto del codice sorgente del passaggio precedente.<\/span><\/p>\n CodeBuild eseguir\u00e0 quindi il provisioning di un container per la build, scaricher\u00e0 il codice sorgente ed eseguir\u00e0 i comandi contenuti nel file buildspec.yml.<\/span><\/p>\n Il file yml deve essere archiviato nella radice del repository.<\/span><\/p>\n Ecco un esempio di file buildspec.yml<\/strong> per un’applicazione Angular standard:<\/span><\/p>\n L’output del processo di build \u00e8 un archivio dei file e delle cartelle contenuti nella cartella dist.<\/span><\/p>\n Anche lo step di deploy \u00e8 completamente gestito, CodePipeline scompatter\u00e0 e copier\u00e0 su S3 tutti i file e le cartelle dell’archivio ottenuto in output dal processo di build.<\/span><\/p>\n Questa azione, al momento in cui scriviamo questo articolo, non \u00e8 in grado di eliminare i file da S3. Data la tipica struttura di un\u2019app Angular, questa limitazione non rappresenta solitamente un problema.<\/span><\/p>\n Tuttavia, \u00e8 importante tenere presente che se si desidera eliminare un file dal sito Web \u00e8 necessario aggiungere un passaggio per svuotare il bucket prima di procedere al nuovo deploy, oppure occorre aggiungere un apposito passaggio che elimina i file desiderati successivamente.<\/span><\/p>\n L’ultimo passaggio della pipeline richiama una funzione Lambda per creare un invalidazione per la distribuzione CloudFront. Creare l\u2019invalidazione consente agli utenti di ottenere la versione aggiornata dell\u2019applicazione pochi minuti dopo la distribuzione, invece di attendere la scadenza degli oggetti in ciascun nodo CDN che pu\u00f2 verificarsi in momenti diversi.<\/span><\/p>\n La funzione lambda pu\u00f2 utilizzare l’SDK AWS per creare l’invalidazione. Questa funzione deve inoltre informare CodePipeline ogni volta che riscontra errori o termina con successo utilizzando un’API specifica di CodePipeline. Ecco un esempio lambda per invalidare completamente una distribuzione CloudFront e quindi notificare a CodePipeline il risultato:<\/span><\/p>\n Di seguito \u00e8 riportato un elenco di operazioni da seguire per creare la soluzione. Si noti che questo non \u00e8 un tutorial di tipo copia-incolla, ma fornisce il giusto ordine per ogni operazione da eseguire e una breve descrizione per ciascuno dei passi.<\/span><\/p>\n Prima di iniziare, assicurarsi di disporre del codice sorgente in un repository CodeCommit e di avere pieno accesso all’account per creare e configurare ciascun servizio della soluzione.<\/span><\/p>\n Complimenti!<\/strong> Hai completato l’implementazione della soluzione! Ora dovresti avere un hosting Angular completamente funzionale, completo di una pipeline di CD.<\/span><\/p>\n Siete interessanti all’argomento?<\/p>\n Leggete anche<\/p>\nI servizi<\/span><\/h2>\n
\n
\n
L’architettura<\/span><\/h2>\n
<\/p>\n
I passaggi della pipeline<\/span><\/h2>\n
Source<\/span><\/h3>\n
Build<\/span><\/h3>\n
version: '0.2'\r\nphases:\r\n install:\r\n runtime-versions:\r\n nodejs: 12\r\n pre_build:\r\n commands:\r\n - echo \"Prebuild, installing npm dependencies\"\r\n - npm install\r\n build:\r\n commands:\r\n - echo \"Starting the build step\"\r\n - npm run build\r\n - echo \"Finished\"\r\nartifacts:\r\n name: \"BuildOutput\"\r\n files:\r\n - '**\/*'\r\n base-directory: 'dist'\r\n<\/pre>\n
Deploy<\/span><\/h3>\n
CloudFront Invalidation<\/span><\/h3>\n
import boto3\r\nimport os\r\n \r\nfrom botocore.exceptions import ClientError\r\n \r\ncloudfront_client = boto3.client('cloudfront')\r\ncodepipeline_client = boto3.client('codepipeline')\r\n \r\n \r\ndef lambda_handler(event, context):\r\n try:\r\n cdn_id = os.environ[\"CDN\"]\r\n cloudfront_client.create_invalidation(\r\n DistributionId=cdn_id,\r\n InvalidationBatch={\r\n 'Paths': {\r\n 'Quantity': 1,\r\n 'Items': [\r\n '\/*'\r\n ],\r\n },\r\n 'CallerReference': event['CodePipeline.job']['id']\r\n }\r\n )\r\n \r\n codepipeline_client.put_job_success_result(jobId=event['CodePipeline.job']['id'])\r\n except ClientError as e:\r\n print(\"Boto3 exception\", e)\r\n codepipeline_client.put_job_failure_result(\r\n jobId=event['CodePipeline.job']['id'],\r\n failureDetails={\r\n 'type': 'JobFailed',\r\n 'message': e.response\r\n })\r\n except Exception as e:\r\n print(\"Error\", e)\r\n codepipeline_client.put_job_failure_result(\r\n jobId=event['CodePipeline.job']['id'],\r\n failureDetails={\r\n 'type': 'JobFailed',\r\n 'message': e.args\r\n })\r\n<\/pre>\n
Come costruire la soluzione<\/span><\/h2>\n
\n
\n
\n
\n
\n
\n
\n
\n
\n