El autor del material, cuya traducción publicamos hoy, es un ingeniero de DevOps. Él dice que tiene que usar
Docker . En particular, esta plataforma de gestión de contenedores se utiliza en varias etapas del ciclo de vida de las aplicaciones Node.js. El uso de Docker, una tecnología que recientemente ha sido extremadamente popular, le permite optimizar el proceso de desarrollo y salida de los proyectos Node.js en producción.

Ahora estamos publicando una
serie de artículos sobre Docker diseñados para aquellos que desean aprender esta plataforma para su uso en una variedad de situaciones. El mismo material se centra principalmente en el uso profesional de Docker en el desarrollo de Node.js.
¿Qué es un acoplador?
Docker es un programa diseñado para organizar la virtualización a nivel del sistema operativo (contenedorización). En el corazón de los contenedores hay imágenes en capas. En pocas palabras, Docker es una herramienta que le permite crear, implementar y ejecutar aplicaciones utilizando contenedores independientes del sistema operativo en el que se ejecutan. El contenedor incluye una imagen del sistema operativo base necesario para que la aplicación funcione, la biblioteca de la que depende esta aplicación y esta aplicación en sí. Si varios contenedores se ejecutan en la misma computadora, entonces usan los recursos de esta computadora juntos. Los contenedores Docker pueden empaquetar proyectos creados usando una variedad de tecnologías. Estamos interesados en proyectos basados en Node.js.
Crear un proyecto Node.js
Antes de empaquetar un proyecto Node.js en un contenedor Docker, necesitamos crear este proyecto. Hagámoslo Aquí está el archivo
package.json
de este proyecto:
{ "name": "node-app", "version": "1.0.0", "description": "The best way to manage your Node app using Docker", "main": "index.js", "scripts": { "start": "node index.js" }, "author": "Ankit Jain <ankitjain28may77@gmail.com>", "license": "ISC", "dependencies": { "express": "^4.16.4" } }
Para instalar las dependencias del proyecto, ejecute el
npm install
. En el curso de este comando, entre otras cosas, se
package-lock.json
archivo
package-lock.json
. Ahora cree el archivo
index.js
, que contendrá el código del proyecto:
const express = require('express'); const app = express(); app.get('/', (req, res) => { res.send('The best way to manage your Node app using Docker\n'); }); app.listen(3000); console.log('Running on http://localhost:3000');
Como puede ver, aquí describimos un servidor simple que devuelve algo de texto en respuesta a las solicitudes.
Crear Dockerfile
Ahora que la aplicación está lista, hablemos sobre cómo empaquetarla en un contenedor Docker. A saber, se tratará de cuál es la parte más importante de cualquier proyecto basado en Docker, sobre el Dockerfile.
Un Dockerfile es un archivo de texto que contiene instrucciones para crear una imagen Docker para una aplicación. Las instrucciones en este archivo, si no entra en detalles, describen la creación de capas de un sistema de archivos multinivel, que tiene todo lo que una aplicación necesita para funcionar. La plataforma Docker puede almacenar en caché las capas de imagen, lo que, al reutilizar las capas que ya están en el caché, acelera el proceso de creación de imágenes.
En la programación orientada a objetos, existe una clase. Las clases se usan para crear objetos. En Docker, las imágenes se pueden comparar con clases y los contenedores se pueden comparar con instancias de imágenes, es decir, con objetos. Considere el proceso de generar un Dockerfile, que nos ayudará a resolver esto.
Cree un Dockerfile vacío:
touch Dockerfile
Dado que vamos a construir un contenedor para la aplicación Node.js, lo primero que necesitamos poner en el contenedor es la imagen de Nodo base, que se puede encontrar en
Docker Hub . Utilizaremos la versión LTS de Node.js. Como resultado, la primera declaración de nuestro Dockerfile será la siguiente declaración:
FROM node:8
Después de eso, cree un directorio para nuestro código. Al mismo tiempo, gracias a la instrucción
ARG
utilizada aquí, podemos, si es necesario, especificar el nombre del directorio de la aplicación que no sea
/app
durante el ensamblaje del contenedor. Los detalles sobre este manual se pueden encontrar
aquí .
Como usamos la imagen Node, las plataformas Node.js y npm ya estarán instaladas en ella. Usando lo que ya está en la imagen, puede organizar la instalación de las dependencias del proyecto. Usando el indicador
NODE_ENV
(o si la
NODE_ENV
entorno
NODE_ENV
establecida en
production
) npm no instalará los módulos listados en la sección
devDependencies
del archivo
devDependencies
.
Aquí estamos copiando el
package*.json
a la imagen, en lugar de, por ejemplo, copiar todos los archivos del proyecto. Hacemos eso precisamente porque las instrucciones Dockerfile
RUN
,
COPY
y
ADD
crean capas de imágenes adicionales, por lo que puede usar las funciones de almacenamiento en caché de las capas de la plataforma Docker. Con este enfoque, la próxima vez que recopilemos una imagen similar, Docker descubrirá si es posible reutilizar las capas de imágenes que ya están en el caché y, de ser así, aprovechará lo que ya está allí, en lugar de crear otras nuevas. capas Esto le permite ahorrar mucho tiempo al ensamblar capas en el curso del trabajo en proyectos grandes, que incluyen muchos módulos npm.
Ahora copie los archivos del proyecto al directorio de trabajo actual. Aquí no usaremos la instrucción
ADD , sino la instrucción
COPY . De hecho, en la mayoría de los casos se recomienda dar preferencia a la instrucción
COPY
.
La instrucción
ADD
, en comparación con
COPY
, tiene algunas características que, sin embargo, no siempre son necesarias. Por ejemplo, estamos hablando de opciones para desempaquetar archivos .tar y descargar archivos por URL.
Los contenedores Docker son entornos aislados. Esto significa que cuando lancemos la aplicación en el contenedor, no podremos interactuar con ella directamente sin abrir el puerto en el que escucha esta aplicación. Para informar a Docker que hay una aplicación en un determinado contenedor que escucha en un determinado puerto, puede usar la instrucción
EXPOSE .
Hasta la fecha, nosotros, utilizando el Dockerfile, hemos descrito la imagen que contendrá la aplicación y todo lo que necesita para iniciarse con éxito. Ahora agregue las instrucciones al archivo que le permite iniciar la aplicación. Esta es una instrucción
CMD . Le permite especificar un determinado comando con parámetros que se ejecutarán cuando se inicie el contenedor y, si es necesario, pueden ser anulados por las herramientas de línea de comandos.
Así se verá el Dockerfile terminado:
FROM node:8
Asamblea de imagen
Hemos preparado un archivo Dockerfile que contiene instrucciones para construir la imagen, sobre la base de la cual se creará un contenedor con una aplicación en ejecución. Ensamble la imagen ejecutando un comando de la siguiente forma:
docker build --build-arg <build arguments> -t <user-name>/<image-name>:<tag-name> /path/to/Dockerfile
En nuestro caso, se verá así:
docker build --build-arg APP_DIR=var/app -t ankitjain28may/node-app:V1 .
Dockerfile tiene una declaración
ARG
que describe el argumento
APP_DIR
. Aquí establecemos su significado. Si esto no se hace, tomará el valor que se le asigna en el archivo, es decir, la
app
.
Después de ensamblar la imagen, verifique si Docker la ve. Para hacer esto, ejecute el siguiente comando:
docker images
En respuesta a este comando, se debe generar aproximadamente lo siguiente.
Imágenes de DockerLanzamiento de imagen
Después de haber ensamblado la imagen de Docker, podemos ejecutarla, es decir, crear una instancia de ella, representada por un contenedor de trabajo. Para hacer esto, use un comando de este tipo:
docker run -p <External-port:exposed-port> -d --name <name of the container> <user-name>/<image-name>:<tag-name>
En nuestro caso, se verá así:
docker run -p 8000:3000 -d --name node-app ankitjain28may/node-app:V1
Le pediremos al sistema información sobre contenedores que funcionan con este comando:
docker ps
En respuesta a esto, el sistema debería generar algo como lo siguiente:
Contenedores DockerHasta ahora, todo va como se esperaba, aunque aún no hemos intentado acceder a la aplicación que se ejecuta en el contenedor. A saber, nuestro contenedor, denominado
node-app
, escucha en el puerto
8000
. Para intentar acceder a él, puede abrir un navegador y acceder a él en
localhost:8000
. Además, para verificar el estado del contenedor, puede usar el siguiente comando:
curl -i localhost:8000
Si el contenedor realmente funciona, se devolverá algo como el que se muestra en la siguiente figura en respuesta a este comando.
Resultado del control de salud del contenedorSobre la base de la misma imagen, por ejemplo, sobre la base de recién creado, es posible crear muchos contenedores. Además, puede enviar nuestra imagen al registro de Docker Hub, lo que permitirá a otros desarrolladores cargar nuestra imagen y lanzar los contenedores apropiados en casa. Este enfoque simplifica el trabajo con proyectos.
Recomendaciones
Aquí hay algunas sugerencias que vale la pena considerar para aprovechar el poder de Docker y crear imágenes tan compactas como sea posible.
▍1. Siempre cree un archivo .dockerignore
En la carpeta del proyecto que planea colocar en el contenedor, siempre necesita crear un archivo
.dockerignore
. Le permite ignorar archivos y carpetas que no son necesarios al construir la imagen. Con este enfoque, podemos reducir el llamado contexto de construcción, que nos permitirá ensamblar rápidamente la imagen y reducir su tamaño. Este archivo admite plantillas de nombre de archivo, en este caso es similar a un archivo
.gitignore
. Se recomienda agregar un comando a
.dockerignore
debido a que Docker ignorará la carpeta
/.git
, ya que esta carpeta generalmente contiene materiales grandes (especialmente durante el desarrollo de un proyecto) y agregarla a la imagen conduce a un aumento en su tamaño. Además, copiar esta carpeta en una imagen no tiene mucho sentido.
▍2. Utilice el proceso de ensamblaje de imágenes en varias etapas
Considere el ejemplo cuando recopilamos un proyecto para una determinada organización. Este proyecto utiliza muchos paquetes npm, y cada uno de estos paquetes puede instalar paquetes adicionales de los que depende. La realización de todas estas operaciones lleva a un tiempo adicional dedicado al proceso de ensamblar la imagen (aunque esto, gracias a las capacidades de almacenamiento en caché de Docker, no es tan importante). Peor aún, la imagen resultante que contiene las dependencias de un determinado proyecto es bastante grande. Aquí, si hablamos de proyectos front-end, podemos recordar que dichos proyectos generalmente se procesan utilizando paquetes como webpack, que permiten empaquetar convenientemente todo lo que una aplicación necesita en un paquete de ventas. Como resultado, los archivos de paquete npm para dicho proyecto son innecesarios. Y esto significa que podemos deshacernos de dichos archivos después de construir el proyecto usando el mismo paquete web.
Armado con esta idea, intente hacer esto:
Tal enfoque, sin embargo, no nos conviene. Como ya dijimos, las instrucciones
RUN
,
ADD
y
COPY
crean capas almacenadas en caché por Docker, por lo que debemos encontrar una manera de manejar la instalación de dependencias, construir el proyecto y luego eliminar archivos innecesarios con un solo comando. Por ejemplo, podría verse así:
En este ejemplo, solo hay una instrucción
RUN
que instala las dependencias,
node_modules
proyecto y elimina la carpeta
node_modules
. Esto lleva al hecho de que el tamaño de la imagen no será tan grande como el tamaño de la imagen que incluye la carpeta
node_modules
. Usamos los archivos de esta carpeta solo durante el proceso de compilación del proyecto y luego lo eliminamos. Es cierto que este enfoque es malo, ya que lleva mucho tiempo instalar dependencias npm. Puede eliminar este inconveniente utilizando la tecnología de ensamblaje de imágenes en varias etapas.
Imagine que estamos trabajando en un proyecto frontend que tiene muchas dependencias, y usamos webpack para construir este proyecto. Con este enfoque, podemos, en aras de reducir el tamaño de la imagen, aprovechar las capacidades de Docker para el
ensamblaje de imágenes en
varias etapas .
FROM node:8 As build
Con este enfoque, la imagen resultante es mucho más pequeña que la imagen anterior, y también utilizamos el
node:alpine
imagen
node:alpine
, que es muy pequeña. Y aquí hay una comparación de un par de imágenes, durante las cuales se puede ver que la imagen del
node:alpine
mucho más pequeña que la imagen del
node:8
.
Comparación de imágenes del repositorio de nodos▍3. Usar caché de Docker
Esfuércese por utilizar las capacidades de almacenamiento en caché de Docker para crear sus imágenes. Ya prestamos atención a esta característica cuando trabajamos con un archivo al que se accedió mediante el
package*.json
nombres
package*.json
. Esto reduce el tiempo de construcción de la imagen. Pero esta oportunidad no se debe usar precipitadamente.
Supongamos que describimos en Dockerfile la instalación de paquetes en una imagen creada a partir de la imagen base de
Ubuntu:16.04
:
FROM ubuntu:16.04 RUN apt-get update && apt-get install -y \ curl \ package-1 \ . .
Cuando el sistema procesará este archivo, si hay muchos paquetes instalados, las operaciones de actualización e instalación tomarán mucho tiempo. Para mejorar la situación, decidimos aprovechar las capacidades de almacenamiento en caché de capas de Docker y reescribimos el Dockerfile de la siguiente manera:
FROM ubuntu:16.04 RUN apt-get update RUN apt-get install -y \ curl \ package-1 \ . .
Ahora, al ensamblar la imagen por primera vez, todo sale como debería, ya que el caché aún no se ha formado. Imagine ahora que necesitamos instalar otro paquete,
package-2
. Para hacer esto, reescribimos el archivo:
FROM ubuntu:16.04 RUN apt-get update RUN apt-get install -y \ curl \ package-1 \ package-2 \ . .
Como resultado de dicho comando,
package-2
no se instalará ni actualizará. Por qué El hecho es que al ejecutar la instrucción
RUN apt-get update
, Docker no ve ninguna diferencia entre esta instrucción y la instrucción ejecutada anteriormente, como resultado, toma datos del caché. Y estos datos ya están desactualizados. Al procesar la instrucción
RUN apt-get install
sistema la ejecuta, ya que no parece una instrucción similar en el Dockerfile anterior, pero durante la instalación, pueden producirse errores o se instalará la versión anterior de los paquetes. Como resultado, resulta que los comandos de
update
e
install
deben ejecutarse dentro de la misma instrucción
RUN
, como se hace en el primer ejemplo. El almacenamiento en caché es una gran característica, pero el uso imprudente de esta característica puede generar problemas.
▍4. Minimiza el número de capas de imagen
Se recomienda, siempre que sea posible, esforzarse por minimizar el número de capas de imagen, ya que cada capa es el sistema de archivos de la imagen Docker, lo que significa que cuanto más pequeñas sean las capas en la imagen, más compacta será. Cuando se usa el proceso de múltiples etapas de ensamblar imágenes, se logra una reducción en el número de capas en la imagen y una disminución en el tamaño de la imagen.
Resumen
En este artículo, analizamos el proceso de empaquetar aplicaciones Node.js en contenedores Docker y trabajar con dichos contenedores. Además, hicimos algunas recomendaciones que, por cierto, pueden usarse no solo al crear contenedores para proyectos de Node.js.
Estimados lectores! Si utiliza Docker profesionalmente cuando trabaja con proyectos Node.js, comparta recomendaciones sobre el uso efectivo de este sistema con principiantes.
