Docker como entorno de desarrollo local

En este art铆culo quiero compartir como implementar un entorno de desarrollo local en tu ordenador para tus aplicaciones empleando para ello Docker. Lo configuraremos de manera que funcione como un LiveReload, a cada cambio que hagamos en el c贸digo de nuestra aplicaci贸n, el contenedor lo refleje.

En otros art铆culos he comentado c贸mo crear contenedores de Docker en Mac OSX e incluso c贸mo desplegarlos en producci贸n en Digital Ocean.

Hoy quiero hablar de Docker como una alternativa a Vagrant como entorno de desarrollo y provisionamiento.

Docker ha evolucionado mucho en poco tiempo. En un principio solo era posible utilizarlo en un sistema Linux, debido a que utiliza la tecnolog铆a de contenedores del sistema operativo. Hace poco empez贸 a ser utilizable en sistemas Mac y Windows gracias la m谩quina virtual boot2docker. Y hoy en d铆a gracias a Docker Toolbox es posible utilizarlo en cualquier sistema.

Definiendo el entorno de desarrollo con Docker Compose

En el ejemplo de este art铆culo voy a utilizar Ubuntu 14.04 como sistema operativo base y una aplicaci贸n Node.js muy sencilla que ilustre lo que queremos hacer.

Primero de todo vamos a crear una carpeta para nuestro proyecto, en el directorio que prefieras de tu m谩quina local. En el vamos a incluir un fichero Dockerfile, un fichero docker-compose.yml y una carpeta para el c贸digo fuente de nuestra aplicaci贸n que puede ser de nombre app/.

Dentro de app/ vamos a crear un fichero package.json muy simple con el siguiente c贸digo:

{
  "name": "project",
  "scripts": {
    "start": "nodemon app.js"
  },
  "dependencies": {
    "express": "^4.x.x"
  },
  "devDependencies": {
    "nodemon": "*"
  }
}

Y un fichero con el c贸digo de la aplicaci贸n de nombre app.js con el siguiente contenido (de momento):

console.log("Hola Mundo!");

Ahora procedemos con el fichero de la imagen Docker, el Dockerfile, que tendr谩 el siguiene contenido:

FROM node:4.4.0
MAINTAINER cazaustre@gmail.com

WORKDIR /app
COPY app/ .

En 茅ste fichero b谩sicamente estamos diciendo que utilice la imagen de Node versi贸n 4.4.0 como base, y que se coloque en el directorio /app para trabajar all铆. Lo siguiente ser谩 que copie el contenido de la carpeta app local en el directorio actual del contenedor (que sera app por la instrucci贸n anterior)

Dejamos este fichero as铆 y continuamos con el fichero docker-compose.yml:

web:
  build: .
  command: 'npm start'
  volumes:
    - ./app:/app

Este fichero nos ayuda a reducir c贸digo del Dockerfile y de comandos a la hora de correr el contenedor. M谩s adelante tambi茅n nos servir谩 para defiir otros contenedores y enlazarlos.

Creamos un contenedor de nombre web que realiza lo siguiente:

  • Con build indicamos que imagen de Dockerfile tiene que utilizar. Con la notaci贸n . leer谩 el fichero Dockerfile de directorio principal (donde se encuentre el fichero docker-compose.yml)

  • Con command indicamos que comando tiene que ejecutarse al correr el contenedor. En este caso npm start que ejecuta el script start definido en el package.json (nodemon app.js)

  • Y volumes es la parte m谩s importante, ya que nos permite enlazar un directorio local con un directorio dentro del contenedor. En este caso, el directorio app donde est谩 definido el c贸digo de la aplicaci贸n, lo enlazamos con app dentro del contenedor. Este es el directorio de trabajo que ya definimos con WORKDIR en el fichero Dockerfile.

Para poner en marcha este workflow, primero ejecutamos $ docker-compose build para construir los contenedores:

root@ubuntu:/home/carlosazaustre/Development/project# docker-compose build
Building web
Step 1 : FROM node:4.4.0
 ---> 564c70a84ab7
Step 2 : MAINTAINER cazaustre@gmail.com
 ---> Using cache
 ---> 62f115911e67
Step 3 : WORKDIR /app
 ---> Using cache
 ---> eb7e7144e107
Step 4 : COPY app/ .
 ---> b151f82fbff3
Removing intermediate container 9afda7bedfff
Successfully built b151f82fbff3

Y despu茅s $ docker-compose up para ponerlo en marcha:

root@ubuntu:/home/carlosazaustre/Development/project# docker-compose up
Recreating project_web_1
Attaching to project_web_1
web_1 | npm info it worked if it ends with ok
web_1 | npm info using npm@2.14.20
web_1 | npm info using node@v4.4.0
web_1 | npm info prestart project@
web_1 | npm info start project@
web_1 | 
web_1 | > project@ start /app
web_1 | > nodemon app.js
web_1 | 
web_1 | [nodemon] 1.9.1
web_1 | [nodemon] to restart at any time, enter `rs`
web_1 | [nodemon] watching: *.*
web_1 | [nodemon] starting `node app.js`
web_1 | Hola Mundo!
web_1 | [nodemon] clean exit - waiting for changes before restart

Si todo sale bien, al final en el terminal tiene que aparecer el mensaje Hola Mundo! que es lo que hace el fichero app.js

Creando nuestra App Node.js

Ahora vamos a modificar un poco el fichero app.js para que cree un peque帽o servidor web con Express:

const express = require('express')
const app = express()
const port = 3000

app.set('port', port)

app.get('/', (req, res) => {
  res.send('Hola Mundo!')
})

app.listen(app.get('port'), (err) => {
  console.log(`Server running on port ${app.get('port')}`)
})

Y cambiamos el c贸digo del fichero docker-compose.yml para definir un par de cosas nuevas:

web:
  build: .
  command: sh -c 'npm install; npm start'
  ports: 
    - '3000:3000'
  volumes:
    - ./app:/app
  • El comando que ejecutaremos ahora es sh -c 'npm install; npm start' que lo que hace es abrir un terminal de shell en el contenedor y ejecutar los comandos npm install para instalar las dependencias definidas en el package.json y npm start para correr el script start.

  • Con ports exponemos fuera del contenedor el puerto que est谩 utilizando la aplicaci贸n, en este caso el 3000

Si ahora ejecutamos de nuevo $ docker-compose build y $ docker-compose up tendremos la siguiente salida

Recreating project_web_1
Attaching to project_web_1
web_1 | npm info it worked if it ends with ok
web_1 | npm info using npm@2.14.20
web_1 | npm info using node@v4.4.0
web_1 | npm info prestart project@
web_1 | npm info start project@
web_1 | 
web_1 | > project@ start /app
web_1 | > nodemon app.js
web_1 | 
web_1 | [nodemon] 1.9.1
web_1 | [nodemon] to restart at any time, enter `rs`
web_1 | [nodemon] watching: *.*
web_1 | [nodemon] starting `node app.js`
web_1 | Server running on port 3000

Como se puede ver, el servidor est谩 escuchando en el puerto 3000. Si abrimos un navegador en la direcci贸n http://localhost:3000 veremos lo siguiente.

La IP no ser谩 localhost si estamos en Mac o Windows, debido a que Docker necesita una m谩quina virtual linux para ejecutarse. En ese caso ser谩 la IP que nos proporcione la m谩quina virtual.

Para este ejemplo estoy usando Linux Ubuntu, por eso empleo localhost

Docker Hola Mundo

Realizando cambios en nuestra App

Como tenemos el contenedor corriendo, el volumen enlazado, y el script nodemon ejecut谩ndose, si hacemos cambios en el c贸digo, se ver谩n reflejados en el contenedor.

Vamos a cambiar el c贸digo de app.js para que a la ruta /:nombre nos muestre el nombre que pasamos en la URL:

const express = require('express')
const app = express()
const port = 3000

app.set('port', port)

app.get('/:nombre', (req, res) => {
  res.send(`Hola ${req.params.nombre}!`)
})

app.listen(app.get('port'), (err) => {
  console.log(`Server running on port ${app.get('port')}`)
})

En cuanto salvemos, la terminal mostrar谩 el siguiente mensaje:

...
web_1 | [nodemon] restarting due to changes...
web_1 | [nodemon] starting `node app.js`
web_1 | Server running on port 3000

Y si en el navegador cambiamos la URL a http://localhost:3000/Carlos tendremos lo siguiente:

Docker como entorno de desarrollo

De esta manera, tenemos un entorno de desarrollo definido gracias a Dockerfile, que luego podremos trasladar a producci贸n, pero a su vez lo podemos utilizar al mismo tiempo que vamos cambiando nuestro c贸digo como si el entorno estuviese instalado en nuestra m谩quina virtual.


Desarrollador web Frontend y apasionado de JavaScript. Aqu铆 te ense帽o todo lo que aprendo y conozco sobre JavaScript y la programaci贸n web en general.

驴Te gusta lo que lees?

Inv铆tame a un caf茅 virtual 鈽曪笍 馃槈

Buy me a coffeeBuy me a coffee