Utilizando contenedores Docker con Nodejs

Seguimos con los artículos dedicados al desarrollo de aplicaciones haciendo uso de javascript del lado del servidor. En este post vamos a ver cómo podemos aprovecharnos de la tecnología de contenedores Docker con NodeJs.

En un artículo anterior ya hice una comparativa entre Docker y Vagrant pero para el que venga de nuevas y antes de entrar en materia tenemos que conocer qué es Docker.

Según la Wikipedia:

Docker es un proyecto de código abierto que automatiza el despliegue de aplicaciones dentro de contenedores de software, proporcionando una capa adicional de abstracción y automatización de Virtualización a nivel de sistema operativo en Linux.

¿Eing?.. ¿Se te ha quedado cara de bobo tras leer eso? No te preocupes que voy a tratar de explicarte qué es Docker desde otra aproximación.

Imagina que puedes construir toda una aplicación funcional, con sus librerías, assets y dependencias de manera aislada.

Esta aplicación siempre te va a funcionar en cualquier ordenador en el que esté alojada. Ningún conflicto mas entre versiones ni librerías perdidas.

Olvídate de eso.

Tu metes tu aplicación en una cajita y te aseguras que dejes donde dejes esa cajita siempre va a funcionar.

Pues algo así es Docker y así es cómo nos ayuda.

Nos permite aislar los componentes de la aplicación del resto del sistema operativo asegurándonos que siempre va a funcionar.

Obviamente el uso de Docker está muy ligado tanto a entornos de desarrollo como a despliegues de aplicaciones, pero ahora que ya tienes una idea aproximada de qué son estos contenedores vamos a ver cómo podemos usar Docker con Nodejs.

Aprender a usar Docker con NodeJs

La finalidad de este ejemplo va a ser enseñarte como conseguir que tu aplicación NodeJs corra dentro de un contenedor Docker. Nos vamos a centrar bajo la óptica del entorno de desarrollo no en la de despliegue en producción así que si usas este ejemplo para hacer una subida a producción y peta luego no quiero represalias 😛

Podríamos utilizar el ejemplo que hemos venido desarrollando en artículos anteriores sobre cómo construir una aplicación NodeJs pero como el fin último de este articulo es aprender a configurar Docker con NodeJs vamos a crearnos una aplicación pequeñita desde cero.

Docker nos va a permitir empaquetar toda nuestra aplicación junto con sus librerías y dependencias en una unidad estandarizada y atómica, esto es lo que se conoce como contenedor docker.

Otro concepto importante del universo docker que hay que conocer son las imágenes y esto es el software que se va a cargar dentro del contenedor previamente creado.

Creando una aplicación NodeJs

Lo primero de todo, créate en tu workspace un directorio para la aplicación. Al mío yo le he llamado directorio_para_aplicación_nodejs_guay_con_docker. ¿Bastante intuitivo no creéis?

Bien, en este directorio residirán todos los ficheros de tu aplicación pero el primer fichero necesario para empezar es el package.json el cual sirve de descripción de tu aplicación y de todas sus dependencias.

Bien, una vez hecho esto vamos a crear el fichero que definirá la aplicación web haciendo uso del framework Express. Esto es, el fichero server.js.

A continuación vamos a ver cómo podemos crear esta aplicación tan tonta dentro de un contenedor de Docker usando para ello la imagen oficial aunque antes de eso es necesario que creemos la imagen Docker de la aplicación.

Creando el fichero Dockerfile

En el mismo directorio en el que hemos puesto los dos ficheros anteriores, vamos ahora a crear uno nuevo que se llame Dockerfile.

Este fichero contendrá las instrucciones necesarias para que Docker sepa cómo ha de construir la imagen que albergará la aplicación NodeJs.

Este fichero se ve tal que así:

