One place for hosting & domains

      producción

      Cómo automatizar implementaciones de producción de Node.js con Shipit en CentOS 7


      El autor seleccionó la Electronic Frontier Foundation para recibir una donación como parte del programa Write for DOnations.

      Introducción

      Shipit es una herramienta universal de automatización e implementación para desarrolladores de Node.js. Incluye un flujo de tareas basado en el popular paquete Orchestrator, comandos de inicio de sesión y SSH interactivos a través de OpenSSH y una API extensible. Los desarrolladores pueden usar Shipit a fni de automatizar flujos de trabajo de compilación e implementación para una amplia variedad de aplicaciones de Node.js.

      El flujo de trabajo de Shipit no solo permite a los desarrolladores configurar tareas, sino también especificar su orden de ejecución; si deben ejecutarse de forma sincrónica o asincrónica y en qué entorno.

      En este tutorial, instalará y configurará Shipit para implementar una aplicación de Node.js de su entorno de desarrollo local en su entorno de producción. Usará Shipit para implementar su aplicación y configurar el servidor remoto haciendo lo siguiente:

      • transfiriendo los archivos de su aplicación de Node.js de su entorno local al entorno de producción (con rsync, git y ssh);
      • instando las dependencias de su aplicación (módulos de nodos);
      • configurando y administrando los procesos de Node.js que se ejecutan en el servidor remoto con PM2.

      Requisitos previos

      Antes de iniciar este tutorial, necesitará lo siguiente:

      Nota: Los usuarios de Windows deberán instalar el subsistema de Windows para Linux para ejecutar los comandos de esta guía.

      Paso 1: Configurar el repositorio remoto

      En Shipit se requiere un repositorio de Git para la sincronía entre la máquina de desarrollo local y el servidor remoto. En este paso, creará un repositorio remoto en Github.com. Si bien cada proveedor es ligeramente diferente, los comandos pueden ser transferibles.

      Para crear un repositorio, abra Github.com en su navegador web e inicie sesión. Verá un símbolo + en la esquina superior derecha de cualquier página. Haga clic en + y luego en New repository.

      Github-new-repository

      Ingrese un nombre corto y recordable para su repositorio; por ejemplo, hello-world. Tenga en cuenta que cualquier nombre que elija aquí se replicará como la carpeta del proyecto a partir de la que trabajará en su máquina local.

      Github-repository-name

      De manera opcional, puede añadir una descripción de su repositorio.

      Github-repository-description

      Según su preferencia, fije la visibilidad de su repositorio de modo que sea público o privado.

      Asegúrese de que este se inicialice con .gitignore; seleccione Node en la lista desplegable Add .gitignore. Este paso es importante para evitar que se añadan archivos innecesarios (por ejemplo, la carpeta node_modules) a su repositorio.

      Github-gitignore-node

      Haga clic en el botón Create repository.

      Ahora, el repositorio debe clonarse de Github.com y enviarse a su equipo local.

      Abra su terminal y diríjase a la ubicación en la que desee almacenar todos los archivos de su proyecto de Node.js. Tenga en cuenta que en este proceso se creará una subcarpeta dentro del directorio actual. Para clonar el repositorio en su máquina local, ejecute el siguiente comando:

      • git clone https://github.com/your-github-username/your-github-repository-name.git

      Deberá sustituir your-github-username y your-github-repository-name para reflejar su nombre de usuario de Github y el nombre del repositorio previamente suministrado.

      Nota: Si habilitó la autenticación de dos factores (2FA) en Github.com, debe usar un token de acceso personal o una clave SSH en lugar de su contraseña cuando acceda a Github en la línea de comandos. En la página de ayuda de Github relacionada con 2FA se proporciona más información.

      Verá un resultado similar a lo siguiente:

      Output

      Cloning into 'your-github-repository-name'... remote: Enumerating objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3 Unpacking objects: 100% (3/3), done.

      Diríjase al repositorio ejecutando el siguiente comando:

      • cd your-github-repository-name

      En el repositorio se encuentra un solo archivo y una carpeta, ambos son archivos que Git utiliza para administrar el repositorio. Puede verificar esto con lo siguiente:

      Visualizará un resultado similar al siguiente:

      Output

      total 8 0 drwxr-xr-x 4 asciant staff 128 22 Apr 07:16 . 0 drwxr-xr-x 5 asciant staff 160 22 Apr 07:16 .. 0 drwxr-xr-x 13 asciant staff 416 22 Apr 07:16 .git 8 -rw-r--r-- 1 asciant staff 914 22 Apr 07:16 .gitignore

      Ahora que configuró un repositorio git funcional, creará el archivo shipit.js que administra su proceso de implementación.

      Paso 2: Integrar Shipit a un proyecto de Node.js

      En este paso, creará un proyecto de ejemplo de Node.js y luego agregará los paquetes de Shipit. En este tutorial se ofrece una aplicación de ejemplo: el servidor web Node.js que acepta solicitudes HTTP y responde con Hello World en texto simple. Para crear la aplicación, ejecute el siguiente comando:

      Añada el siguiente código de aplicación de ejemplo a hello.js (actualizando la variable APP_PRIVATE_IP_ADDRESS de modo que sea la dirección IP privada de su servidor app):

      hello.js

      var http = require('http');
      http.createServer(function (req, res) {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('Hello Worldn');
      }).listen(8080, 'APP_PRIVATE_IP_ADDRESS');
      console.log('Server running at http://APP_PRIVATE_IP_ADDRESS:8080/');
      

      Ahora, cree el archivo package.json para su aplicación:

      Con este comando se crea un archivo package.json, que usará para configurar su aplicación Node.js. En el siguiente paso, agregará dependencias a este archivo con la interfaz de línea de comandos npm.

      Output

      Wrote to ~/hello-world/package.json: { "name": "hello-world", "version": "1.0.0", "description": "", "main": index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

      A continuación, instale los paquetes npm necesarios con el siguiente comando:

      • npm install --save-dev shipit-cli shipit-deploy shipit-shared

      Aquí se utiliza el indicador --save-dev, ya que los paquetes de Shipit solo son necesarios en su equipo local. Visualizará un resultado similar al siguiente:

      Output

      + shipit-shared@4.4.2 + shipit-cli@4.2.0 + shipit-deploy@4.1.4 updated 4 packages and audited 21356 packages in 11.671s found 62 low severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details

      Con esto también se añadieron los tres paquetes a su archivo package.json como dependencias de desarrollo:

      package.json

      . . .
        "devDependencies": {
          "shipit-cli": "^4.2.0",
          "shipit-deploy": "^4.1.4",
          "shipit-shared": "^4.4.2"
        },
      . . .
      

      Ahora que configuró su entorno local, podrá proceder a preparar el servidor app remoto para las implementaciones basadas en Shipit.

      Paso 3: Preparar el servidor app remoto

      En este paso, usará ssh para establecer conexión con el servidor app e instalará la dependencia remota rsync. Rsync es una utilidad que permite transferir y sincronizar de manera eficiente archivos entre unidades de máquinas locales y en computadoras en red mediante la comparación de fechas de modificación y tamaños de archivos.

      En Shipit se utiliza rsync para transferir y sincronizar archivos entre su equipo local y el servidor app remoto. No emitirá comandos para rsync de forma directa; Shipit lo hará por usted.

      Nota: A través de Cómo configurar una aplicación de Node.js para la producción en CentOS 7, obtuvo dos servidores app y web. Estos comandos deben ejecutarse únicamente en app.

      Establezca conexión con su servidor app remoto a través de ssh:

      • ssh deployer@your_app_server_ip

      Instale rsync en su servidor ejecutando el siguiente comando:

      Confirme la instalación con lo siguiente:

      Verá una línea similar en el resultado de este comando:

      Output

      rsync version 3.1.2 protocol version 31 . . .

      Puede cerrar su sesión ssh escribiendo exit.

      Con rsync instalado y disponible en la línea de comandos, puede proceder con las tareas de implementación y su relación con los eventos.

      Paso 4: Configurar y ejecutar tareas de implementación

      Tanto los eventos como las tareas son componentes claves de las implementaciones de Shipit y es importante comprender la forma en que complementan la implementación de su aplicación. Los eventos desencadenados por Shipit representan puntos específicos en el ciclo de vida de la implementación. Sus tareas se ejecutarán en respuesta a estos eventos, según la secuencia del ciclo de vida de Shipit.

      Un ejemplo común de la utilidad de este sistema de tareas y eventos en una aplicación de Node.js se encuentra en la instalación de las dependencias de la aplicación (node_modules) en el servidor remoto. Más adelante en este paso, hará que en Shipit se escuche el evento updated (emitido después de que los archivos de la aplicación se transfieren) y ejecutará una tarea para instalar las dependencias de la aplicación (npm install) en el servidor remoto.

      Para escuchar los eventos y ejecutar tareas, Shipit necesita un archivo de configuración que contenga información sobre su servidor remoto (app) y registre las escuchas de eventos y los comandos que estas tareas ejecutarán. Este archivo reside en su máquina de desarrollo local, dentro del directorio de su aplicación de Node.js.

      Para comenzar, cree este archivo; incluyea información sobre su servidor remoto, las escuchas de eventos a las que desee suscribirse y algunas definiciones de sus tareas. Cree shipitfile.js dentro del directorio root de su aplicación, en su máquina local, ejecutando el siguiente comando:

      Ahora que creó un archivo, este debe completarse con la información inicial de entorno que Shipit requiere. Esta consiste principalmente en la ubicación de su repositorio Git remoto y, lo que es más importante, la dirección IP pública y la cuenta de usuario SSH de su servidor app.

      Añada esta configuración inicial y actualice las líneas resaltadas para que coincidan con su entorno:

      shipitfile.js

      module.exports = shipit => {
        require('shipit-deploy')(shipit);
        require('shipit-shared')(shipit);
      
        const appName = 'hello';
      
        shipit.initConfig({
          default: {
            deployTo: '/home/sammy/your-domain',
            repositoryUrl: 'https://git-provider.tld/YOUR_GIT_USERNAME/YOUR_GIT_REPO_NAME.git',
            keepReleases: 5,
            shared: {
              overwrite: true,
              dirs: ['node_modules']
            }
          },
          production: {
            servers: 'sammy@YOUR_APP_SERVER_PUBLIC_IP'
          }
        });
      
        const path = require('path');
        const ecosystemFilePath = path.join(
          shipit.config.deployTo,
          'shared',
          'ecosystem.config.js'
        );
      
        // Our listeners and tasks will go here
      
      };
      

      La actualización de las variables en su método shipit.initConfig proporciona a Shipit la configuración específica para su implementación. Estas representan lo siguiente para Shipit:

      • deployTo: es el directorio en el que Shipit implementará el código de su aplicación en el servidor remoto. En este caso, se utiliza la carpeta /home/ para un usuario no root con privilegios sudo (/home/sammy) debido a que es segura y evitará problemas con los permisos. El componente /your-domain es una convención de nomenclatura para distinguir la carpeta de otras dentro de la carpeta de inicio del usuario.
      • repositoryUrl: es la URL para el repositorio completo de Git; en Shipit se utilizará esta URL para garantizar que los archivos del proyecto se encuentren en sincronización antes de la implementación.
      • keepReleases: es el número de versiones que se deben mantener en el servidor remoto. release es una carpeta fechada que contiene los archivos de su aplicación en el momento de su lanzamiento. Puede ser útil para un rollback de una implementación.
      • shared: es la configuración que se corresponde con keepReleases y permite que los directorios sean shared entre las versiones. En este caso, tenemos una sola carpeta node_modules que comparten todas las versiones.
      • production: representa un servidor remoto para la implementación de su aplicación. En este caso, tiene un único servidor (app) al que asignó el nombre production y la configuración los servers: coincide con su public ip address y user de SSH. El nombre production se corresponde con el comando de implementación de Shipit utilizado al final de este tutorial (npx shipit server name deploy o, en su caso, npx shipit production deploy).

      Se puede encontrar más información sobre el objeto de configuración de implementación de Shipit en el repositorio Shipit de Github.

      Antes de continuar con la actualización de su shipitfile.js, revisaremos el siguiente fragmento de código de ejemplo para entender las tareas de Shipit:

      Example event listener

      shipit.on('deploy', () => { shipit.start('say-hello'); }); shipit.blTask('say-hello', async () => { shipit.local('echo "hello from your local computer"') });

      Esta es una tarea de ejemplo que utiliza el método shipit.on para suscribirse al evento deploy. Esta tarea se esperará que el ciclo de vida de Shipit emita el evento deploy; luego, cuando se reciba el evento, en la tarea se ejecutará el método shipit.start que indica a Shipit que aplique start a la tarea say-hello.

      En el método shipit.on se toman dos parámetros: el nombre del evento que se escuchará y la función de devolución de llamada que se ejecutará cuando se reciba el evento.

      En la declaración del método shipit.on, la tarea se define con el método shipit.blTask. Esto crea una nueva tarea Shipit que bloqueará otras tareas durante su ejecución (es una tarea sincrónica). En el método shipit.blTask también se toman dos parámetros: el nombre de la tarea que define y una función de devolución de llamada que se ejecutará una vez que shipit.start active la tarea.

      En la función de devolución de llamada de este ejemplo (say-hello), método shipit.local ejecuta un comando en la máquina local. En el comando local se emite “hello from your local computer” en el resultado de la terminal.

      Si quisiera ejecutar un comando en el servidor remoto, usaría el método shipit.remote. Los dos métodos, shipit.local y shipit.remote, proporcionan una API para emitir comandos de forma local o remota como parte de una implementación.

      Ahora, actualice shipitfile.js para incluir las escuchas de eventos a fin de suscribirse al ciclo de vida de Shipit con shipit.on. Añada las escuchas de eventos a su shipitfile.js, e insértelos conforme al marcador de posición de comentarios de la configuración inicial // Our tasks will go here:

      shipitfile.js

      . . .
        shipit.on('updated', () => {
          shipit.start('npm-install', 'copy-config');
        });
      
        shipit.on('published', () => {
          shipit.start('pm2-server');
        });
      

      Estos dos métodos escuchan los eventos updated y published que se emiten como parte del ciclo de vida de implementación de Shipit. Cuando se reciba el evento, cada uno iniciará tareas usando el método shipit.start, en un proceso similar al de la tarea de ejemplo.

      Ahora que programó las escuchas, agregará la tarea correspondiente. Añada la siguiente tarea a su shipitfile.js; insértelas después de las escuchas de eventos:

      shipitfile.js

      . . .
      shipit.blTask('copy-config', async () => {
      
      const fs = require('fs');
      
      const ecosystem = `
      module.exports = {
      apps: [
        {
          name: '${appName}',
          script: '${shipit.releasePath}/hello.js',
          watch: true,
          autorestart: true,
          restart_delay: 1000,
          env: {
            NODE_ENV: 'development'
          },
          env_production: {
            NODE_ENV: 'production'
          }
        }
      ]
      };`;
      
        fs.writeFileSync('ecosystem.config.js', ecosystem, function(err) {
          if (err) throw err;
          console.log('File created successfully.');
        });
      
        await shipit.copyToRemote('ecosystem.config.js', ecosystemFilePath);
      });
      

      Primero declara una tarea llamada copy-config. En esta tarea se crea un archivo local llamado ecosystem.config.js y luego se copia ese archivo a su servidor de app remoto. En PM2, este archivo se utiliza para administrar su aplicación de Node.js. Proporciona a PM2 la información necesaria sobre rutas de archivos para garantizar que se ejecuten los últimos archivos que implementó. Más adelante, en el proceso de compilación, creará una tarea que ejecuta PM2 con ecosystem.config.js como configuración.

      Si en su aplicación se necesitan variables de entorno (como una cadena de conexión de base de datos), puede declararlas de forma local, en env:, o en el servidor remoto, en env_production:, aplicando el mismo método con el que estableció la variable Node_ENV en estos objetos.

      Añada la siguiente tarea a su shipitfile.js después de la tarea copy-config:

      shipitfile.js

      . . .
      shipit.blTask('npm-install', async () => {
        shipit.remote(`cd ${shipit.releasePath} && npm install --production`);
      });
      

      A continuación, declare una tarea llamada npm-install. En esta tarea se utiliza una terminal bash remota (a través de shipit.remote) para instalar las dependencias de la aplicación (paquetes npm).

      Añada la última tarea a su shipitfile.js después de la tarea npm-install:

      shipitfile.js

      . . .
      shipit.blTask('pm2-server', async () => {
        await shipit.remote(`pm2 delete -s ${appName} || :`);
        await shipit.remote(
          `pm2 start ${ecosystemFilePath} --env production --watch true`
        );
      });
      

      Por último, declare una tarea llamada pm2-server. En esta tarea, también se utiliza una terminal bash remota para primero evitar que PM2 administre su implementación anterior a través del comando delete y luego iniciar una nueva instancia de su servidor Node.js proporcionando el archivo ecosystem.config.js como una variable. También se notifica a PM2 que debe usar variables de entorno del bloque production en su configuración inicial y se solicita, también a PM2, que controle la aplicación y la reinicie si falla.

      El archivo shipitfile.js completo:

      shipitfile.js

      module.exports = shipit => {
        require('shipit-deploy')(shipit);
        require('shipit-shared')(shipit);
      
        const appName = 'hello';
      
        shipit.initConfig({
          default: {
            deployTo: '/home/deployer/example.com',
            repositoryUrl: 'https://git-provider.tld/YOUR_GIT_USERNAME/YOUR_GIT_REPO_NAME.git',
            keepReleases: 5,
            shared: {
              overwrite: true,
              dirs: ['node_modules']
            }
          },
          production: {
            servers: 'deployer@YOUR_APP_SERVER_PUBLIC_IP'
          }
        });
      
        const path = require('path');
        const ecosystemFilePath = path.join(
          shipit.config.deployTo,
          'shared',
          'ecosystem.config.js'
        );
      
        // Our listeners and tasks will go here
        shipit.on('updated', async () => {
          shipit.start('npm-install', 'copy-config');
        });
      
        shipit.on('published', async () => {
          shipit.start('pm2-server');
        });
      
        shipit.blTask('copy-config', async () => {
          const fs = require('fs');
          const ecosystem = `
      module.exports = {
        apps: [
          {
            name: '${appName}',
            script: '${shipit.releasePath}/hello.js',
            watch: true,
            autorestart: true,
            restart_delay: 1000,
            env: {
              NODE_ENV: 'development'
            },
            env_production: {
              NODE_ENV: 'production'
            }
          }
        ]
      };`;
      
          fs.writeFileSync('ecosystem.config.js', ecosystem, function(err) {
            if (err) throw err;
            console.log('File created successfully.');
          });
      
          await shipit.copyToRemote('ecosystem.config.js', ecosystemFilePath);
        });
      
        shipit.blTask('npm-install', async () => {
          shipit.remote(`cd ${shipit.releasePath} && npm install --production`);
        });
      
        shipit.blTask('pm2-server', async () => {
          await shipit.remote(`pm2 delete -s ${appName} || :`);
          await shipit.remote(
            `pm2 start ${ecosystemFilePath} --env production --watch true`
          );
        });
      };
      

      Guarde y cierre el archivo cuando esté listo.

      Con su shipitfile.js configurado, las escuchas de eventos y las tareas asociadas completadas, puede proceder con la implementación en el servidor app.

      Paso 5: Implementar su aplicación

      En este paso, implementará su aplicación de forma remota y verificará que la implementación haya permitido la disponibilidad de su aplicación en Internet.

      Debido a que en Shipit se clonan los archivos de proyecto del repositorio remoto de Git, debe mover los archivos locales de su aplicación de Node.js de su máquina local a Github. Diríjase al directorio de la aplicación de su proyecto de Node.js (donde se ubican hello.js y shiptitfile.js) y ejecute el siguiente comando:

      Con el comando git status se muestra el estado del directorio de trabajo y el área de ensayos. Le permite ver los cambios que se prepararon o no, y los archivos que no se sometieron a seguimiento a través de Git. Sus archivos no se encuentran bajo seguimiento y aparecen en rojo en el resultado:

      Output

      On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) hello.js package-lock.json package.json shipitfile.js nothing added to commit but untracked files present (use "git add" to track)

      Puede añadir estos archivos a su repositorio con el siguiente comando:

      Con este comando no se produce ningún resultado, pero si ejecutara git status de nuevo los archivos se mostrarían en verde con una nota que señalaría la necesidad de confirmar cambios.

      Puede crear una confirmación ejecutando el siguiente comando:

      • git commit -m "Our first commit"

      En el resultado de este comando se proporciona información específica de Git sobre los archivos.

      Output

      [master c64ea03] Our first commit 4 files changed, 1948 insertions(+) create mode 100644 hello.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 shipitfile.js

      Lo único que queda por hacer ahora es enviar su confirmación al repositorio remoto para que Shipit realice una clonación a su servidor app durante la implementación. Ejecute el siguiente comando:

      En el resultado, se incluye información acerca de la sincronización con el repositorio remoto:

      Output

      Enumerating objects: 7, done. Counting objects: 100% (7/7), done. Delta compression using up to 8 threads Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 15.27 KiB | 7.64 MiB/s, done. Total 6 (delta 0), reused 0 (delta 0) To github.com:Asciant/hello-world.git e274312..c64ea03 master -> master

      Para implementar su aplicación, ejecute el siguiente comando:

      • npx shipit production deploy

      En el resultado de este comando (que es demasiado largo para incluirlo en su totalidad) se proporcionan detalles sobre las tareas que se ejecutan y el resultado de la función específica. En el resultado que se observa a continuación para la tarea pm2-server se muestra que la aplicación Node.js se ha iniciado:

      Output

      Running 'deploy:init' task... Finished 'deploy:init' after 432 μs . . . Running 'pm2-server' task... Running "pm2 delete -s hello || :" on host "centos-ap-app.asciant.com". Running "pm2 start /home/deployer/example.com/shared/ecosystem.config.js --env production --watch true" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com [PM2][WARN] Node 4 is deprecated, please upgrade to use pm2 to have all features @centos-ap-app.asciant.com [PM2][WARN] Applications hello not running, starting... @centos-ap-app.asciant.com [PM2] App [hello] launched (1 instances) @centos-ap-app.asciant.com ┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────────┬──────────┐ @centos-ap-app.asciant.com │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ @centos-ap-app.asciant.com ├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────────┼──────────┤ @centos-ap-app.asciant.com │ hello │ 0 │ 1.0.0 │ fork │ 4177 │ online │ 0 │ 0s │ 0% │ 4.5 MB │ deployer │ enabled │ @centos-ap-app.asciant.com └──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────────┴──────────┘ @centos-ap-app.asciant.com Use `pm2 show <id|name>` to get more details about an app Finished 'pm2-server' after 5.27 s Running 'deploy:clean' task... Keeping "5" last releases, cleaning others Running "(ls -rd /home/deployer/example.com/releases/*|head -n 5;ls -d /home/deployer/example.com/releases/*)|sort|uniq -u|xargs rm -rf" on host "centos-ap-app.asciant.com". Finished 'deploy:clean' after 1.81 s Running 'deploy:finish' task... Finished 'deploy:finish' after 222 μs Finished 'deploy' [ deploy:init, deploy:fetch, deploy:update, deploy:publish, deploy:clean, deploy:finish ]

      Para ver su aplicación como la vería un usuario, puede ingresar la URL de su sitio web your-domain en su navegador a fin de acceder a su servidor web. Esto proporcionará la aplicación de Node.js, a través de un proxy inverso, en el servidor app en el que se implementaron sus archivos.

      Verá un saludo Hello World.

      Nota: Después de la primera implementación, en su repositorio de Git se hará un seguimiento de un archivo nuevo llamado ecosystem.config.js. Debido a que este archivo se reconstruye en cada implementación y puede contener secretos de la aplicación compilados, se debe añadir al archivo .gitignore en el directorio root de la aplicación de su máquina local antes de su próxima confirmación de git.

      .gitignore

      . . .
      # ecosystem.config
      ecosystem.config.js
      

      Implementó su aplicación Node.js en el servidor app, que hace referencia a su nueva implementación. Con todo configurado y en funcionamiento, puede proceder con la supervisión de los procesos de su aplicación.

      Paso 6: Controlar su aplicación

      PM2 es una excelente herramienta para administrar sus procesos remotos, pero también incluye funciones para controlar el rendimiento de estos procesos de aplicación.

      Establezca conexión con su servidor app remoto mediante SSH con el siguiente comando:

      • ssh deployer@your_app_server_ip

      Para obtener información específica relacionada con los procesos administrados de PM2, ejecute lo siguiente:

      Verá un resultado similar a lo siguiente:

      Output

      ┌─────────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬──────┬───────────┬──────────┬──────────┐ │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├─────────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼──────┼───────────┼──────────┼──────────┤ │ hello │ 0 │ 0.0.1 │ fork │ 3212 │ online │ 0 │ 62m │ 0.3% │ 45.2 MB │ deployer │ enabled │ └─────────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴──────┴───────────┴──────────┴──────────┘

      Verá un resumen de la información recopilada por PM2. Para ver información detallada, puede ejecutar lo siguiente:

      En el resultado, se amplía la información del resumen proporcionada por el comando pm2 list. También proporciona información sobre varios comandos suplementarios y también la ubicación de los archivos de registro:

      Output

      Describing process with id 0 - name hello ┌───────────────────┬─────────────────────────────────────────────────────────────┐ │ status │ online │ │ name │ hello │ │ version │ 1.0.0 │ │ restarts │ 0 │ │ uptime │ 82s │ │ script path │ /home/deployer/example.com/releases/20190531213027/hello.js │ │ script args │ N/A │ │ error log path │ /home/deployer/.pm2/logs/hello-error.log │ │ out log path │ /home/deployer/.pm2/logs/hello-out.log │ │ pid path │ /home/deployer/.pm2/pids/hello-0.pid │ │ interpreter │ node │ │ interpreter args │ N/A │ │ script id │ 0 │ │ exec cwd │ /home/deployer │ │ exec mode │ fork_mode │ │ node.js version │ 4.2.3 │ │ node env │ production │ │ watch & reload │ ✔ │ │ unstable restarts │ 0 │ │ created at │ 2019-05-31T21:30:48.334Z │ └───────────────────┴─────────────────────────────────────────────────────────────┘ Revision control metadata ┌──────────────────┬────────────────────────────────────────────────────┐ │ revision control │ git │ │ remote url │ N/A │ │ repository root │ /home/deployer/example.com/releases/20190531213027 │ │ last update │ 2019-05-31T21:30:48.559Z │ │ revision │ 62fba7c8c61c7769022484d0bfa46e756fac8099 │ │ comment │ Our first commit │ │ branch │ master │ └──────────────────┴────────────────────────────────────────────────────┘ Divergent env variables from local env ┌───────────────────────────┬───────────────────────────────────────┐ │ XDG_SESSION_ID │ 15 │ │ HOSTNAME │ N/A │ │ SELINUX_ROLE_REQUESTED │ │ │ TERM │ N/A │ │ HISTSIZE │ N/A │ │ SSH_CLIENT │ 44.222.77.111 58545 22 │ │ SELINUX_USE_CURRENT_RANGE │ │ │ SSH_TTY │ N/A │ │ LS_COLORS │ N/A │ │ MAIL │ /var/mail/deployer │ │ PATH │ /usr/local/bin:/usr/bin │ │ SELINUX_LEVEL_REQUESTED │ │ │ HISTCONTROL │ N/A │ │ SSH_CONNECTION │ 44.222.77.111 58545 209.97.167.252 22 │ └───────────────────────────┴───────────────────────────────────────┘ . . .

      Además, PM2 proporciona una herramienta de supervisión en terminal a la que es posible acceder con lo siguiente:

      Como resultado de este comando se obtiene un panel interactivo, en el cual pm2 proporciona registros, métricas, metadatos e información sobre procesos en tiempo real. Este panel puede ser útil para controlar los recursos y registros de errores:

      Output

      ┌─ Process list ────────────────┐┌─ Global Logs ─────────────────────────────────────────────────────────────┐ │[ 0] hello Mem: 22 MB ││ │ │ ││ │ │ ││ │ └───────────────────────────────┘└───────────────────────────────────────────────────────────────────────────┘ ┌─ Custom metrics (http://bit.l─┐┌─ Metadata ────────────────────────────────────────────────────────────────┐ │ Heap Size 10.73 ││ App Name hello │ │ Heap Usage 66.14 ││ Version N/A │ │ Used Heap Size 7.10 ││ Restarts 0 │ │ Active requests 0 ││ Uptime 55s │ │ Active handles 4 ││ Script path /home/asciant/hello.js │ │ Event Loop Latency 0.70 ││ Script args N/A │ │ Event Loop Latency p95 ││ Interpreter node │ │ ││ Interpreter args N/A │ └───────────────────────────────┘└───────────────────────────────────────────────────────────────────────────┘

      Al comprender la forma en la que puede supervisar sus procesos con PM2, ahora puede considerar la forma en la que Shipit puede asistir en la reversión a una implementación funcional anterior.

      Finalice su sesión ssh en su servidor app ejecutando exit.

      Paso 7: Revertir una implementación defectuosa

      En las implementaciones, de tanto en tanto se exhiben errores imprevistos o problemas que pueden causar fallas en su sitio. Los desarrolladores y los encargados de mantenimiento de Shipit previeron esto y proporcionaron la capacidad de restablecer la implementación (funcional) anterior de su aplicación.

      Para garantizar que su configuración de PM2 persista, añada otra escucha de eventos a shipitfile.js en el evento rollback.

      shipitfile.js

      . . .
        shipit.on('rollback', () => {
          shipit.start('npm-install', 'copy-config');
        });
      

      Debe añadir una escucha al evento rollback para ejecutar sus tareas npm-install y copy-config. Esto se requiere porque, a diferencia del evento published, el evento updated no se ejecuta a través del ciclo de vida de Shipit cuando se revierte una implementación. Añadir esta escucha garantiza que su administrador de procesos PM2 se oriente hacia la implementación más reciente, incluso en caso de una reversión.

      Este proceso es similar a la implementación, con un pequeño cambio de comando. Para intentar restablecer a una implementación anterior, puede ejecutar lo siguiente:

      • npx shipit production rollback

      Al igual que el comando deploy, rollback proporciona detalles sobre el proceso de reversión y las tareas que se ejecutan:

      Output

      Running 'rollback:init' task... Get current release dirname. Running "if [ -h /home/deployer/example.com/current ]; then readlink /home/deployer/example.com/current; fi" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com releases/20190531213719 Current release dirname : 20190531213719. Getting dist releases. Running "ls -r1 /home/deployer/example.com/releases" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com 20190531213719 @centos-ap-app.asciant.com 20190531213519 @centos-ap-app.asciant.com 20190531213027 Dist releases : ["20190531213719","20190531213519","20190531213027"]. Will rollback to 20190531213519. Finished 'rollback:init' after 3.96 s Running 'deploy:publish' task... Publishing release "/home/deployer/example.com/releases/20190531213519" Running "cd /home/deployer/example.com && if [ -d current ] && [ ! -L current ]; then echo "ERR: could not make symlink"; else ln -nfs releases/20190531213519 current_tmp && mv -fT current_tmp current; fi" on host "centos-ap-app.asciant.com". Release published. Finished 'deploy:publish' after 1.8 s Running 'pm2-server' task... Running "pm2 delete -s hello || :" on host "centos-ap-app.asciant.com". Running "pm2 start /home/deployer/example.com/shared/ecosystem.config.js --env production --watch true" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com [PM2][WARN] Node 4 is deprecated, please upgrade to use pm2 to have all features @centos-ap-app.asciant.com [PM2][WARN] Applications hello not running, starting... @centos-ap-app.asciant.com [PM2] App [hello] launched (1 instances) @centos-ap-app.asciant.com ┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────────┬──────────┐ @centos-ap-app.asciant.com │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ @centos-ap-app.asciant.com ├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────────┼──────────┤ @centos-ap-app.asciant.com │ hello │ 0 │ 1.0.0 │ fork │ 4289 │ online │ 0 │ 0s │ 0% │ 4.5 MB │ deployer │ enabled │ @centos-ap-app.asciant.com └──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────────┴──────────┘ @centos-ap-app.asciant.com Use `pm2 show <id|name>` to get more details about an app Finished 'pm2-server' after 5.55 s Running 'deploy:clean' task... Keeping "5" last releases, cleaning others Running "(ls -rd /home/deployer/example.com/releases/*|head -n 5;ls -d /home/deployer/example.com/releases/*)|sort|uniq -u|xargs rm -rf" on host "centos-ap-app.asciant.com". Finished 'deploy:clean' after 1.82 s Running 'rollback:finish' task... Finished 'rollback:finish' after 615 μs Finished 'rollback' [ rollback:init, deploy:publish, deploy:clean, rollback:finish ]

      Configuró Shipit para mantener 5 versiones a través del ajuste keepReleases: 5 en shipitfile.js. Shipit realiza un seguimiento interno de estas versiones para garantizar que la reversión sea posible cuando se necesite. Shipit también proporciona una alternativa práctica para identificar las versiones creando un directorio con el nombre de una marca de tiempo (AAAAMMDDHHmmss; por ejemplo, /home/deployer/your-domain/releases/20190420210548).

      Si desea personalizar aún más el proceso de reversión, puede escuchar eventos específicos de la operación de reversión. Luego, puede utilizarlos para ejecutar tareas que complementarán su reversión. Puede consultar la lista de eventos proporcionada en el desglose del ciclo de vida de Shipit, y configurar las tareas y escuchas dentro de su shipitfile.js.

      La capacidad de reversión implica que siempre puede proporcionar a sus usuarios una versión funcional de su aplicación, incluso si en una implementación se producen errores o problemas inesperados.

      Conclusión

      A lo largo de este tutorial, configuró un flujo de trabajo que le permite crear una alternativa sumamente personalizable a las plataformas como servicio, todo ello desde algunos servidores. Este flujo de trabajo permite personalizar la implementación y la configuración, controlar procesos con PM2, escalar y agregar servicios, o contar con servidores o entornos adicionales en la implementación cuando sea necesario.

      Si está interesado en continuar desarrollando sus aptitudes para Node.js, consulte el contenido de Node.js de DigtalOcean y la serie Cómo producir código en Node.js.



      Source link

      Cómo configurar una aplicación de Node.js para producción en Ubuntu 18.04


      Introducción

      Node.js es un entorno de tiempo de ejecución de código abierto basado en JavaScript que use usa para crear aplicaciones de redes. La plataforma funciona en Linux, macOS, FreeBSD y Windows. Aunque puede ejecutar aplicaciones de Node.js en la línea de comandos, en este tutorial se pondrá el foco en su ejecución como un servicio. Esto significa que se reiniciarán durante el inicio o ante una falla y que pueden usarse de forma segura en un entorno de producción.

      A través de este tutorial, configurará un entorno de Node.js listo para producción en un servidor único de Ubuntu 18.04. Este servidor ejecutará una aplicación de Node.js administrada por PM2 y brindará a los usuarios acceso seguro a la aplicación mediante un proxy inverso de Nginx. El servidor de Nginx brindará HTTPS usando un certificado gratuito proporcionado por Let’s Encrypt.

      Requisitos previos

      Para esta guía, se supone cuenta con lo siguiente:

      Una vez que cumpla con los requisitos previos, dispondrá de un servidor en el que funcionará la página de marcador de posición predeterminada de su dominio en https://example.com/.

      Paso 1: Instalar Node.js

      Comencemos instalando la versión más reciente de LTS de Node.js con los archivos de paquete de NodeSource.

      Primero, instale el PPA de NodeSource para poder acceder a su contenido. Asegúrese de estar posicionado en su directorio de inicio y use curl para recuperar la secuencia de instalación para los archivos de Node.js 8.x:

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh

      Puede inspeccionar el contenido de esta secuencia de comandos con nano o su editor de texto preferido:

      Cuando termine de inspeccionar la secuencia, ejecútela con sudo:

      • sudo bash nodesource_setup.sh

      El PPA se agregará a su configuración y su caché de paquetes locales se actualizará de forma automática. Después de ejecutar la secuencia de comandos de configuración de Nodesource, puede instalar el paquete de Node.js.

      Para comprobar la versión de Node.js que instaló después de estos pasos iniciales, escriba lo siguiente:

      Output

      v8.11.3

      Nota: Cuando la instalación se realiza a partir del PPA de NodeSource, el ejecutable de Node.js se llama nodejs, en lugar de node.

      El paquete nodejs contiene el binario nodejs y npm, un administrador de paquetes para módulos de Node, por lo que no tendrá que instalar npm por separado.

      npm usa un archivo de configuración en su directorio de inicio para hacer un seguimiento de las actualizaciones. Se creará la primera vez que ejecute npm. Ejecute este comando para verificar que npm esté instalado y crear el archivo de configuración:

      Output

      5.6.0

      Para que algunos paquetes de npm funcionen (por ejemplo, aquellos para los cuales se seba compilar código de una fuente), deberá instalar el paquete build-essential:

      • sudo apt install build-essential

      Ahora dispondrá de las herramientas necesarias para trabajar con paquetes npm para los que se deba compilar código de la fuente.

      Ahora que está instalado el tiempo de ejecución de Node.js, escribiremos una la aplicación de Node.js.

      Paso 2: Crear una aplicación de Node.js

      Escribiremos una aplicación Hello World que responda “Hello World” a cualquier solicitud de HTTP. Con esta aplicación de ejemplo, podrá a configurar Node.js. Puede reemplazarla por su propia aplicación; solo debe asegurarse de modificar su aplicación para escuchar en las direcciones IP y los puertos apropiados.

      Primero, crearemos una aplicación de ejemplo llamada hello.js:

      Inserte el siguiente código en el archivo:

      ~/hello.js

      const http = require('http');
      
      const hostname = 'localhost';
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Guarde el archivo y salga del editor.

      La aplicación Node.js escucha la dirección (localhost) y el puerto (3000) especificados, y responde “Hello World!” con un código de éxito HTTP 200. Debido a que escucharemos en localhost, los clientes remotos no podrán conectarse a nuestra aplicación.

      Para probar su aplicación, escriba lo siguiente:

      Verá el siguiente resultado:

      Output

      Server running at http://localhost:3000/

      Nota: Ejecutar una aplicación de Node.js de esta manera bloqueará comandos adicionales hasta que esta se detenga pulsando CTRL+C.

      Para probar la aplicación, abra otra sesión de terminal en su servidor y conéctese a localhost con curl:

      • curl http://localhost:3000

      Si ve el siguiente resultado, significa que la aplicación funciona de forma adecuada y escucha en las direcciones y los puertos correctos.

      Output

      Hello World!

      Si no ve el resultado esperado, asegúrese de que su aplicación de Node.js funcione y esté configurada para escuchar en la dirección y el puerto apropiados.

      Una vez que esté seguro de que funciona, deténgala (si aún no lo hizo) presionando CTRL+C.

      Paso 3: Instalar PM2

      A continuación, instalaremos PM2, un administrador de procesos para aplicaciones de Node.js. PM2 permite implementar demonios en aplicaciones para que puedan funcionar en segundo plano como servicios.

      Use npm para instalar la última versión de PM2 en su servidor:

      • sudo npm install pm2@latest -g

      La opción -g indica a npm que instale el módulo de forma global, de modo que esté disponible en todo el sistema.

      Primero, usaremos el comando pm2 start para ejecutar su aplicación, hello.js, en segundo plano:

      Con esto, también se agrega su aplicación a la lista de procesos de PM2, que se emite cada vez que se inicia una aplicación:

      Output

      [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤ │ hello │ 0 │ fork │ 1338 │ online │ 0 │ 0s │ 0% │ 23.0 MB │ sammy │ disabled │ └──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app

      Como puede ver, PM2 asigna automáticamente un App name (según el nombre del archivo, sin la extensión .js) y un id de PM2. También conversa otra información, como el PID de los procesos, su estado actual y su uso de memoria.

      Las aplicaciones que se ejecutan en PM2 se reiniciarán de forma automática si la aplicación se bloquea o se detiene, pero podemos dar un paso adicional para que se cargue en el inicio del sistema usando el subcomando startup. Este subcomando genera y configura una secuencia de comandos de inicio para iniciar PM2 y sus procesos administrados al iniciarse el servidor:

      En la última línea del resultado obtenido se incluirá un comando que se ejecutará con privilegios de superusuario a fin de configurar PM2 para que se cargue en el inicio:

      Output

      [PM2] Init System found: systemd [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Ejecute el comando del resultado, con su nombre de usuario en lugar de sammy:

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Como paso adicional, podemos guardar la lista de procesos de PM2 y los entornos correspondientes:

      De esta manera, habrá creado una unidad de systemd que ejecuta pm2 para su usuario en el inicio. Esta instancia de pm2, a su vez, ejecuta hello.js.

      Inicie el servicio con systemctl:

      • sudo systemctl start pm2-sammy

      Compruebe el estado de la unidad de systemd:

      • systemctl status pm2-sammy

      Para hallar una descripción detallada de systemd, consulte Aspectos básicos de systemd: trabajar con servicios, unidades y el componente de diario.

      Además de los subcomandos que ya abarcamos, PM2 proporciona muchos que le permiten administrar o buscar información sobre sus aplicaciones.

      Detenga una aplicación con este comando (especifique App name o id de PM2):

      Reinicie una aplicación:

      • pm2 restart app_name_or_id

      Liste las aplicaciones actualmente administradas por PM2:

      Obtenga información sobre una aplicación específica usando su App name:

      El monitor de procesos de PM2 se puede extraer con el subcomando monit. Con esto, se muestra el estado y el uso de CPU y memoria de la aplicación:

      Tenga en cuenta que si se ejecuta pm2 sin argumentos, también se mostrará una página de ayuda con uso de ejemplos.

      Ahora que su aplicación de Node.js funciona y PM2 la administra, configuraremos el proxy inverso.

      Paso 4: Configurar Nginx como servidor proxy inverso

      Su aplicación está en ejecución y escucha en localhost, pero necesita configurar una alternativa para que sus usuarios accedan a ella. Configuraremos el servidor web de Nginx como un proxy inverso para este propósito.

      En el tutorial de los requisitos previos, configuró sus ajustes de Nginx en el archivo /etc/nginx/sites-available/example.com. Abra este archivo para editarlo:

      • sudo nano /etc/nginx/sites-available/example.com

      En el bloque server, debería disponer de un bloque location /. Sustituya el contenido de dicho bloque por la siguiente configuración. Si su aplicación está configurada para escuchar en un puerto diferente, actualice la parte resaltada con el número de puerto correcto:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Con esto, se configurará el servidor para que responda las solicitudes en su root. Suponiendo que nuestro servidor esté disponible en example.com, al acceder a https://example.com/ a través de un navegador web se enviaría la solicitud a hello.js y la escucha se realizaría en el puerto 3000 en localhost.

      Puede agregar bloques location al mismo bloque de servidor para proporcionar acceso a otras aplicaciones en el mismo servidor. Por ejemplo, si también contaba con otra aplicación Node.js en el puerto 3001, podría agregar este bloque de ubicación para permitir el acceso a él a través de https://example.com/app2:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Una vez que termine de agregar los bloques de ubicación a sus aplicaciones, guarde el archivo y cierre el editor.

      Asegúrese de no haber introducido errores de sintaxis escribiendo lo siguiente:

      Reinicie Nginx:

      • sudo systemctl restart nginx

      Suponiendo que su aplicación Node.js está en ejecución, y que su aplicación y las configuraciones de Nginx son correctas, ahora debería poder acceder a su aplicación a través del proxy inverso de Nginx. Pruébelo accediendo a la URL de su servidor (su dirección IP pública o nombre de dominio).

      Conclusión

      ¡Felicitaciones! De esta manera, habrá logrado hacer funcionar su aplicación de Node.js detrás de un proxy inverso de Nginx en un servidor de Ubuntu 18.04. Esta configuración de proxy inverso es suficientemente flexible como para proporcionar a sus usuarios acceso a otras aplicaciones o a contenido web estático que desee compartir.



      Source link