Terug naar blog

Hoe een Node.js (Express.js) app te deployen met Docker op Ubuntu 20.04

Hoe een Node.js (Express.js) app te deployen met Docker op Ubuntu 20.04

Inleiding

Docker is een containerplatform dat een lichtgewicht, gevirtualiseerde, draagbare, softwaregedefinieerde gestandaardiseerde omgeving is. Het stelt de software in staat om geïsoleerd te draaien van andere software die op de fysieke hostmachine draait. Docker is een bepalend onderdeel van het Continuous Development en Integration-aspect van softwareontwikkeling. Het biedt een lichtgewicht alternatief voor virtuele machines en stelt ontwikkelaars in staat om te genieten van gedistribueerde applicatiearchitecturen. Voor een grondig overzicht van het Docker-ecosysteem, bekijk dit artikel.

Het proces van het bouwen van een applicatie met Docker begint met een ontwikkelaar die een image voor hun applicatie maakt. Vervolgens wordt de image geïmplementeerd in een container. De image bevat bepalende componenten van een applicatie, zoals de applicatiecode, bibliotheken, configuratiebestanden, omgevingsvariabelen en de runtime-omgeving. De image standaardiseert de omgeving binnen een container, wat containerisatie de draagbaarheidskarakteristieken geeft. Node.js is een open-source, cross-platform backend JavaScript-runtime-omgeving die JavaScript-code buiten een webbrowser kan uitvoeren. Het is gebouwd op de V8 JavaScript Engine. Express.js is een minimalistisch backend JavaScript-framework dat bovenop Node.js draait.

In deze handleiding maken we een image voor een website die op het Express-framework draait. We gebruiken Bootstrap, een frontend-bibliotheek, om de frontend er beter uit te laten zien. Zodra we de image hebben gemaakt, bouwen we een container en pushen we deze naar Docker Hub. Met Docker Hub kunnen ontwikkelaars gecontaineriseerde applicaties hosten voor eenvoudige implementatie in elke Docker-omgeving. Zodra je container op Docker Hub wordt gehost, zullen we deze pullen en een andere image bouwen die daadwerkelijk onze website zal serveren.

Vereisten

Dit wordt een praktische handleiding. Je moet een omgeving creëren waarin je de stappen kunt volgen.

Stap 1: Applicatie-afhankelijkheden configureren

Je moet de broncode van je applicatie maken voordat je de image kunt maken. De broncode van de applicatie bevat code, statische inhoud en afhankelijkheden die naar de container worden gekopieerd. Begin met het maken van een map voor je project in de thuismap van de non-root gebruiker. We noemen deze node_express, maar het staat je vrij om een mapnaam te gebruiken die je zelf wilt:

Ga vervolgens naar deze map:

Dit wordt de hoofdmap van je applicatie. Een node.js applicatie verwacht een package.json bestand in de hoofdmap. Npm gebruikt dit bestand om te bepalen welke afhankelijkheden je applicatie nodig heeft. Voer de volgende opdracht in om dit bestand te maken:

Voeg daarna het volgende codefragment toe aan het bestand. Je kunt de naam, auteur, beschrijving en het startbestand naar wens aanpassen:

Zoals je kunt zien, specificeert dit bestand de projectnaam, versie, auteur en licentie waaronder de applicatiecode zal worden gedeeld. Het wordt aanbevolen om een korte en beschrijvende naam voor je project te gebruiken om duplicaten te voorkomen in de npm registry. We hebben de ISC-licentie voor het project gespecificeerd, die het gratis kopiëren, wijzigen of distribueren van de applicatiecode toestaat.

Het belangrijkste is dat je let op de volgende richtlijnen in het bestand:

  • main”: deze richtlijn specificeert het startpunt van de applicatie, dat we hebben ingesteld als index.js. We zullen dit bestand binnenkort aanmaken.
  • dependencies”: deze richtlijn specificeert de applicatie-afhankelijkheden die uit de npm registry worden opgehaald wanneer we het npm-commando uitvoeren, in ons geval willen we Express-versie 4.17.1 en hoger.

Je kunt het bestand nu opslaan door op Ctrl + O te drukken. Sluit het bestand vervolgens door op Ctrl + X te drukken. Vervolgens installeren we de afhankelijkheden door het volgende commando uit te voeren:

Het commando installeert de applicatie-afhankelijkheden die zijn gespecificeerd in het package.json bestand in de node_modules mappen. Deze zijn automatisch aangemaakt toen je het commando voor het eerst uitvoerde. Nu de applicatie-afhankelijkheden zijn geïnstalleerd, kun je beginnen met het toevoegen van de applicatiecode.

Stap 2: Je applicatiecodebestanden toevoegen

We gaan een eenvoudige receptenwebsite maken, met dank aan allrecipes. Het belangrijkste startpunt voor de applicatie is het index.js bestand. We voegen een views map toe die de verschillende pagina's en statische bestanden van het project zal bevatten. De website krijgt een landingspagina met inleidende informatie en links naar enkele recepten.

De code voor onze landingspagina wordt geplaatst in het home.html bestand. Maak eerst het index.js bestand aan door het volgende commando in te voeren:

Voeg de volgende code toe, die een Express-applicatie importeert en aanmaakt. Het specificeert ook het Router-object, de basismap en de poort waarop deze app wordt aangeboden:

require is een JavaScript-functie die een module laadt. In dit geval laden we de express-module. Vervolgens gebruiken we de geïmporteerde module om de express- en router-objecten te maken. Het router-object voert de routeringsfuncties van de app uit door te reageren op HTTP-methode-aanroepen die we in de loop van de tutorial aan dit object zullen toevoegen.

We hebben ook path en port. De constante path definieert de basismap voor de code. In ons geval is dat de views submap binnen de hoofdmap van het project. De port specificeert de poort waarop de express-app moet luisteren, in ons voorbeeld hebben we deze ingesteld op 8090.

Zodra we de constanten hebben, kunnen we enkele routes voor de applicatie specificeren met behulp van het router object. Voeg de volgende code toe aan het index.js bestand om de routes te specificeren:

Je kunt middleware toevoegen aan routes met behulp van de router.use functie. In dit geval voegen we een functie toe die de verzoeken van de router logt voordat ze worden doorgegeven aan de applicatieroutes. Een GET verzoek naar de basis van de applicatie zal een home.html pagina. Vervolgens hebben we pagina's toegevoegd voor drie recepten die ook zullen worden opgehaald met behulp van het GET-verzoek naar de specifieke receptpagina.

Voeg ten slotte de volgende code toe om de router middleware en de statische bestanden van de applicatie te koppelen. Geef daarnaast de express-applicatie de opdracht om te luisteren op poort 8090:

Je complete index.js-bestand zou er als volgt uit moeten zien:

Je kunt het bestand nu opslaan en sluiten. De volgende stap is het toevoegen van de statische webpagina's aan de views-map. Begin met het invoeren van de volgende opdracht om de map aan te maken:

Voer de volgende opdracht in om het home.html-landingspaginabestand te openen:

Voeg de volgende code toe aan het bestand. De code importeert Bootstrap en biedt websitebezoekers wat informatie over waar de website over gaat:

Naast het importeren van Bootstrap voegt de pagina ook een eenvoudig navigatiemenu toe om ons te helpen door de pagina's te navigeren en terug te keren naar de landingspagina. We hebben ook een regel toegevoegd om ons aangepaste CSS-bestand te importeren:

We zullen dit bestand later gebruiken om aangepaste styling aan de applicatie toe te voegen. Laten we nu de drie pagina's voor de recepten maken. We beginnen eerst met het maken van de lasagne-pagina. Open het bestand met de nano-editor met behulp van de volgende opdracht:

Voeg de volgende code toe aan het geopende bestand. Dit bestand importeert Bootstrap, het custom.css-bestand, specificeert een navigatiemenu en biedt wat informatie over het lasagnerecept:

Laten we hetzelfde proces volgen om een bestand te maken voor de guacamole receptpagina. Open het bestand met nano door de volgende opdracht uit te voeren:

Voeg vervolgens deze code toe aan het bestand:

Laten we tot slot het bestand banana_bread.html maken door de volgende opdracht in te voeren:

Voeg vervolgens de volgende HTML-code toe aan het bestand:

Nu hebben we alle pagina's gemaakt. Als je het nog weet, moeten we het css/custom.css-bestand toevoegen. Voer de volgende opdracht uit om de map aan te maken:

Maak vervolgens het bestand aan en open het in de nano-editor met de opdracht:

Je kunt meer CSS-code toevoegen om je website naar wens op te maken. Laten we voor de kortheid het volgende codefragment aan het bestand toevoegen:

Sla het bestand op en sluit het als je klaar bent.

