Eseguire il deploy di una webapp Angular in produzione
Non sai come distribuire una webapp Angular in produzione, ma non sai come fare? Nessun problema: sei nel posto giusto.
Introduzione
Se non hai familiarità con Angular portato in produzione, nessun problema: questo articolo è quello che fa al caso tuo. Quello che leggerai vale anche per React o Angular o qualsiasi altra applicazione basata su un framework che lavori con un router interno. Dal momento che il codice che viene scritto viene poi eseguito tramite un browser, il quale parla solo HTML, CSS e JavaScript, questa guida descrive esattamente il modo in cui possiamo trasformare il tuo codice per essere_impacchettato_ e portato in produzione. In questo caso, partiremo da una semplice applicazione Angular: se non sai come fare, leggi questo articolo dove, tra le altre cose, vediamo come creare una semplice webapp!
Dopo aver visto come funziona, vedremo come funziona il processo di build per la creazione dei file di produzione, e poi porteremo l’esempio su tre casi d’uso piuttosto frequenti: Nginx, Apache e Docker come tecnologia per la messa in produzione della nostra applicazione.
Come funziona
Utilizzando il comando ng serve per avviare la nostra applicazione, vengono generati cinque file:
- inline.bundle.js: un piccolo file che aiuta il webpack a caricare le risorse di cui ha bisogno;
- main.bundle.js: la maggior parte della nostra applicazione Angular;
- polyfills.bundle.js: questo file tutto l’occorrente per far funzionare Angular nei browser meno recenti;
- styles.bundle.js: gli stili!
- vendor.bundle.js: contiene la definizione delle librerie di Angular.
Ora che abbiamo una vaga idea di come funziona, passiamo al primo step: il processo di build.
Step 1: build
Il comando ng serve funziona per eseguire la nostra applicazione in ambiente di sviluppo. E per la produzione? Se guardiamo nel file package.json, possiamo vedere che ci sono diversi script che possiamo usare:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
Per rendere il nostro codice fruibile per la produzione, abbiamo bisogno di configurare un parametro aggiuntivo, ossia il flag –prod. Possiamo farlo in due modi:
# utilizzando gli script npm
$ npm run build
# utilizzando direttamente la cli di Angular
$ ng build --prod
Your global Angular CLI version (11.2.2) is greater than your local version (11.1.0). The local Angular CLI version is used.
To disable this warning use "ng config -g cli.warnings.versionMismatch false".
√ Browser application bundle generation complete.
√ Copying assets complete.
√ Index html generation complete.
Initial Chunk Files | Names | Size
main.dc687d6444d7b41c628c.js | main | 563.91 kB
styles.ace737526e6d3cb122f0.css | styles | 71.10 kB
polyfills.6abdde2583a2e01a2350.js | polyfills | 35.73 kB
runtime.7b63b9fd40098a2e8207.js | runtime | 1.45 kB
| Initial Total | 672.19 kB
Build at: 2021-05-03T15:55:44.502Z - Hash: fea003044cca574ddcf4 - Time: 47700ms
Il comando ng build in questo caso compila l’app Angular in una directory di output denominata dist nella cartella principale del progetto (se non specificato diversamente); questa opzione attiva molti flag di ottimizzazione. Uno di questi è –aot per la compilation Ahead Of Time (se non ne hai mai sentito parlare, trovi qui un articolo molto interessante).
I modelli dei componenti vengono compilati durante la compilazione, quindi TypeScript può rilevare più problemi nel codice di quanti non ne abbiamo rilevati durante l’esecuzione in sviluppo: nessun problema!
Gli errori sono solitamente molto parlanti, quindi ti basterà dare un’occhiata ai log per risolverli.
Tornando alla cartella dist, dicevamo che il comando build crea una nuova cartella chiamata per la distribuzione: al suo interno infatti ci sono i file che ci serviranno per dare vita alla nostra applicazione su un server!
Questo è tutto ciò che serve per distribuire un’applicazione Angular:
A questo punto, quello che ci serve è solo… un server. Che sia Apache o qualsiasi altra soluzione, è piuttosto semplice: passiamo allo step successivo!
Step 2a: usare Nginx
All’interno della cartella di Nginx, sotto /etc/nginx/sites-available, crea un nuovo file per l’applicazione che hai bisogno di deployare copiando il contenuto riportato di seguito, dove my-website rappresenta il nome del tuo sito:
$ cd /etc/nginx/sites-available && nano my-website.com
my-website.com
server {
root path/to/folder;
index index.html;
server_name my-website.com;
location / {
try_files $uri $uri/ /index.html;
}
}
Con queste semplici righe, facciamo in modo che Nginx serva solo il file index.html, il quale contiene tutto l’occorrente per tirare su l’applicazione, in maniera del tutto indipendente dall’URL del nostro dominio.
In questo modo, anche quando l’utente navigherà l’applicazione, il file a cui farà riferimento sarà sempre index.html.
Il percorso dove andremo a inserire i file dell’applicazione viene specificato nella direttiva root: ad esempio, una cartella che possiamo utilizzare per inserire questi file potrebbe essere /usr/share/nginx/html.
Nginx leggerà le configurazioni dalla cartella sites-enabled ed esporrà le relative applicazioni: ecco perché dobbiamo creare un link verso il file appena creato utilizzando questo comando:
$ ln -s /etc/nginx/sites-available/my-website /etc/nginx/sites-enabled/
Infine, per verificare che la configurazione sia a posto, eseguiamo questo comando:
$ sudo nginx -t
Se non ci sono errori, è il momento di riavviare il servizio Nginx.
$ sudo systemctl restart nginx
Fatto questo, la nostra applicazione è pronta per la produzione!
Step 2b: usare Apache
Il processo è del tutto analogo a quanto visto con Nginx: dopo aver installato Apache, all’interno della cartella /var/www/html copiamo il contenuto della cartella dist prodotta nel primo step; ci occorre tuttavia fare un passo in più, ossia configurare l’errore 404 di reindirizzamento alla pagina index.html, nel caso in cui si utilizzi il router interno di Angular.
Per farlo, modifichiamo il file 000-default.conf presente nella cartella /etc/apache2/sites-enabled e aggiungiamo la seguente riga:
ErrorDocument 404 /index.html
Il risultato dovrebbe essere simile al seguente:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
# redirect 404 to index page
ErrorDocument 404 /index.html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Riavviamo a questo punto il server Apache tramite il seguente comando:
sudo service apache2 restart
That’s all, folks!
Step 2c: usare Docker
Infine, vediamo come mettere in produzione un’applicazione Angular usando Docker.
Supponendo che questa applicazione non abbia dipendenze particolari (database, backend, ecc.), possiamo lavorare in questo modo: usiamo una prima immagine di base Node.js per eseguire tutte le operazioni viste in precedenza, ossia installare le dipendenze tramite il file package.json e copiare il contenuto dell’app Angular dopo che è stato eseguire il comando ng build –prod, e poi, grazie all’immagine di base di Nginx, andiamo a copiare il contenuto della cartella prodotta all’interno della cartella /usr/share/nginx/html:
FROM node:12.16.1-alpine As builder
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build --prod
FROM nginx:1.15.8-alpine
COPY --from=builder /usr/src/app/dist/SampleApp/ /usr/share/nginx/html
Questi sono solo alcuni dei metodi: ne conosci altri?
Tips&Tricks:
Problemi con il routing? (i.e.: 404: page not found?)
Se, per esempio, l’applicazione viene deployata in produzione all’interno di una sottocartella del server (ad esempio, sotto /var/www/html/mysite), può darsi che sia necessario eseguire uno step in più: in particolare, dovremo rivedere lo step 1 e cambiare il tag di base nel tuo file index.html, da a
Questo tag viene infatti utilizzato dal router di Angular per sapere dov’è la base di ogni path, però mentre il server di Angular lo interpreta correttamente (perché deploya l’applicazione sul contesto principale), nel caso di un webserver con questa gestione delle sottocartelle, c’è bisogno di un’informazione aggiuntiva.