{ "nodes": [{ "uri": "https:\/\/log.exos.ninja\/3X", "content": "# Usando **docker-compose** con node.js *(backend)*\r\n## Con supervisor y toda la bola\r\n\r\nHace rato vengo usando [Docker](https:\/\/log.exos.ninja\/?q=docker) tanto para dev como en producci\u00f3n para algunos servicios, y una herramienta genial para armar entornos de desarrollo en realmente pocos comandos es [docker-compose](https:\/\/docs.docker.com\/compose\/).\r\n\r\n> Antes que nada aclaro unas cosas:\r\n\r\n> * **NO** usar la siguiente receta para producci\u00f3n\r\n> * Yo personalmente no recomiendo usar *docker-compose* en producci\u00f3n\r\n> * No voy a entrar en detalles de como instalar\/configurar Docker\r\n\r\n## Instalaci\u00f3n\r\n\r\ndocker-compose est\u00e1 disponible en varias distribuciones como paquete, y tambi\u00e9n se puede instalar por medio de [pip](https:\/\/pypi.python.org\/pypi\/pip), en Debian\/Ubuntu:\r\n\r\n $ sudo apt-get install docker-compose\r\n\r\nEn Archlinux existe el paquete [community\/docker-compose](https:\/\/www.archlinux.org\/packages\/community\/x86_64\/docker-compose\/):\r\n\r\n # pacman -S docker-compose\r\n\r\nY si se desea instalar a trav\u00e9s de pip:\r\n\r\n $ pip install docker-compose\r\n\r\n## Creaci\u00f3n del Dockerfile\r\n\r\nEsta parte es normal a Docker, solo que nuestro *container* va a tener algunas diferencias de si lo estar\u00edamos creando para producci\u00f3n, por ejemplo, vamos a montar el c\u00f3digo fuente, levantar nuestra app con [supervisor](https:\/\/www.npmjs.com\/package\/supervisor) *(si nunca lo usaste no es problema)*.\r\n\r\nEn mi caso me baso en la im\u00e1gen [mhart\/alpine-node](https:\/\/hub.docker.com\/r\/mhart\/alpine-node\/) simplemente porque al estar basada en alpine es liviana.\r\n\r\nUna cosa a tener en cuenta es que el orden de las instrucciones importa mucho, ya que los cambios mas intrusivos van primero para evitar la re-generaci\u00f3n de im\u00e1genes intermediarias:\r\n\r\n FROM mhart\/alpine-node:6\r\n RUN mkdir -p \/src\r\n ENV DEBUG \"*\"\r\n EXPOSE 3000\r\n WORKDIR \/src\r\n ADD package.json \/src\/\r\n RUN npm install --no-optional\r\n ADD . \/src\/\r\n\r\nLa linea `ENV DEBUG \"*\"` es para mostrar la salida de debug, en caso de utilizar [debug](https:\/\/www.npmjs.com\/package\/debug), en mi caso yo prefiero no setearla en `*` ya que muchas libs lo usan y se vuelve demaciado verborragico, yo las llamadas a `debug` normalmente les pongo un prefijo, al estilo `miapp.{modulo}`, por lo que si es el caso podemos setearla; `ENV DEBUG \"miapp.*\"`.\r\n\r\nComo abr\u00e1s notado primero agrego el archivo package.json y hago un install de las dependencias, eso har\u00e1 que la imagen intermediar\u00eda de las dependencias quede creada y no necesite ser actualizada cada vez que actualizamos el c\u00f3digo fuente de nuestra aplicaci\u00f3n. Para esto hay que tener en cuenta que `node_modules` vamos a tener que ignorarlo en docker, agregando \"node_modules\" en el archivo `.dockerignore`, a este punto tenemos que tener en cuenta que no necesitamos tener el c\u00f3digo fuente para que se instalen estas. Tambi\u00e9n ser cocientes de que se instalar\u00e1n todas las dependencias, incluyendo las de desarrollo.\r\n\r\nLo siguiente que hacemos es meter todo el arbol de directorios (menos lo ignorado por `.dockerignore`) en `\/src`.\r\n\r\nAhora en este caso no le vamos a dar un entry-point ya que eso solo agregar\u00eda mas intermediarios y esto lo vamos a trabajar directamente con docker-compose.\r\n\r\nLuego, una vez creado nuestro Dockerfile, en vez de hacer el build, vamos a generar nuestro `docker-compose.yml`, que ser\u00e1 nuestro archivo de configuraci\u00f3n de docker-compose:\r\n\r\n version: '2'\r\n services:\r\n db:\r\n image: postgres:9.6-alpine\r\n environment:\r\n POSTGRES_USER: \"someuser\"\r\n POSTGRES_PASSWORD: \"somepassword\"\r\n POSTGRES_DB: \"SomeDBName\"\r\n ports:\r\n - \"7432:5432\"\r\n MyProject:\r\n build: .\r\n command: .\/node_modules\/.bin\/supervisor -w lib \/src\/app.js\r\n volumes:\r\n - .:\/src\r\n ports:\r\n - \"3000:3000\"\r\n depends_on:\r\n - db\r\n\r\nDe esta forma definimos un container llamado `db` *(usando la imagen `postgres`)* y le seteamos la configuraci\u00f3n por defecto que debe tomar a trav\u00e9s de variables de entorno y aparte exponemos el puerto `7462` para poder conectarnos localmente a la base de datos y poder manipular los datos a mano. El segundo container es `MyProject` donde en vez de definirle una imagen a usar, le decimos que haga `build` desde `.` *(usando nuestro Dockerfile)*, y que el directorio actual, osea el de nuestro proyecto, va montado como `\/src`. Finalmente en command, que ser\u00eda nuestro `entry-point`, le decimos que corra supervisor con el parametro `-w` (watch), para que si hacemos alg\u00fan cambio en nuestro c\u00f3digo, el servidor se reinicie, sin necesidad de resetear los containers ni nada.\r\n\r\n## Levantando el entorno de desarrollo\r\n\r\nPara levantar todo de una vez, solo deberemos usar el comando:\r\n\r\n $ docker-compose up\r\n\r\nEso autom\u00e1ticamente crear\u00e1 la im\u00e1gen de nuestro proyecto, las dependencias y levantar\u00e1 todo.\r\n\r\nSi necesitamos ejecutar un comando dentro de nuestro entorno de desarrollo, podemos usar el comando `run` de `docker-compose`, por ejemplo:\r\n\r\n $ docker-compose run MyProject npm run syncdb\r\n\r\nComo el `workdir` es `\/src`, si corremos npm podremos correr scripts definidos en nuestro `package.json` libremente.\r\n\r\n## Rebuild\r\n\r\nEn el caso de que tengamos un cambio severo en nuestro container principal, por ejemplo, instalamos librer\u00edas y tenemos que re-armar la im\u00e1gen con las dependencias de npm *(tener en cuenta que `node_modules` dentro del container es independiente)* o instalamos alguna librer\u00eda a nivel sistema, deberemos parar el `docker-compose up`, y ejecutar:\r\n\r\n $ docker-compose build MyProject\r\n\r\nEso forzar\u00e1 el re-build de la imagen.", "created": "2017-09-04 21:06:56"}] }