Je kunt de applicatie starten omdat we nu de broncode van de applicatie en de projectafhankelijkheden hebben geïnstalleerd.

We had set the app to listen on a port 8090, voer de volgende opdracht uit om de firewall opdracht te geven verkeer via deze poort toe te staan. Als je een andere poort had opgegeven, vervang dan het poortnummer in de opdracht:

Nu kun je de applicatie starten. Maar zorg er eerst voor dat je je in de hoofdmap van het project bevindt door de volgende opdracht uit te voeren:

Start de applicatie met node index.js. Als je een ander startpunt hebt opgegeven, vervang dit dan door jouw startpunt:

Als je met je browser navigeert naar http://your_public_server_ip:8090, zie je de bestemmingspagina van Recepten zoals gedefinieerd:

Recipes

 

Je kunt de links naar de verschillende recepten in de navigatie zien. Laten we op een paar klikken. Hieronder hebben we de Lasagne receptpagina:

Node.js app install on Ubuntu 1

And hier hebben we de Guacamole receptpagina:

Guacamole

Tot zover heb je je applicatie gemaakt en getest of deze naar verwachting werkt. Je kunt de server afsluiten door op Ctrl + C te drukken en doorgaan met het maken van de Dockerfile. Dockerfiles helpen bij de schaalbaarheid door het mogelijk te maken om een instantie van een applicatie opnieuw te maken wanneer dat nodig is.

Stap 3: De Dockerfile maken

Docker leest de instructies die zijn gespecificeerd in een Dockerfile bij het bouwen van images. Het specificeert de runtime-omgeving van een applicatie. Hierdoor helpt het ontwikkelaars discrepanties met afhankelijkheden of veranderende runtime-versies te voorkomen. Voer de volgende opdracht in om de Dockerfile aan te maken:

Een Docker-image wordt gemaakt met behulp van verschillende lagen van images die op elkaar voortbouwen. Je begint met het toevoegen van een base-image om het startpunt voor de app te vormen.

Aangezien de applicatie verwacht te draaien in een node.js-omgeving, beginnen we met het toevoegen van de node:10-alpine-image voor node.js. Momenteel, op het moment dat we deze handleiding schrijven, is dit de aanbevolen LTS-versie van Node.js. We hebben voor deze specifieke image gekozen omdat deze is afgeleid van het Alpine Linux-project. Hierdoor helpt het om onze image-grootte tot een minimum te beperken. Er zijn verschillende image-varianten onder de Docker Hub Node images-pagina waaruit je kunt kiezen, afhankelijk van je behoeften.

Voeg de volgende code toe om de base-image van de applicatie in te stellen met behulp van de FROM-instructie:

Deze image bevat Node.js en npm. Elke Dockerfile moet beginnen met een FROM-instructie. De Docker node-image wordt standaard geleverd met een niet-root node-gebruiker die je kunt gebruiken om je applicatiecontainer als root uit te voeren. Docker-beveiliging raadt aan om de containers niet als root uit te voeren en de privileges te beperken tot alleen die welke vereist zijn om de bronnen uit te voeren.

In dat geval gebruiken we de home-directory van de node-gebruiker als de werkdirectory voor de applicatie en als de gebruiker binnen de container. Je kunt deze Docker Node image best practices-handleiding raadplegen voor meer informatie.

We maken de node_modules subdirectory aan binnen /home/node samen met de app-directory om de machtigingen voor de applicatiecode te stroomlijnen. Het aanmaken van deze directories zorgt ervoor dat ze de juiste machtigingen hebben wanneer we de npm install-opdracht lokaal binnen de containers uitvoeren. Zodra je de directories hebt aangemaakt, moet je het eigendom ervan instellen op de node-gebruiker. We doen dit binnen de Dockerfile door de volgende regel toe te voegen:

Vervolgens stel je de werkdirectory in door de volgende regel toe te voegen:

Het is een goed idee om altijd de WORKDIR in te stellen, zodat Docker er niet standaard een hoeft aan te maken.

Voeg de volgende regel toe om de package.json and package-lock.json-bestanden te kopiëren:

Het wordt aanbevolen om de COPY-instructie toe te voegen voordat je npm install of de broncode van de applicatie kopieert. Hiermee kun je profiteren van het caching-mechanisme van Docker. Tijdens het bouwproces controleert Docker of er voor elke instructie een gecachte laag is. Dit betekent dat als je het package.json-bestand niet hebt gewijzigd, Docker de bestaande image-laag zal gebruiken en het opnieuw installeren van node-modules vermijdt, wat zorgt voor snellere bouwprocessen.

