Introducción
Este tutorial te guiará a través de la creación de una imagen de aplicación para un sitio web estático que utiliza el framework Express y Bootstrap. Luego compilarás un contenedor con esa imagen y lo subirás a Docker Hub. Finalmente lo usarás para construir otro contenedor, demostrando cómo puedes recrear y escalar tu aplicación.
Prerrequisitos
Para seguir este tutorial, necesitarás:
- Un usuario sudo en tu servidor o en tu entorno local.
- Docker.
- Node.js y npm.
- Una cuenta de Docker Hub.
Paso 1: instalación de las dependencias de tu aplicación
Primero, debes crear un directorio para tu proyecto en el directorio de inicio de tu usuario no root:
1 |
$ mkdir node_project |
Navega a este directorio:
1 |
$ cd node_project |
Este será el directorio raíz del proyecto.
A continuación, crea un package.json con las dependencias de tu proyecto:
1 |
$ nano package.json |
Agrega la siguiente información sobre el proyecto al archivo; asegúrate de reemplazar la información del autor con tu propio nombre y datos de contacto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
~/node_project/package.json { "name": "nodejs-image-demo", "version": "1.0.0", "description": "nodejs image demo", "author": "Cesar the Shark <cesar@example.com>", "license": "MIT", "main": "app.js", "scripts": { "start": "node app.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "nodejs", "bootstrap", "express" ], "dependencies": { "express": "^4.16.4" } } |
Debes instalar las dependencias de tu proyecto:
1 |
$ npm install |
Paso 2: creación de los archivos de la aplicación
Crearemos un sitio web que ofrezca a los usuarios información sobre tiburones.
Debes abrir app.js en el directorio principal del proyecto para definir las rutas del proyecto:
1 |
$ nano app.js |
Agrega el siguiente contenido al archivo para crear la aplicación Express y los objetos Router, define el directorio base, el puerto y el host como variables. Además, configura las rutas y debes montar el router middleware junto con los activos estáticos de la aplicación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
~/node_project/app.js var express = require("express"); var app = express(); var router = express.Router(); var path = __dirname + '/views/'; // Constants const PORT = 8080; const HOST = '0.0.0.0'; router.use(function (req,res,next) { console.log("/" + req.method); next(); }); router.get("/",function(req,res){ res.sendFile(path + "index.html"); }); router.get("/sharks",function(req,res){ res.sendFile(path + "sharks.html"); }); app.use(express.static(path)); app.use("/", router); app.listen(8080, function () { console.log('Example app listening on port 8080!') }) |
A continuación, agregaremos contenido estático a la aplicación. Debes crear el directorio views:
1 |
$ mkdir views |
Debes abrir index.html:
1 |
$ nano views/index.html |
Agrega el siguiente código al archivo, que importará Boostrap y creará un componente jumbotron con un enlace a la página sharks.html de información más detallada:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
~/node_project/views/index.html <!DOCTYPE html> <html lang="en"> <head> <title>About Sharks</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link href="css/styles.css" rel="stylesheet"> <link href='https://fonts.googleapis.com/css?family=Merriweather:400,700' rel='stylesheet' type='text/css'> <a href="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js">https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js</a> <a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js</a> </head> <body> <nav class="navbar navbar-inverse navbar-static-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Everything Sharks</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav mr-auto"> <li class="active"><a href="/">Home</a></li> <li><a href="/sharks">Sharks</a></li> </ul> </div> </div> </nav> <div class="jumbotron"> <div class="container"> <h1>Want to Learn About Sharks?</h1> <p>Are you ready to learn about sharks?</p> <br> <p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a></p> </div> </div> <div class="container"> <div class="row"> <div class="col-md-6"> <h3>Not all sharks are alike</h3> <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.</p> </div> <div class="col-md-6"> <h3>Sharks are ancient</h3> <p>There is evidence to suggest that sharks lived up to 400 million years ago.</p> </div> </div> </div> </body> </html> |
A continuación, abre un archivo llamado sharks.html:
1 |
$ nano views/sharks.html |
Agrega el siguiente código, que importa Bootstrap y la hoja de estilo personalizada y ofrece a los usuarios información detallada sobre ciertos tiburones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
~/node_project/views/sharks.html <!DOCTYPE html> <html lang="en"> <head> <title>About Sharks</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link href="css/styles.css" rel="stylesheet"> <link href='https://fonts.googleapis.com/css?family=Merriweather:400,700' rel='stylesheet' type='text/css'> <a href="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js">https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js</a> <a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js</a> </head> <nav class="navbar navbar-inverse navbar-static-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Everything Sharks</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav mr-auto"> <li><a href="/">Home</a></li> <li class="active"><a href="/sharks">Sharks</a></li> </ul> </div> </div> </nav> <div class="jumbotron text-center"> <h1>Shark Info</h1> </div> <div class="container"> <div class="row"> <div class="col-md-6"> <p> <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.</div> <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark"> </p> </div> <div class="col-md-6"> <p> <div class="caption">Other sharks are known to be friendly and welcoming!</div> <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark"> </p> </div> </div> </div> </body> </html> |
Finalmente, debes crear la hoja de estilo CSS personalizada a la que se ha vinculado index.html y sharks.html. Primero debes crear una carpeta css en el directorio views:
1 |
$ mkdir views/css |
Abre la hoja de estilo y agrega el siguiente código, que establecerá el color y la fuente deseada para nuestras páginas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/node_project/views/css/styles.css .navbar { margin-bottom: 0; } body { background: #020A1B; color: #ffffff; font-family: 'Merriweather', sans-serif; } h1, h2 { font-weight: bold; } p { font-size: 16px; color: #ffffff; } .jumbotron { background: #0048CD; color: white; text-align: center; } .jumbotron p { color: white; font-size: 26px; } .btn-primary { color: #fff; text-color: #000000; border-color: white; margin-bottom: 5px; } img, video, audio { margin-top: 20px; max-width: 80%; } div.caption: { float: left; clear: both; } |
Inicia la aplicación:
1 |
$ npm start |
Dirígete en tu navegador hacia http://your_server_ip:8080 o si estás trabajando localmente a localhost:8080. Verás la siguiente página de destino:
Haz clic en el botón Get Shark Info. Verás la siguiente página de información:
Ahora tienes una aplicación en funcionamiento. Cuando estés listo, puedes salir del servidor escribiendo CTRL+C.
Paso 3 – Escribir el Dockerfile
En el directorio raíz de tu proyecto, debes crear el Dockerfile:
1 |
$ nano Dockerfile |
Debes agregar el siguiente código al archivo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
~/node_project/Dockerfile FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8080 CMD [ "node", "app.js" ] |
Este Dockerfile utiliza una imagen base de Alpine. Esto garantiza que los archivos de la aplicación sean propiedad del usuario del node no root que proporciona de manera predeterminada la imagen del Nodo Docker.
A continuación, agrega tus módulos de node local, registros npm, Dockerfile y .dockerignore a tu archivo .dockerignore:
1 2 3 4 5 |
~/node_project/.dockerignore node_modules npm-debug.log Dockerfile .dockerignore |
Crea la imagen de la aplicación con el comando docker build:
1 |
docker build -t your_dockerhub_username/nodejs-image-demo . |
El . especifica que el contexto de creación es el directorio actual.
Revisa tus imágenes:
1 |
$ docker images |
Verás el siguiente resultado:
1 2 3 4 |
Output REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 895MB node 10 f09e7c96b6de 17 hours ago 893MB |
Ejecuta el siguiente comando para crear un contenedor con esta imagen:
1 |
$ docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo |
Debes inspeccionar la lista de tus contenedores en ejecución con docker ps:
1 |
$ docker ps |
Verás el siguiente resultado:
1 2 3 |
Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "npm start" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo |
Con tu contenedor en ejecución, ahora puedes visitar tu aplicación en tu navegador en http://ip_de_tu_servidor o localhost. Verás la página de destino de su aplicación una vez más.
Ahora que has creado una imagen para tu aplicación, puedes enviarla a Docker Hub para su uso futuro.
Paso 4: uso de un repositorio para trabajar con imágenes
El primer paso para subir la imagen es iniciar sesión en tu cuenta de Docker Hub:
1 |
docker login -u tu_usuario_dockerhub -p tu_contraseña_dockerhub |
Iniciar sesión de esta manera creará un archivo ~/.docker/config.json en el directorio de inicio de tu usuario con tus credenciales de Docker Hub.
Sube tu imagen utilizando tu propio nombre de usuario en lugar de your_dockerhub_username:
1 |
$ docker push your_dockerhub_username/nodejs-image-demo |
Si lo deseas, puedes probar la utilidad del registro de imágenes destruyendo la imagen y el contenedor de la aplicación actual y reconstruyéndolos.
Primero, enumera tus contenedores en ejecución:
1 |
$ docker ps |
Verás el siguiente resultado:
1 2 3 |
Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "npm start" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo |
Usando el CONTAINER ID listado en tu salida, detén el contenedor de la aplicación en ejecución. Asegúrate de reemplazar el ID resaltado a continuación con tu propio CONTAINER ID:
1 |
$ docker stop e50ad27074a7 |
Puedes incluir todas tus imágenes con el indicador –a:
1 |
$ docker images -a |
Verás el siguiente resultado con el nombre de tu imagen, tu_usuario_dockerhub/nodejs-image-demo junto con la imagen de node y las otras imágenes de tu compilación:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Output REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 895MB <none> <none> e039d1b9a6a0 7 minutes ago 895MB <none> <none> dfa98908c5d1 7 minutes ago 895MB <none> <none> b9a714435a86 7 minutes ago 895MB <none> <none> 51de3ed7e944 7 minutes ago 895MB <none> <none> 5228d6c3b480 7 minutes ago 895MB <none> <none> 833b622e5492 8 minutes ago 893MB <none> <none> 5c47cc4725f1 8 minutes ago 893MB <none> <none> 5386324d89fb 8 minutes ago 893MB <none> <none> 631661025e2d 8 minutes ago 893MB node 10 f09e7c96b6de 17 hours ago 893MB |
Elimina el contenedor detenido y todas las imágenes, incluidas las imágenes no utilizadas o colgantes, con el siguiente comando:
1 |
$ docker system prune -a |
Con todas tus imágenes y contenedores eliminados, ahora puedes extraer la imagen de la aplicación de Docker Hub:
1 |
$ docker pull tu_usuario_dockerhub/nodejs-image-demo |
Lista tus imágenes una vez más:
1 |
$ docker images |
Verás la imagen de tu aplicación:
1 2 3 |
Output REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 895MB |
Ahora puedes reconstruir tu contenedor utilizando el comando del Paso 3:
1 |
docker run --name nodejs-image-demo -p 80:8080 -d tu_usuario_dockerhub/nodejs-image-demo |
Ahora, enumera tus contenedores en ejecución:
1 2 3 4 5 |
$ docker ps Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6bc2f50dff6 your_dockerhub_username/nodejs-image-demo "npm start" 4 |
Debes visitar http://ip_de_tu_servidor o localhost una vez más para ver tu aplicación en ejecución.