Torna al blog

Come distribuire un'app Node.js (Express.js) con Docker su Ubuntu 20.04

Come distribuire un'app Node.js (Express.js) con Docker su Ubuntu 20.04

Introduzione

Docker è una piattaforma di containerizzazione che costituisce un ambiente standardizzato leggero, virtualizzato, portabile e definito dal software. Consente al software di essere eseguito in isolamento rispetto ad altri software in esecuzione sulla macchina host fisica. Docker è un componente fondamentale dell'aspetto di Continuous Development e Integration dello sviluppo software. Offre un'alternativa leggera alle macchine virtuali e consente agli sviluppatori di sfruttare architetture applicative distribuite. Per una panoramica approfondita dell'ecosistema Docker, consulta questo articolo.

Il processo di creazione di un'applicazione con Docker inizia con uno sviluppatore che crea un'immagine per la propria applicazione. Successivamente, l'immagine verrà distribuita all'interno di un container. L'immagine contiene i componenti che definiscono un'applicazione, come il codice dell'applicazione, le librerie, i file di configurazione, le variabili d'ambiente e l'ambiente di runtime. L'immagine standardizza l'ambiente all'interno di un container, conferendo alla containerizzazione le sue caratteristiche di portabilità. Node.js è un ambiente di runtime JavaScript backend open-source e multipiattaforma in grado di eseguire codice JavaScript al di fuori di un browser web. È basato sul motore V8 JavaScript Engine. Express.js è un framework JavaScript backend minimalista che viene eseguito sopra Node.js.

In questo tutorial, creeremo un'immagine per un sito web basato sul framework Express. Utilizzeremo Bootstrap, che è una libreria frontend, per migliorare l'aspetto del frontend. Una volta creata l'immagine, creeremo un container e lo invieremo a Docker Hub. Docker Hub consente agli sviluppatori di ospitare applicazioni containerizzate per una facile distribuzione in qualsiasi ambiente Docker. Una volta che il container sarà ospitato su Docker Hub, lo scaricheremo (pull) e creeremo un'altra immagine che servirà effettivamente il nostro sito web.

Prerequisiti

Questo sarà un tutorial pratico. Dovresti creare un ambiente che ti consenta di seguire i passaggi.

Passo 1: Configurare le dipendenze dell'applicazione

Devi creare il codice sorgente della tua applicazione prima di poter creare l'immagine. Il codice sorgente dell'applicazione include codice, contenuti statici e dipendenze che verranno copiati nel container. Inizia creando una directory per il tuo progetto nella home directory dell'utente non root. La chiameremo node_express, ma sei libero di usare il nome di directory che preferisci:

Successivamente, spostati in questa directory:

Questa sarà la directory radice della tua applicazione. Un'applicazione node.js si aspetta un file package.json nella cartella radice. Npm utilizza questo file per determinare di quali dipendenze ha bisogno la tua applicazione. Inserisci il seguente comando per creare questo file:

Successivamente, aggiungi il seguente frammento di codice al file. Puoi aggiornare il nome, l'autore, la descrizione e il file del punto di ingresso come desideri:

Come puoi vedere, questo file specifica il nome del progetto, la versione, l'autore e la licenza con cui verrà condiviso il codice dell'applicazione. Si consiglia di utilizzare un nome breve e descrittivo per il progetto per evitare duplicati nel registro npm. Abbiamo specificato la licenza ISC per il progetto, che consente la copia, la modifica o la distribuzione gratuita del codice dell'applicazione.

Ancora più importante, dovresti notare le seguenti direttive nel file:

  • main”: questa direttiva specifica il punto di ingresso dell'applicazione, che abbiamo impostato come index.js. Creeremo questo file a breve.
  • dependencies”: questa direttiva specifica le dipendenze dell'applicazione che verranno scaricate dal registro npm quando eseguiamo il comando npm, nel nostro caso, vogliamo Express versione 4.17.1 e successive.

Ora puoi salvare il file premendo Ctrl + O. Quindi, chiudi il file premendo Ctrl + X. Successivamente, installeremo le dipendenze eseguendo il seguente comando:

Il comando installa le dipendenze dell'applicazione specificate nel file package.json all'interno delle directory node_modules. Sono state create automaticamente la prima volta che hai eseguito il comando. Con le dipendenze della nostra applicazione installate, ora puoi iniziare ad aggiungere il codice dell'applicazione.

Passo 2: Aggiunta dei file di codice dell'applicazione

Creeremo un sito web di ricette di base, per gentile concessione di allrecipes. Il punto di ingresso principale per l'applicazione è il file index.js. Aggiungeremo una directory views che conterrà le varie pagine e gli asset statici del progetto. Il sito web avrà una landing page che conterrà informazioni introduttive e link ad alcune ricette.

Il codice della nostra landing page sarà inserito nel file home.html. Per prima cosa, crea il file index.js inserendo il seguente comando:

Aggiungi il seguente codice, che importa e crea un'applicazione Express. Specifica anche l'oggetto Router, la directory di base e la porta su cui verrà servita questa app:

require è una funzione JavaScript che carica un modulo. In questo caso, stiamo caricando il modulo express. Successivamente, utilizzeremo il modulo importato per creare gli oggetti express e router. L'oggetto router esegue le funzioni di routing dell'app rispondendo alle chiamate dei metodi HTTP che aggiungeremo a questo oggetto man mano che procediamo con il tutorial.

Abbiamo anche impostato path e port. La costante path definisce la directory di base per il codice. Nel nostro caso è la sottodirectory views all'interno della directory principale del progetto. La costante port specifica la porta su cui l'app express deve rimanere in ascolto, nel nostro esempio l'abbiamo impostata su 8090.

Una volta ottenute le costanti, possiamo specificare alcune rotte per l'applicazione utilizzando l'oggetto router. Aggiungi il seguente codice al file index.js per specificare le rotte:

Puoi aggiungere middleware alle rotte utilizzando la funzione router.use. In questo caso, aggiungiamo una funzione che registra le richieste del router prima di passarle alle rotte dell'applicazione. Una richiesta GET alla base dell'applicazione restituirà un file home.html pagina. Successivamente, abbiamo aggiunto le pagine per tre ricette che verranno anch'esse recuperate utilizzando la richiesta GET alla pagina della ricetta specifica.

Infine, aggiungi il seguente codice per montare il router middleware e gli asset statici dell'applicazione. Inoltre, indica all'applicazione express di rimanere in ascolto sulla porta 8090:

Il tuo file index.js completo dovrebbe apparire così:

Ora puoi salvare e chiudere il file. Il passo successivo consiste nell'aggiungere le pagine web statiche alla directory views. Inizia inserendo il seguente comando per creare la directory:

Inserisci il seguente comando per aprire il file della pagina di destinazione home.html:

Aggiungi il seguente codice al file. Il codice importa Bootstrap e offre ai visitatori del sito web alcune informazioni sullo scopo del sito:

Oltre a importare Bootstrap, la pagina aggiunge anche un semplice menu di navigazione per aiutarci a spostarci tra le pagine e tornare alla pagina di destinazione. Abbiamo anche aggiunto una riga per importare il nostro file CSS personalizzato:

Useremo questo file per aggiungere uno stile personalizzato all'applicazione in seguito. Ora creiamo le tre pagine per le ricette. Iniziamo creando la pagina della lasagna. Apri il file con l'editor nano usando il seguente comando:

Nel file aperto, aggiungi il seguente codice. Questo file importerà Bootstrap, il file custom.css, specificherà un menu di navigazione e offrirà alcune informazioni sulla ricetta delle lasagne:

Seguiamo lo certo processo per creare un file per la pagina della ricetta del guacamole. Apri il file con nano eseguendo il seguente comando:

Quindi aggiungi questo codice al file:

Infine, creiamo il banana_bread.html file inserendo il comando:

Quindi, aggiungi il seguente codice HTML al file:

Ora abbiamo creato tutte le pagine. Se ricordi, dobbiamo aggiungere il file css/custom.css. Inserisci il seguente comando per creare la directory:

Quindi crea e apri il file nell'editor nano con il comando:

Puoi aggiungere altri codici CSS per personalizzare lo stile del tuo sito web come desideri. Per brevità, aggiungiamo il seguente frammento di codice al file:

Salva e chiudi il file quando hai finito.

Puoi avviare l'applicazione poiché ora abbiamo installato il codice sorgente dell'applicazione e le dipendenze del progetto.

We had set the app to listen on a port 8090, esegui il seguente comando per istruire il firewall a consentire il traffico attraverso questa porta. Se hai specificato una porta diversa, sostituisci il numero di porta nel comando:

Ora puoi avviare l'applicazione. Ma prima, assicurati di trovarti nella directory principale del progetto eseguendo il seguente comando:

Avvia l'applicazione con node index.js. Se hai specificato un punto di ingresso diverso, sostituiscilo con il tuo punto di ingresso:

Se navighi con il tuo browser su http://your_public_server_ip:8090, vedrai la pagina di destinazione delle Ricette come definita:

Recipes

 

Puoi vedere i collegamenti alle varie ricette nella barra di navigazione. Facciamo clic su alcuni. Di seguito abbiamo la pagina della ricetta delle Lasagne:

Node.js app install on Ubuntu 1

E qui abbiamo la pagina della ricetta del Guacamole:

Guacamole

Fino a questo punto, hai creato la tua applicazione e verificato che funzioni come previsto. Puoi uscire dal server premendo Ctrl + C e passare alla creazione del Dockerfile. I Dockerfile aiutano nella scalabilità rendendo possibile ricreare un'istanza dell'applicazione quando necessario.

Passo 3: Creazione del Dockerfile

Docker legge le istruzioni specificate in un Dockerfile durante la compilazione delle immagini. Specifica l'ambiente di runtime di un'applicazione. Di conseguenza, aiuta gli sviluppatori a evitare discrepanze con le dipendenze o modifiche alle versioni di runtime. Inserisci il seguente comando per creare il Dockerfile:

Un'immagine Docker viene creata utilizzando diversi livelli di immagini che si sovrappongono l'uno all'altro. Si inizia aggiungendo un'immagine di base per formare il punto di partenza dell'app.

Poiché l'applicazione prevede di essere eseguita in un node.js ambiente, inizieremo aggiungendo l' immagine node:10-alpine per node.js. Attualmente, mentre scriviamo questo tutorial, questa è la versione LTS consigliata di Node.js. Abbiamo scelto questa immagine specifica perché deriva dal progetto Alpine Linux. Di conseguenza, aiuterà a mantenere al minimo le dimensioni della nostra immagine. Esistono diverse varianti di immagine nella pagina delle immagini Node di Docker Hub tra cui puoi scegliere in base alle tue esigenze.

Aggiungi il seguente codice per impostare l'immagine di base dell'applicazione utilizzando la direttiva FROM:

Questa immagine include Node.js e npm. Ogni Dockerfile deve iniziare con una direttiva FROM. L'immagine Docker node viene fornita per impostazione predefinita con un utente node non root che puoi utilizzare per eseguire il container dell'applicazione come root. La sicurezza di Docker consiglia di non eseguire i container come root e di limitare i privilegi solo a quelli richiesti per eseguire le sue risorse.

In tal caso, utilizzeremo la directory home dell'utente node come directory di lavoro per l'applicazione e come utente all'interno del container. Puoi consultare questa guida alle migliori pratiche per l'immagine Docker Node per ulteriori informazioni.

Creeremo la sottodirectory node_modules all'interno di /home/node insieme alla directory dell'app per aiutare a ottimizzare i permessi per il codice dell'applicazione. La creazione di queste directory garantisce che abbiano i permessi corretti quando eseguiamo il comando npm install localmente all'interno dei container. Una volta create le directory, devi impostarne la proprietà sull'utente node . Lo faremo all'interno del Dockerfile aggiungendo la seguente riga:

Quindi imposterai la directory di lavoro aggiungendo la seguente riga:

È una buona idea impostare sempre WORKDIR in modo che Docker non debba crearne una per impostazione predefinita.

Aggiungi la seguente riga per copiare i file package.json e package-lock.json :