Voordat je npm install uitvoert, voeg je de volgende regel toe om de gebruiker over te schakelen naar node om ervoor te zorgen dat alle applicatiebestanden en de node_modules-directory eigendom zijn van de niet-root node-gebruiker:

Onze container is nu klaar om de npm install-opdracht uit te voeren. Voeg de volgende regel toe aan de Dockerfile:

Zodra node_modules zijn geïnstalleerd, voeg je de volgende regel toe die Docker vertelt om de applicatiecode naar de applicatiedirectory op de container te kopiëren met de juiste machtigingen en eigendom, d.w.z. de niet-root node-gebruiker:

De laatste stap is het blootstellen van de poort 8090 op de container, zoals we hadden gedefinieerd in ons startbestand index.js:

EXPOSE stelt in welke poorten op de container open zullen zijn tijdens runtime. CMD voert de opdracht uit om de applicatie te starten, in dit geval node index.js.

Je mag maar één CMD-instructie in de Dockerfile hebben, aangezien alleen de laatste effect heeft. Bekijk de Dockerfile-referentiedocumentatie voor een lijst met dingen die je kunt doen met Dockerfile.

Je complete Dockerfile zou er zo uit moeten zien:

Je kunt het bestand nu opslaan en sluiten.

Het volgende dat je doet is het toevoegen van het .dockerignore-bestand. Net als het .gitignore-bestand, specificeert het .dockerignore welke bestanden en mappen binnen de projectmap niet naar de container gekopieerd mogen worden.

Open het bestand met de nano-editor:

Voeg de volgende regels toe aan het bestand:

Als je met een git-repo werkt, moet je ook de .git-map en het .gitignore-bestand toevoegen. Sla het bestand op en sluit het.

Als alles goed is gegaan, is het tijd om de applicatie-image te bouwen met het docker build-commando. Je kunt de –t-vlag toevoegen aan het docker build-commando om de image te taggen met een herkenbare naam in plaats van de willekeurige tekenreeks die Docker standaard instelt. We zullen de image ook naar Docker Hub pushen, dus het is het beste om je Docker Hub-gebruikersnaam in de tag op te nemen.

We zullen nodejs-express-image als tagnaam gebruiken. Je bent vrij om een tagnaam te kiezen die je leuk vindt. Hier is het commando om de image te bouwen:

Vergeet niet om your_dockerhub_username te vervangen door je daadwerkelijke Docker Hub-gebruikersnaam. De . (punt) aan het einde geeft aan dat de build-context de huidige map is.

Het bouwproces duurt een minuut of twee. Zodra het klaar is, voer je het commando in om je images te controleren:

Je zou zoiets als dit moeten zien:

sudo docker images

Onthoud dat we your_dockerhub_username hebben vervangen door een daadwerkelijke gebruikersnaam.

Na het bevestigen dat je image is gebouwd, kun je nu een container maken met de image met behulp van docker run. De volgende vlaggen zullen worden opgenomen:

  • -p: publiceert de poort op de container en koppelt deze aan een poort op het hostsysteem. We zullen poort 80 op het hostsysteem gebruiken voor demonstratiedoeleinden. Als je echter een ander proces op die poort hebt draaien, kun je dit desgewenst aanpassen. Lees meer over poortkoppeling in de Docker-documentatie.
  • -d: voor de losgekoppelde (detached) modus. Hiermee kan de container op de achtergrond blijven draaien.
  • --name: dit kun je gebruiken om een herkenbare naam in te stellen in plaats van Docker een willekeurige tekenreeks te laten toewijzen.

Het commando om de container te bouwen is als volgt. Vervang je Docker Hub-gebruikersnaam op de juiste manier:

Wacht tot de container is gebouwd en begint te draaien. Je kunt dit commando gebruiken om alle actieve containers te inspecteren:

Je zou een uitvoer moeten zien die lijkt op de volgende:

Node.js app install on Ubuntu 3

Zoals te zien is in de uitvoer, draait de container nu. Je kunt deze in de browser bekijken als je het openbare IP-adres van je server bezoekt zonder de poort in de browser. Je startpagina zal laden:

awesome recipe

 

Je hebt met succes een statische Node Express-website geïmplementeerd met Docker. Laten we eens kijken hoe we deze image naar Docker Hub kunnen pushen voor toekomstig gebruik en schaalbaarheidsdoeleinden.