Pero vamos a ir paso a paso:

  • La primera línea indica que tipo de imagen queremos construir. En este caso estamos haciendo uso de la versión de largo soporte (LTS) de argon para NodeJs. Puedes ver otras imágenes desde Docker Hub.
  • Las siguientes líneas le dicen a Docker que tiene que crear un directorio bajo /usr/src/app (hablando siempre desde dentro del contenedor) y establece este path como su directorio de trabajo
  • Después se le dice a Docker que copie el fichero de definición de la aplicación NodeJs (package.json) al directorio de trabajo creado en previamente y que ejecute las dependencias que tiene la aplicación. Al usar node:argon (primera línea) ya tenemos disponible NPM y NodeJs.
  • Ten presente que en lugar de copiar todo el directorio de trabajo tan solo estamos copiando el fichero package.json dentro del contenedor. Esto nos permite aprovecharnos de las diferentes capas cacheadas de Docker. Si quieres profundizar un poquito mas y saber que carajos son estas capas cacheadas échale un vistazo a este artículo.
  • La siguiente línea o instrucción le dice a Docker que copie todo el directorio de fuera del contenedor al directorio de dentro del contenedor. Es decir, en este ejemplo solo copiará el fichero server.js pero si tu aplicación tuviera mas cosas las copiaría todas pa’ dentro.
  • También tenemos que exponer un puerto a través del cual se comunicará el mundo exterior con nuestro contenedor. Es típico poner siempre el puerto 8080 aunque puedes elegir el que mas te guste siempre y cuando no esté dentro de los Well-Know Ports.
  • Por último pero no menos importante, hay que definir el comando a ejecutar a través de la terminal para nuestra aplicación. En el ejemplo usamos el inicio típico y básico de una aplicación NodeJs el cual ejecutará el fichero server.js.

Poh’ ya está. Con esto ya tenemos definido el fichero que dará vida a nuestra imagen Docker.

Construyendo nuestra imagen

Para ello vamos a ir al directorio en el que tenemos el fichero Dockerfile y ejecutaremos en una terminal el siguiente comando para que Docker construya la imagen Docker con Nodejs.

docker build -t node-web-app .

El parámetro -t del comando anterior te permite darle un nombre a la imagen por lo que es mas fácil después encontrar la imagen al hacer uso de docker images. A nuestra imagen la hemos llamado node-web-app, también muy original… xD

Al listar las imágenes disponibles con docker images tendríamos que ver lo siguiente:

$ docker images

# Example
REPOSITORY                      TAG        ID              CREATED
node                            argon      539c0211cd76    3 weeks ago
node-web-app                    latest     d64d3505b0d2    1 minute ago

Corriendo nuestra imagen Docker

Aquí hay varias opciones de configuración al correr una imagen que puedes consultar en la documentación oficial de Docker pero para no liarnos mas ejecuta el siguiente comando:

$ docker run -p 49160:8080 -d node-web-app

La opción -d le indica a Docker que tiene que ser ejecutado en modo detached, esto es, que corra el contenedor en segundo plano. Por otro lado, la opción -p lo que permite es redirigir todas las consultas desde un puerto público hasta el puerto privado del contenedor que hemos definido en el fichero Dockerfile.

En este caso estamos mapeando el puerto 49160 con el puerto 8080.

Una vez hayas ejecutado el comando anterior ya tendrás tu contenedor Docker con NodeJs corriendo satisfactoriamente y podrás acceder a tu aplicación a través de http://localhost:49160

Si hacemos una petición curl a la url anterior obtendremos lo siguiente:

$ curl -i localhost:49160

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 12
Date: Sun, 02 Jun 2013 03:53:22 GMT
Connection: keep-alive

Que pasa gandul!

Como veis ya tenemos nuestra aplicación corriendo. Ha sido facilísimo configurar Docker con NodeJs pero por si acaso alguno tiene dudas o se ha liado en algún punto y me quiere preguntar algo hacedlo por favor en la sección de comentarios o en twitter a través del siguiente banner.

[xyz-ips snippet=”FAQS-GORKAMU-TW-YELLOW”]

Hasta la vista chumachos!

Anuncios

#1 Batalla de gallos: Vagrant vs Docker

