Docker y NodeJS, un ejemplo básico

Archivada en Desarrollo Web

Docker y NodeJS, un ejemplo básico

Lo primero que tenemos que hacer es instalar Docker, algo facilísimo en MacOS y en Linux (y seguramente en las versiones más recientes de Windows). Solo hay que seguir las instrucciones.

Una vez instalado podemos verificar la versión:

$ docker --version 
Docker version 17.09.0-ce, build afdb6d4

Y también podemos ejecutar el contenedor más pequeño posible:

 $ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9a0669468bf7: Pull complete
Digest: sha256:0e06ef5e1945a718b02a8c319e15bae44f47039005530bc617a5d071190ed3fc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

La aplicación

Para este ejemplo básico vamos a crear una aplicación de ReactJS que solo diga el clásico Hola Mundo, pero usa una configuración completa.

Lo primero es crear la carpeta, yo la llamé mern.

$ mkdir mern
$ cd mern

Yo empecé con npm init que crea un archivo package.json, este es el mío:

{
  "name": "mern",
  "version": "1.0.0",
  "description": "Pruebas con React",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "compile": "babel src --presets react,es2015 --out-dir static/js",
    "watch": "babel src --presets react,es2015 --out-dir static/js --watch",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "react",
    "mongo",
    "express",
    "node"
  ],
  "author": "Javier Sanchez",
  "license": "MIT",
  "dependencies": {
    "express": "^4.16.2"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1"
  }
}

Utiliza ExpressJS como única dependencia en producción y para desarrollo uso BabelJS y dos plugins, uno para transpilar (o traducir) el código de React y ES2015.

La orden de ejecución, arranca el servidor con el archivo índex.js que es muy simple:

const express = require('express')
const app = express()
const PORT = 3000;

app.use(express.static('static'))

app.listen(PORT, () => console.log(`Funcionando en el puerto ${PORT}`))

La aplicación reside en el directorio src y solo es un componente que dice Hola Mundo:

const nodoContenido = document.getElementById('contenido')

class HolaMundo extends React.Component {
  render() {
    return (
      <h1>Hola Mundo!</h1>
    )
  }
}
ReactDOM.render(<HolaMundo />, nodoContenido)

Como vemos en package.json necesita compilarse, lo que hacemos con la orden npm run compile que ejecuta el comando:

$ babel src --presets react,es2015 --out-dir static/js

y una vez compilada la aplicación podemos ejecutar el servidor con

npm start

Ahora si, estamos listos para dockerizar nuestra aplicación.

El contenedor base

Lo primero que elegimos es el contenedor base.

Alpine Linux es una distribución muy ligera, que incluso puede ejecutarse en RAM. No utiliza las librerías estándar de C (las glibc) lo que puede ocasionar algunos problemas, pero no en este ejemplo.

Como yo voy a usar este contenedor base en otros proyectos, voy a descargarla para tenerla disponible.

$ docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
Digest: sha256:d6bfc3baf615dc9618209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478
Status: Image is up to date for alpine:latest

Dockerfile con NodeJS para Alpine

Ahora, el contenedor para nuestra aplicación es la variante alpine y la versión que vamos a utilizar es la mas nueva, es decir la 9.01. Para esto vamos a crear un archivo Dockerfile que contiene las siguientes líneas.

Primero instalamos el contenedor de NodeJS.

FROM node:alpine

Ahora vamos a crear un directorio de trabajo dentro del contenedor, al que llamaremos /app.

WORKDIR /app

Luego copiamos los archivos al directorio de trabajo.

ADD . /app

Después, instalamos los paquetes y compilamos el programa.

RUN npm install
RUN npm run compile

Tenemos que abrir el puerto 3000 para que sea visible en el exterior.

EXPOSE 3000

Y por último ejecutamos nuestra aplicación, con el script indicado en el archivo package.json.

CMD ["npm", "start"]

Este es el archivo completo:

FROM node:alpine

WORKDIR /app

ADD . /app

RUN npm install
RUN npm run compile

EXPOSE 3000
CMD ["npm", "start"]

Construir y ejecutar un contenedor.

Ahora toca el turno de construir el contenedor, que hacemos con la orden build. Además, para identificarlo le agregamos una etiqueta con la marca -t de tag.

$ docker build -t mern .
Sending build context to Docker daemon  113.7kB
Step 1/7 : FROM node:alpine
 ---> d06e52d14cac
Step 2/7 : WORKDIR /app
 ---> Using cache
 ---> 8235cc38124f
Step 3/7 : ADD . /app
 ---> 2a89296671af
Step 4/7 : RUN npm install
 ---> Running in 480a4e340ed9
npm WARN mern@1.0.0 No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.1.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

added 327 packages in 463.458s
 ---> 4365094b5241
Removing intermediate container 480a4e340ed9
Step 5/7 : RUN npm run compile
 ---> Running in 1e4b8eb87c21

> mern@1.0.0 compile /app
> babel src --presets react,es2015 --out-dir static/js

src/app.jsx -> static/js/app.js
 ---> e6226251f8dd
Removing intermediate container 1e4b8eb87c21
Step 6/7 : EXPOSE 3000
 ---> Running in 0c2f78b34720
 ---> a9888504fbaa
Removing intermediate container 0c2f78b34720
Step 7/7 : CMD npm start
 ---> Running in dab2e748794a
 ---> 19bd1377caf6
Removing intermediate container dab2e748794a
Successfully built 19bd1377caf6
Successfully tagged mern:latest

Ahora estamos listos para ejecutar el contenedor con la orden run y mapeamos el puerto 3000.

$ docker run -p 3000:3000 mern

> mern@1.0.0 start /app
> node index.js

Funcionando en el puerto 3000

Y verificamos el funcionamiento de nuestro servidor dockerizado visitando la página http://localhost:3000/.

En el siguiente artículo, veremos algunos comando útiles para administrar los contenedores, las imagines así como crear un entorno de desarrollo usando Docker.


  1. En un siguiente artículo vamos a crear un entorno de desarrollo con Docker y usaremos un contenedor con la versión LTS de NodeJS y una imagen de Ubuntu 16.04. 

Javier Sanchez Toledano

Auditor Líder ISO 9000 ● Desarrollo Web Full Stack ● Django/Python ● JavaScript · NodeJS · Angular · VueJS · EmberJS ● Swift 4 ● WordPress · Genesis Framework ● Lector ● Fotografo aficionado ● Generación X ● Soy de Tlaxcala

Tlaxcala, México

Comentarios