Si consiglia di aggiungere l'istruzione COPY prima di eseguire npm install o di copiare il codice sorgente dell'applicazione. Ciò consente di sfruttare il meccanismo di caching di Docker. Durante il processo di compilazione, Docker verifica se ha un livello memorizzato nella cache per ogni istruzione. Ciò significa che se non hai modificato il file package.json , Docker utilizzerà il livello dell'immagine esistente ed eviterà di reinstallare i moduli node , garantendo così processi di compilazione più rapidi.

Prima di eseguire npm install , aggiungi la seguente riga per passare all'utente node per garantire che tutti i file dell'applicazione e la directory node_modules siano di proprietà dell'utente node non root:

Il nostro container è ora pronto per eseguire il comando npm install. Aggiungi la seguente riga al Dockerfile:

Una volta installati i node_modules , aggiungi la seguente riga che dirà a Docker di copiare il codice dell'applicazione nella directory dell'applicazione sul container con i permessi e la proprietà corretti, ovvero l'utente node non root:

L'ultimo passaggio consiste nell'esporre la porta 8090 sul container, come avevamo definito nel nostro file di ingresso index.js :

EXPOSE imposta quali porte sul container saranno aperte a runtime. CMD esegue il comando per avviare l'applicazione, in questo caso, node index.js.

Dovresti avere solo un comando CMD nel Dockerfile, poiché solo l'ultimo ha effetto. Dai un'occhiata alla documentazione di riferimento del Dockerfile per un elenco di cose che puoi fare con il Dockerfile.

Il tuo Dockerfile completo dovrebbe apparire così:

Ora puoi salvare e chiudere il file.

La prossima cosa da fare è aggiungere il file .dockerignore. Proprio come il file .gitignore, il file .dockerignore specifica quali file e directory all'interno della directory del progetto non devono essere copiati nel container.

Apri il file con l'editor nano:

Aggiungi le seguenti righe all'interno del file:

Se stai lavorando con un repository git, dovresti anche aggiungere la directory .git e il file .gitignore. Salva e chiudi il file.

Se tutto è andato a buon fine, è il momento di compilare l'immagine dell'applicazione utilizzando il comando docker build. Puoi aggiungere il flag –t al comando docker build per taggare l'immagine con un nome facile da ricordare, anziché la stringa casuale impostata da Docker per impostazione predefinita. Invieremo anche l'immagine a Docker Hub, quindi è consigliabile includere il tuo nome utente Docker Hub nel tag.

Useremo nodejs-express-image come nome del tag. Sei libero di scegliere il nome del tag che preferisci. Ecco il comando per compilare l'immagine:

Ricorda di sostituire your_dockerhub_username con il tuo vero nome utente Docker Hub. Il . (punto) alla fine specifica che il contesto di compilazione è la directory corrente.

Il processo di compilazione richiede un minuto o due. Una volta terminato, inserisci il comando per verificare le tue immagini:

Dovresti vedere qualcosa di simile a questo:

sudo docker images

Ricorda che abbiamo sostituito your_dockerhub_username con un nome utente reale.

Dopo aver confermato che la tua immagine è stata compilata, ora puoi creare un container con l'immagine utilizzando docker run. Saranno inclusi i seguenti flag:

  • -p: pubblica la porta sul container e la mappa su una porta del sistema host. Utilizzeremo la porta 80 sul sistema host a scopo dimostrativo. Tuttavia, se hai un altro processo in esecuzione su quella porta, sentiti libero di modificarla come necessario. Scopri di più sul port binding nella documentazione di Docker.
  • -d: per la modalità detached. Consente al container di continuare a essere eseguito in background.
  • --name: puoi usarlo per impostare un nome facile da ricordare invece di lasciare che Docker assegni una stringa casuale.

Il comando per compilare il container è il seguente. Sostituisci opportunamente il tuo nome utente Docker Hub:

Attendi che il container venga compilato e avviato. Puoi utilizzare questo comando per ispezionare tutti i container in esecuzione:

Dovresti vedere un output simile al seguente:

Node.js app install on Ubuntu 3

Come si vede nell'output, il container è ora in esecuzione. Puoi visualizzarlo nel browser visitando l'indirizzo IP pubblico del tuo server senza specificare la porta. La tua home page verrà caricata:

awesome recipe

 

