Ver contenido

carlosazaustre.es carlosazaustre.es

Manejando Docker desde OS X. Creando nuestro primer contenedor de NodeJS

🗓 | 💻 Desarrollo | 🕐 5 minutos de lectura | 💬 Comments

###¿Qué es Docker? Docker es una aplicación que nos permite ejecutar entornos completos dentro de contenedores Linux. Pudiendo crear esos entornos a través de Dockerfiles o utilizando imágenes ya creadas por terceros desde el Hub (Una especie de GitHub de imágenes). Yo lo considero la evolución a Vagrant y un buen punto de partida si queremos empezar a separar nuestras aplicaciones en trozos más pequeños (microservicios).

Como Docker utiliza como base el sistema operativo Linux, en OS X no podemos utilizarlo tal cual, necesitamos una ayuda de una máquina virtual y algunos ajustes para que funcione como si nuestra máquina fuera Linux.

Para ejemplificar la estructura de cómo está organizado Docker en nuestro sistema operativo, estas imágenes sacadas de la documentación oficial de Docker nos resultan de ayuda.

Si nuestro equipo fuera Linux, esta sería la organización: linux docker host

Y en el caso de un equipo Mac con OS X, es ésta: mac docker host

La diferencia es que en Linux, nuestro Host es el mismo sistema operativo. Y en OSX, el Host es la máquina virtual que tiene Linux. Lo complicado aquí es el acceso, las IPs y el mapeo de los puertos, pero en este post vamos a explicar como solucionar esto y utilizarlo de manera que la máquin virtual no de guerra ;)

###Instalar Docker en OS X (Yosemite). Primero de todo necesitas instalar VirtualBox. Lo puedes descargar desde su página oficial.

Descargar VirtualBox

Después necesitas instalar boot2docker que es una mini-máquina-virtual de Linux para poder utilizar esta tecnología en Mac. Y por supuesto, Docker. Recomiendo hacer esto a través de homebrew que funciona muy bien y así evitamos líos con las variables de entorno y después es más sencillo de desinstalar que si se hace manual:

Iniciamos boot2docker. Esto únicamente debemos hacerlo la primera vez, después ya quedará configurado en nuestro equipo.

Cómo boot2docker es una máquina virtual de Linux, ya que nuestro equipo no lo es y para ejecutar Docker necesitamos que se haga sobre Linux, esta máquina virtual tendrá una IP y un puerto con el que podeamos interactuar. Para poder hacer esto de un forma más ágil vamos exportar las siguientes variables de entorno dentro de nuestro fichero ~/.bash_profile

Para probar que vamos bien, ejecutaremos los siguientes comandos en nuestra terminal:

Si sale algo parecido a lo anterior, vamos bien encaminados. ¿Qué hemos hecho hasta ahora? Instalar boot2docker que es una maquina virtual ligera de Linux, instalar el servidor de Docker en esa máquina virtual, y comunicarnos con él utilizan el cliente de Docker que se ha instalado en nuestro Mac OS X.

Ya tenemos boot2docker instalado y configurado en nuestro equipo Mac, pero tenemos que hacer un par de cambios previos si queremos dejarlo perfecto.

###Mapeo de puertos Docker mapea los puertos que usa desde el container al host. En una máquina Linux, el host seríamos nosotros mismos, pero en Mac el host es la Máquina Virtual (boot2docker). Por tanto si corremos una aplicación dentro de contenedor que escuche en el puerto 3000 y este puerto lo mapeamos con el puerto 3000 del host, al poner http://localhost:3000 no veremos nada. ¿Cuál es el truco? Usar la IP que nos da la máquina virtual:

Pero nosotros no queremos estar recordando esa IP siempre que queramos probar algo, ni tampoco tener que estar mapeando los puertos 2 veces, para que pasen del contenedor a la máquina virtual, como si estuviéramos en Inception.

Inception

Entonces podemos hacer estas triquiñuelas:

Y ejecutando el siguiente script (se demora un tiempo) hacemos el mapeo de puertos contenedor > Máquina Virtual > Localhost de nuestro equipo:

Ya tenemos todo configurado y listo. Despues podremos acceder a nuestro navegador con la URL http://dockerhost y estaremos recibiendo la respuesta del contenedor. Para ello lo siguiente que haremos será una sencilla aplicación en Node.js

Creando nuestra Node App

El sistema de archivos de nuestra Mini-App será el siguiente

Dockerfile será nuestro fichero manifiesto donde especificaremos el entorno que vamos a utilizar y los comandos a ejecutar en él.