Desde hace un tiempo a aquí, Vagrant ha sido la solución por defecto para crear entornos de desarrollo que pueden ser fácilmente configurados independientemente de nuestra maquina y compartidos entre el resto del equipo de desarrollo. Existen muchísimos beneficios al utilizar maquinas virtuales frente a tener que instalar todo el software de desarrollo, librerías, dependencias y servidores en nuestra máquina. Los siguientes son un ejemplo de lo que podemos hacer:

  • Construir snippets de código totalmente independiente de la maquina en la que estemos trabajando.
  • Los snippets de código pueden ser compartidos y reproducidos automáticamente con facilidad.
  • Podemos parar y arrancar maquinas virtuales a nuestro antojo.
  • Hostear entornos se convierte en algo efímero y podemos destruir aquellos sitios que no se utilizan ya.
  • Ahorramos tiempo en configuración y ganamos en desarrollo.

Esto ha sido así durante los últimos años, sin embargo, tenemos un chico nuevo en el bloque, un competidor que ha ido ganando terreno poco a poco.

Docker es el chico nuevo y también corre maquinas virtuales pero trabaja de una manera fundamentalmente distinta. En este post quiero hacer una breve explicación sobre cuales son las diferencias entre Vagrant y Docker y sobre cómo instalar WordPress en cada uno de estos.

Si nunca has oído hablar de Vagrant, Docker, máquinas virtuales y entornos de desarrollo te recomiendo que le eches un vistazo a este post en el que hablo sobre Vagrant 😉

Comparación en grandes rasgos de Vagrant y Docker

Para empezar, Vagrant utiliza una arquitectura mucho mas sencilla que la de Docker. Usa maquinas virtuales para correr los entornos independientemente de la maquina anfitrión. Esto se hace utilizando lo que se llama virtualización mediante programas como VirtualBox o VMWare. Cada entorno utiliza su propia maquina virtual y es configurada mediante un fichero que se llama Vagrantfile. Este fichero le dice a Vagrant como tiene que configurar la maquina virtual y que scripts va a necesitar correr junto con su orden de ejecución en el momento de aprovisionar la maquina.

La contrapartida de este enfoque es que cada maquina virtual que creemos contiene no solo el código fuente que estamos desarrollando junto a sus librerías y dependencias sino que además contiene todo un sistema operativo huésped incrementando así en varias Gigas el tamaño de la máquina.

Docker sin embargo utiliza lo que se conoce como contenedores que alojan tu aplicación y todas sus dependencias pero que comparte el kernel (el sistema operativo) con el resto de contenedores.

Estos contenedores se ejecutan como procesos aislados en el sistema operativo anfitrión, pero no están vinculados a ninguna infraestructura especifica. Lo bueno de este enfoque es que los contenedores pueden correr en cualquier ordenador.

¿Cuál es la conclusión de todo esto?

  • Vagrant es mas fácil de entender y es mas sencillo de configurar y poner en marcha. Lo malo es que puede consumir muchos recursos en términos a memoria RAM y almacenamiento.
  • La arquitectura de Docker en cambio es mas compleja de entender y su configuración puede volverse una tortura pero es mucho mas rápido y consume mucha menos CPU y RAM utilizando a su vez menos almacenamiento que Vagrant.
Infografía Docker vs Vagrant
Infografía Docker vs Vagrant

Cómo configurar Vagrant

Una de las mejores cosas sobre ambos sistemas es el ecosistema que se ha creado a su alrededor. Debido al hecho de que los entornos de desarrollo se crean fácilmente con scripts, los desarrolladores de estos scripts los están compartiendo creando así repositorios donde podemos encontrar miles de Vagrant boxes y Docker images diferentes que podemos usar libremente en nuestros proyectos.

Para aprender a configurar Vagrant vamos a utilizar una box de Vagrant que se llama Varying Vagrant Vagrants (VVV) la cual tiene una configuración muy típica para el desarrollo en/de Wordpress.

Antes de empezar necesitarás tener instalado VirtualBox y Vagrant.