Hai distribuito con successo un sito web statico Node Express con Docker. Vediamo come possiamo inviare questa immagine a Docker Hub per usi futuri e scopi di scalabilità.

Passo 4: Lavorare con i repository di immagini Docker

Puoi inviare le tue immagini a registri di immagini come Docker Hub e salvarle per un uso futuro, condividerle con altri sviluppatori o consentire la scalabilità dei tuoi container. Possiamo inviare l'immagine che abbiamo creato a Docker Hub e usarla per ricreare un container.

Usa il seguente comando per accedere al tuo account Docker Hub. Sostituisci con il tuo nome utente Docker Hub effettivo:

Inserisci la tua password quando richiesto. Una volta effettuato l'accesso, un file ~/.docker/config.json viene creato nella directory home del tuo utente, contenente le tue credenziali di Docker Hub.

Fatto questo, inserisci il seguente comando per inviare l'immagine a Docker Hub, specificando il tag impostato in precedenza durante la creazione dell'immagine:

Questo comando invia l'immagine docker al tuo account Docker Hub. Se visiti il tuo account, puoi vedere l'immagine caricata di recente:

Docker Hub

Possiamo testare l'utilità del repository di immagini eliminando il container dell'applicazione corrente e ricostruendolo utilizzando l'immagine nel repository.

Elenca i tuoi container correnti inserendo il comando:

Dovresti vedere un output simile a questo:

Docker Hub

Prendi nota del CONTAINER ID elencato nel tuo output, copialo e usalo per arrestare il tuo container con il comando, sostituendo l'ID con il tuo:

Inserisci il seguente comando per elencare tutte le immagini docker disponibili nel tuo sistema:

L'output mostrerà il nome della tua immagine, l'immagine node.js e altre immagini del processo di compilazione.

Inserisci il seguente comando per rimuovere le immagini, comprese quelle inutilizzate o orfane:

Digita y per confermare. Questo rimuove i container e le immagini arrestati. Se li elenchi, vedrai un elenco vuoto nell'output:

output

Ora hai rimosso sia il container che esegue l'applicazione, sia l'immagine stessa. Scopri di più su come rimuovere container, immagini e volumi Docker seguendo il nostro tutorial.

Ora possiamo ricreare l'intero processo scaricando prima l'immagine da Docker Hub con il seguente comando. Sostituisci il tuo nome utente Docker Hub in modo appropriato:

Elenca nuovamente le tue immagini Docker con il comando:

Dovresti vedere l'immagine nell'output:

sudo docker

Ora puoi ricostruire il tuo container usando il comando del Passo 3. Naturalmente, sostituisci il tuo nome utente Docker Hub dove opportuno:

Elenca i tuoi container per confermare che sia stato ricostruito:

Dovresti vedere un output simile:

Nel tuo browser, naviga verso l'indirizzo IP pubblico del tuo server e dovresti vedere la tua app in esecuzione.

Conclusione

Se hai seguito il tutorial fino a questo punto, ora hai un sito web statico realizzato con Express e Bootstrap, e distribuito con Docker. Hai utilizzato i file del sito web statico per creare un'immagine Docker e hai utilizzato l'immagine per creare un container. Hai quindi inviato l'immagine a un registro di immagini Docker, Docker Hub, rendendola disponibile per un uso futuro o per la scalabilità. Per testare l'uso del registro delle immagini, hai eliminato le immagini e i container, hai scaricato le immagini dal registro e hai ricostruito i container.

Questo tutorial ha spiegato come distribuire un'app Node.js. Se desideri imparare a utilizzare uno stack di sviluppo web diverso, abbiamo un tutorial su Come distribuire un'app Laravel con Docker Compose su Nginx.

Per ulteriori risorse sull'utilizzo di Docker, consulta i seguenti tutorial:

Buon computing!

author

Hark Labs

Autore · CloudSigma

Preslav Dobrev è un designer creativo presso CloudSigma, con un focus su un'identità aziendale coerente attraverso l'uso di canali di marketing tradizionali e innovativi. È abile nel fondere la visione artistica con il marketing strategico per creare narrazioni di brand di grande impatto.

Commenti

Ancora nessun commento. Scrivi il primo.