code será la carpeta raíz de nuestro proyecto, dentro de ella estarán las dependencias (node_modules), el fichero principal (index.js) y el resto de ficheros y carpetas.

Dentro de public tan sólo tendremos un fichero index.html que contendrá lo básico

que serviremos desde nuestra aplicación Node, usando el módulo st que permite crear un servidor de estáticos rápidamente, Éste sería el contenido de nuestro fichero index.js:

Como puedes ver es un servidor muy sencillo, no usamos siquiera Express, con el módulo nativo de http nos apañamos.

Ahora es el momento de probar la aplicación corriendo desde Docker. Para ello editamos el fichero Dockerfile con lo siguiente:

Cosas importantes aquí, Docker tiene un registro parecido a Github pero de imágenes con entornos ya preconfigurados. Podíamos crear uno desde cero que partiese de Ubuntu o CentOS y seguidamente instalar Node desde él, pero para hacer más sencillo este ejemplo, he importado la imagen de Node v0.12.2 ya existente de Joyent.

El campo MAINTAINER es para indicar el autor o mantenedor de la imagen que se está configurando en el Dockerfile en este caso he puesto mi email.

El comando COPY ./code /code Esta indicando que se haga una copia de la carpeta code del proyecto a la carpeta /code del contenedor.

RUN npm install -g pm2 es un comando que se ejecutará dentro del contenedor. Estamos indicando que instale la librería PM2 de manera global en el contenedor.

RUN cd /code; npm install instalará las dependencias que tenga el package.json del proyecto dentro del contenedor.

EXPOSE 3000 indica que el puerto 3000 del contenedor (donde está escuchando la app node) sea mapeado hacia afuera, para que podamos acceder desde fuera del contenedor (Por ejemplo en http://dockerhost:3000)

y por último CMD ["pm2", "start", "/code/index.js"] es el comando que queramos que se ejecute al finalizar la instalación del entorno. pm2 start /code/index.js es como ejecutar node /code/index.js pero PM2 lo ejecuta de contínuo aunque salgamos de la sesión del terminal y tiene muchas otras ventajas. Puedes echarle un ojo al repositorio del proyecto: PM2 en Github.

Ejecutando nuestro contenedor

Llegó el momento. Ya tenemos todo configurado y nuestra aplicación desarrollada.

Podemos ver que imágenes tenemos descargadas en nuestro sistema y que contenedores están en ejecución con los comandos:

En este momento no tenemos niguna. Vamos a crear la primera con nuestro Dockerfile y apliación Node.js:

Con éste comando creamos la imagen carlosazaustre/docker-node que corresponde con el Dockerfile que hemos escrito anteriormente. Consiste en un nombre de usuario, en mi caso mi nombre y el nombre que represete a la imagen, en mi caso le he puesto docker-node

Al ejecutarte el comando se descargará la imagen de node que indicamos en el FROM del Dockerfile y posteriormente ejecutará los comando COPY, RUN y CMD.

Cuando finalice, si ejecutamos docker images veremos algo como:

Donde vemos la imagen de node que ha descargado y la que acabamos de crear con mi nombre.

Para poner en marcha el contenedor ejecutamos el siguiente comando:

Indicamos con -p que puerto del contenedor asociamos con nuestro host y con -d la imagen en cuestión. Esto ejecutará el comando CMD de nuestro Dockerfile.

Podemos ver que contenedores tenemos funcionando con:

Vemos que le asocia un ID a cada contenedor, en este caso docker-node tiene el ID 41da388e4a39. Si queremos ver el log que lanza nuestra aplicación dentro del contenedor lo podemos hacer con el comando docker logs y pasándole las 4 primeras cifras del ID, por ejemplo:

Vemos que la aplicación se está ejecutando. ¿Cómo podemos probarla? Abramos un navegador y escribamos la URL: http://dockerhost:3000

Docker container running on dockerhost

Prueba superada, hemos traspasado 3 niveles, a lo Dominic Cobb. Inception Deeper

Referencias

✎ ¿Ves alguna errata? ¿Quieres modificar algo? Haz una Pull Request.

Carlos Azaustre

Soy Carlos Azaustre. Me dedico al desarrollo web. Actualmente trabajo como Senior Frontend Engineer en Eventbrite. Fui nombrado en 2019 GDE (Google Developer Expert) en Tecnologías Web. Desde 2013 intento documentar en éste blog todo lo que aprendo y así compartirlo con el resto de la comunidad.

Si te gusta lo que lees, puedes apoyarme en mi Patreon o invitarme a un café virtual 🙂

Se mi patrón Invítame a un Café