Stap 4: Werken met Docker Image Repositories

Je kunt je images pushen naar image-registries zoals Docker Hub en ze opslaan voor toekomstig gebruik, ze delen met andere ontwikkelaars of het schalen van je containers mogelijk maken. We kunnen de image die we hebben gemaakt naar Docker Hub pushen en deze gebruiken om een container opnieuw te maken.

Gebruik de volgende opdracht om in te loggen op je Docker Hub-account. Vervang deze door je daadwerkelijke Docker Hub-gebruikersnaam:

Voer je wachtwoord in wanneer daarom wordt gevraagd. Zodra je bent ingelogd, wordt er een ~/.docker/config.json -bestand aangemaakt in de thuismap van je gebruiker met daarin je Docker Hub-inloggegevens.

Als dat is ingesteld, voer je de volgende opdracht in om de image naar Docker Hub te pushen, waarbij je de tag opgeeft die je eerder bij het bouwen van de image hebt ingesteld:

Deze opdracht pusht de docker-image naar je Docker Hub-account. Als je je account bezoekt, kun je je onlangs gepushte image zien:

Docker Hub

We kunnen het nut van de image-repository testen door de huidige applicatiecontainer te vernietigen en deze opnieuw op te bouwen met behulp van de image in de repository.

Toon een lijst van je huidige containers door de volgende opdracht in te voeren:

Je zou een uitvoer moeten zien die vergelijkbaar is met deze:

Docker Hub

Noteer de CONTAINER ID die in je uitvoer wordt vermeld, kopieer deze en gebruik deze om je container te stoppen met de opdracht, waarbij je de ID vervangt door die van jou:

Voer de volgende opdracht in om alle docker-images weer te geven die beschikbaar zijn op je systeem:

De uitvoer toont de naam van je image, de node.js-image en andere images van het bouwproces.

Voer de volgende opdracht in om de images te verwijderen, inclusief ongebruikte of zwevende (dangling) images:

Typ y om te bevestigen. Dit verwijdert de gestopte containers en images. Als je ze opvraagt, zie je een lege lijst in de uitvoer:

output

Nu heb je zowel de container waarin de applicatie draait als de image zelf verwijderd. Leer meer over het verwijderen van Docker-containers, -images en -volumes door onze handleiding te volgen.

We kunnen nu het hele proces opnieuw doorlopen door eerst de image van Docker Hub op te halen met de volgende opdracht. Vervang je Docker Hub-gebruikersnaam op de juiste manier:

Toon je Docker-images opnieuw met de opdracht:

Je zou de image in de uitvoer moeten zien:

sudo docker

Je kunt nu je container opnieuw bouwen met behulp van de opdracht uit Stap 3. Of course, replace your Docker Hub username where appropriate:

Toon een lijst van je containers om te bevestigen dat deze opnieuw is opgebouwd:

Je zou een vergelijkbare uitvoer moeten zien:

Navigeer in je browser naar het openbare IP-adres van je server en je zou je app moeten zien draaien.

Conclusie

Als je de handleiding tot dit punt hebt gevolgd, heb je nu een statische website gemaakt met Express en Bootstrap, en geïmplementeerd met Docker. Je hebt de statische websitebestanden gebruikt om een Docker-image te bouwen en de image gebruikt om een container te maken. Vervolgens heb je de image gepusht naar een Docker-image-registry, Docker Hub, waardoor deze beschikbaar is voor toekomstig gebruik of schalen. Om het gebruik van de image-registry te testen, heb je de images en containers vernietigd, de images uit de registry opgehaald en de containers opnieuw opgebouwd.

In deze handleiding is uitgelegd hoe je een Node.js-app implementeert. Als je wilt leren hoe je een andere webontwikkelingsstack gebruikt, hebben we een handleiding over Een Laravel-app implementeren met Docker Compose op Nginx.

Raadpleeg de volgende handleidingen voor meer bronnen over het gebruik van Docker:

Veel computerplezier!

author

Hark Labs

Auteur · CloudSigma

Preslav Dobrev is een creatief ontwerper bij CloudSigma, met de nadruk op een consistente bedrijfsidentiteit door middel van traditionele en innovatieve marketingkanalen. Hij is bedreven in het samenvoegen van artistieke visie met strategische marketing om impactvolle merkverhalen te creëren.

Reacties

Nog geen reacties. Wees de eerste.