Existen varios plugins para configurar Vagrant que VVV recomienda tener instalado:

  • vagrant-hostsupdater: automáticamente actualiza tu fichero de hosts para poder acceder desde el navegador a las maquinas aprovisionadas.
  • vagrant-triggers: permite configurar Vagranta para que dispare scripts cuando utilizamos comandos como vagrant halt o vagrant destroy. Un ejemplo de trigger podría ser el realizar copias de seguridad de la base de datos.

Si ya tienes todos los prerrequisitos instalados vamos a clonar el repositorio de VVV:

$ git clone git://github.com/Varying-Vagrant-Vagrants/VVV.git vagrant-local

Una vez se haya clonado nos dirigimos al directorio para levantar la maquina.

$ cd vagrant-local && vagrant up

Prepárate un café y espera porque el aprovisionamiento de la maquina suele tardar un poquillo… Piensa que necesita descargar todos los ficheros requeridos y configurar el sistema operativo para que podamos empezar a utilizarlo.

Una vez que haya terminado el proceso deberías ser capaz de poder acceder a http://local.wordpress.dev y ver un WordPress nuevo. Personalmente te recomiendo que te leas el README.md de VVV para saber mas sobre cómo configurar y usar sitios en Varying Vagrant Vagrants.

Cómo configurar Docker

Para empezar a configurar Docker y trabajar con el lo primero que necesitas tener instalado es Docker Toolbox que te provee con no solo el cliente de Docker sino que incluso nos trae Docker Machine y Docker Compose, que te ayuda a especificar múltiples configuraciones de contenedores con un solo fichero.

Una vez lo hayas instalado, la primer cosa que tienes que hacer es configurar Docker a través de una de sus maquinaa virtuales. Docker la utiliza para correr el Docker Engine que se encarga de administrar y controlar cualquier contenedor que quieras correr. Para ello vamos a crear una maquina virtual en VirtualBox llamada “docker-vm”.

$ docker-machine create --driver virtualbox docker-vm

Una vez la maquina se haya creado ya podemos comprobar los detalles de la misma ejecutando:

$ docker-machine env docker-vm

Y deberíamos obtener una salida parecida a la siguiente:

export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.101:2376"
export DOCKER_CERT_PATH="/Users/gilbitron/.docker/machine/machines/docker-vm"
export DOCKER_MACHINE_NAME="docker-vm"
# Run this command to configure your shell:
# eval "$(docker-machine env docker-vm)"

Si te has fijado bien, te habrás dado cuenta de que son las variables de entorno del cliente de Docker que utiliza para poder comunicarse con Docker Engine, así que vamos a hacer lo que nos dice y corramos el siguiente comando…

# eval "$(docker-machine env docker-vm)"

Persistiendo la configuración

Cada vez que abras una nueva sesión necesitas volver a correr este comando ya que sus configuraciones no persisten. Para guardar estas configuraciones te recomiendo que añadas el comando al fichero ~/.bashrc de tu sistema. El fichero bash se ejecuta automáticamente cada vez que se inicia sesión en el equipo.

Por fin somos capaces de empezar a crear contenedores de Docker. Una cosa importante a señalar es que Docker corre un proceso por cada contenedor. Esto significa que técnicamente si estamos corriendo una configuración de LAMP (Apache, Mysql y PHP-FPM) todos y cada uno de ellos deberán ser ejecutados en sus propios contenedores.

Esto sin embargo, es solo un ejemplo de guía y en la gran parte de los casos nos será conveniente agrupar procesos en contenedores, por ejemplo, Apache y PHP podrían correr en un contenedor y Mysql en otro diferente.

Afortunadamente existen imágenes oficiales de Docker para el desarrollo en/de WordPress como por ejemplo esta.

El enfoque que vamos a utilizar es precisamente ese, configurar Docker para correr WordPress (incluye Apache y PHP) en un contenedor y Mysql en otro contenedor diferente.

Creando nuestros contenedores

Para crear el contenedor Mysql ejecutamos lo siguiente:

$ docker run --name wordpressdb -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=wordpress -d mysql:5.7

Podemos echar un vistazo a la documentación de Docker para ver qué es lo que hace cada uno de estos parámetros aunque aquí tienes un pequeño resumen:

  • Con el parámetro –name le estamos dando un nombre a nuestro contenedor, ¿obvio no? Si no lo especificamos se generará un nombre aleatorio.
  • Con el parámetro -e estamos especificando variables de entorno que serán usada por el contenedor. Esta es una forma muy habitual de pasar información al contenedor.
  • Le indicamos al contenedor que se ejecute en modo detached.
  • Finalmente especificamos la imagen del sistema operativo que queremos utilizar con el parámetro image:version. Si esa imagen no existe localmente, Docker la descargará.

Ahora vamos a crear el contenedor para WordPress:

 $ docker run --name wordpress -e WORDPRESS_DB_PASSWORD=password -v "$PWD/":/var/www/html --link wordpressdb:mysql -p 80:80 -d wordpress

Hay dos cosas que hemos utilizado para este contenedor y que no hicimos para el contenedor de la base de datos:

  • Hemos especificado un volumen para mapear nuestro directorio de trabajo contra el directorio /var/www/html de nuestro contenedor utilizando para ello el parámetro -v. Mapeando el directorio podremos editar los ficheros de WordPress.
  • Añadiendo el parámetro –link hemos linkado el contenedor a nuestro contenedor wordpressdb anterior y le hemos dado el alias de mysql. Esto le permite al contenedor de WordPress conectarse contra el contenedor de la base de datos.
  • Hemos configurado el puerto en nuestra maquina virtual.

Podemos comprobar el estado de nuestros contenedores corriendo el siguiente comando:

$ docker ps

Pues ya está, ya serás capaz de visitar la dirección IP de tu maquina de Docker en el navegador y ver la pantalla de instalación de WordPress. Para saber la dirección IP de tu Docker puedes ejecutar el comando:

$ docker-machine env docker-vm

Un buen punto sería añadir un nombre a la IP en tu fichero de hosts para no tener que estar tecleando la dirección IP todo el tiempo.

Bonus: utilizar Docker Compose

Correr comandos tal y como lo hicimos un poquito mas atrás puede ser un coñazo y ademas estar fácilmente expuesto al típico “error humano”. Para ello Docker Compose puede echarnos una mano permitiéndonos utilizar un solo fichero en el que definimos toda la estructura del entorno de desarrollo y luego solo lo ejecutamos con un solo comando, algo así a como trabajan los Vagrantfiles.

Vamos a crear el fichero al que llamaremos docker-compose.yml

wordpress:
  image: wordpress
  environment:
    - WORDPRESS_DB_PASSWORD=password
  ports:
    - "80:80"
  volumes:
    - ./:/var/www/html
  links:
    - wordpressdb:mysql

wordpressdb:
  image: mysql:5.7
  environment:
    - MYSQL_ROOT_PASSWORD=password
    - MYSQL_DATABASE=wordpress

Si te fijas en el fichero, hemos replicado los comandos que hemos utilizado pero en formato yml. Ahora, para correrlo necesitamos ejecutar lo siguiente:

$ docker-compose up -d

En conclusión, ¿cuál es mejor?

Al igual que con el resto de cosas de la vida, no siempre existe un “bueno” y un “malo”, un “correcto” y un “incorrecto”, un “mejor” y un “peor”. Todo depende de cuales son tus necesidades y de cómo lo vas a utilizar así de con cuál te sientes mas cómodo. Personalmente he intentado configurarme un entorno de desarrollo con Docker pero me di cuenta de que era demasiado dificil para las necesidades del desarrollo que estaba haciendo en ese momento. De momento estoy contento con Vagrant ya que es fácil y funciona bastante bien pero estaré atento a ver como madura Docker y ver si lo acabo metiendo en alguno de mis desarrollos 😉

Hala a mamarla!