One place for hosting & domains

      Shipit

      Como automatizar suas implantações Node.js para produção com o Shipit no CentOS 7


      O autor selecionou a Fundação Electronic Frontier para receber uma doação como parte do programa Write for DOnations.

      Introdução

      O Shipit é uma ferramenta de automação e implantação universal para desenvolvedores do Node.js. Ela possui um fluxo de tarefas baseado no pacote popular Orchestrator, comandos de login e SSH interativo através do OpenSSH, além de uma API extensível. Os desenvolvimentos podem usar o Shipit para automatizar os fluxos de trabalho de compilação e DE implantação para uma ampla gama de aplicativos Node.js.

      O fluxo de trabalho do Shipit permite que os desenvolvedores não apenas configurem tarefas, mas também especifiquem a ordem na qual são executadas; se devem ser executadas de maneira sincronizada ou não e em qual ambiente.

      Neste tutorial, você instalará e configurará o Shipit para implantar um aplicativo Node.js a partir do seu ambiente de desenvolvimento local para o seu ambiente de produção. Você usará o Shipit para implantar seu aplicativo e configurar o servidor remoto desta forma:

      • transferindo os arquivos do aplicativo Node.js do seu ambiente local para o ambiente de produção (usando o rsync, git e ssh).
      • instalando as dependências do seu aplicativo (módulos de nó)
      • configurando e gerenciando os processos Node.js em execução no servidor remoto com o PM2.

      Pré-requisitos

      Antes de iniciar este tutorial, você precisará do seguinte:

      • Dois servidores CentOS 7 (neste tutorial, eles serão chamados de app e web), configurados com rede privada, seguindo as instruções do tutorial Como configurar um aplicativo Node.js para produção no CentOS 7.
      • Nginx (no seu servidor web), protegido com o protocolo TLS/SSL como mostrado no tutorial Como proteger o Nginx com o Let’s Encrypt no CentOS 7. Note que, se você estiver seguindo as instruções dos pré-requisitos em ordem cronológica, para o seu servidor web você só terá que realizar os passos 1, 4 e 6.
      • Node.js e npm instalados em seu ambiente de desenvolvimento. Este tutorial usa a versão 10.17.0. Para instalar essa versão em macOS ou Ubuntu 18.04, siga os passos descritos no artigo sobre Como instalar o Node.js e criar um ambiente de desenvolvimento local em macOS ou a seção intitulada Instalando usando um PPA, do artigo sobre Como instalar o Node.js no Ubuntu 18.04. Por ter o Node.js instalado, você também tem o npm instalado; este tutorial usa a versão 6.11.3.
      • Um computador de desenvolvimento local com o rsync e git instalados.
      • Uma conta junto ao GitHub ou outro provedor de serviços git hospedados. Este tutorial usará o GitHub.

      Nota: os usuários do Windows terão que instalar o Subsistema Windows para o Linux para executar os comandos neste guia.

      Passo 1 — Configurando o repositório remoto

      O Shipit exige um repositório Git para a sincronização entre a máquina de desenvolvimento local e o servidor remoto. Neste passo, você criará um repositório remoto no Github.com. Embora cada provedor seja ligeiramente diferente, os comandos são de certo modo transferíveis.

      Para criar um repositório, abra o Github.com em seu navegador Web e faça login. Você notará que, no canto superior de todas as páginas, existe um símbolo de adição +. Clique no + e, em seguida, clique em Novo repositório.

      Github-novo-repositório

      Digite um nome curto e fácil de lembrar para o seu repositório como ,por exemplo, hello-world. Note que, independentemente do nome que escolher aqui, ele será replicado como a pasta do projeto a partir da qual você irá trabalhar em sua máquina local.

      Github-nome-repositório

      De maneira opcional, adicione uma descrição do seu repositório.

      Github-descrição-repositório

      Defina a visibilidade do seu repositório de acordo com sua preferência, como sendo pública ou privada.

      Certifique-se de que o repositório seja inicializado com um .gitignore, selecionando Node na lista suspensa Add .gitignore. Este passo é importante para evitar que arquivos desnecessários (como a pasta node_modules) sejam adicionados ao seu repositório.

      Github-nó-gitignore

      Clique no botão Criar repositório.

      Agora, o repositório precisa ser clonado do Github.com para sua máquina local.

      Abra seu terminal e navegue até o local onde quer armazenar todos os seus arquivos do projeto Node.js. Note que este processo criará uma subpasta dentro do diretório atual. Para clonar o repositório para sua máquina local, execute o seguinte comando:

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

      Você precisará substituir o your-github-username e your-github-repository-name para que reflitam seu nome de usuário do Github e o nome do repositório anteriormente fornecido.

      Nota: caso tenha habilitado a autenticação de dois fatores (2FA) no Github.com, ao acessar o Github na linha de comando, você deverá usar um token de acesso pessoal ou uma chave SSH, em vez de sua senha. A página de ajuda do Github relacionada à 2FA fornece mais informações.

      Você verá um resultado parecido com este:

      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.

      Vá até o repositório, executando o seguinte comando:

      • cd your-github-repository-name

      Dentro do repositório, há um arquivo e uma pasta únicos, sendo ambos arquivos usados pelo Git para gerenciar o repositório. Você pode verificar isso com:

      Você verá um resultado parecido com este:

      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

      Agora que você configurou um repositório git funcional, irá criar o arquivo shipit.js que gerencia o seu processo de implantação.

      Passo 2 — Integrando o Shipit a um projeto Node.js

      Neste passo, você criará um projeto Node.js como exemplo e, depois, adicionará os pacotes do Shipit. Este tutorial fornece um app exemplo — o servidor web Node.js que aceita solicitações HTTP e responde com um Hello World em texto simples. Para criar o aplicativo, execute o seguinte comando:

      Adicione o seguinte código de aplicativo exemplo ao hello.js (atualizando a variável APP_PRIVATE_IP_ADDRESS com o endereço IP da rede privada do servidor do seu 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/');
      

      Agora, crie seu arquivo package.json para seu aplicativo:

      Esse comando cria um arquivo package.json, que você usará para configurar seu aplicativo Node.js. No próximo passo, você adicionará dependências a esse arquivo com a interface de linha de comando 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" }

      Em seguida, instale os pacotes npm necessários com o seguinte comando:

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

      Aqui, você usará o sinalizador --save-dev, uma vez que os pacotes do Shipit somente são necessários em sua máquina local. Você verá um resultado parecido com este:

      Output

      + [email protected]4.4.2 + [email protected]4.2.0 + [email protected]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

      Isso também adicionou os três pacotes ao seu arquivo package.json como dependências de desenvolvimento:

      package.json

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

      Com seu ambiente local configurado, agora você pode prosseguir com a preparação do servidor app remoto para implantações baseadas no Shipit.

      Passo 3 — Preparando o servidor app remoto

      Neste passo, você usará o ssh para se conectar ao seu servidor app e instalar sua dependência remota rsync. O Rsync é um utilitário para transferir e sincronizar arquivos de modo eficiente entre os discos do computador local e os computadores em rede, comparando os tempos de modificação e os tamanhos dos arquivos.

      O Shipit usa o rsync para transferir e sincronizar arquivos entre seu computador local e o servidor app remoto. Você não emitirá qualquer comando diretamente no rsync; o Shipit fará isso por você.

      Nota: o tutorial Como configurar um aplicativo Node.js para produção no CentOS 7 possibilitou que você ficasse com dois servidores, app e web. Esses comandos devem ser executados apenas no app.

      Conecte-se ao seu servidor app remoto via ssh:

      Instale o rsync no seu servidor, executando o seguinte comando:

      Confirme a instalação com:

      Você verá uma linha semelhante no resultado deste comando:

      Output

      rsync version 3.1.2 protocol version 31 . . .

      Você pode encerrar sua sessão ssh, digitando exit.

      Com o rsync instalado e disponível na linha de comando, você pode prosseguir com as tarefas de implantação e sua relação com eventos.

      Passo 4 — Configurando e executando tarefas de implantação

      Tanto events quanto tasks são componentes chave das implantações Shipit. É importante entender como eles complementam a implantação do seu aplicativo. Os eventos desencadeados pelo Shipit representam pontos específicos no ciclo de vida da implantação. Suas tarefas executarão em reposta a esses eventos, com base na sequência do clico de vida do Shipit.

      Um exemplo comum de onde esse sistema de tarefa/evento é útil em um aplicativo Node.js é na instalação das dependências do app (node_modules), no servidor remoto. Mais adiante neste passo, você fará o Shipit escutar para o evento updated (que é emitido depois que os arquivos do aplicativo são transferidos) e executar uma tarefa para instalar as dependências do aplicativo (npm install) no servidor remoto.

      Para escutar eventos e executar tarefas, o Shipit precisa de um arquivo de configuração que possua informações sobre seu servidor remoto (o servidor app) e registre ouvintes de eventos e os comandos a serem executados por essas tarefas. Esse arquivo fica no seu computador de desenvolvimento local, no diretório do seu aplicativo Node.js.

      Para começar, crie esse arquivo, incluindo informações sobre seu servidor remoto, os ouvintes de eventos nos quais quer se inscrever e algumas definições das suas tarefas. Crie o shipitfile.js no diretório raiz do seu aplicativo, em sua máquina local, executando o seguinte comando:

      Agora que você criou um arquivo, ele precisa ser preenchido com as informações iniciais do ambiente que o Shipit precisa. Basicamente, esse é o local do seu repositório Git remoto e, mais importante que tudo, do endereço IP público e da conta de usuário SSH do seu servidor app.

      Adicione essa configuração inicial e atualize as linhas destacadas para que correspondam ao seu ambiente:

      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
      
      };
      

      Atualizando as variables no seu método shipit.initConfig dá ao Shipit uma configuração específica para sua implantação. Essas variáveis representam o seguinte para o Shipit:

      • deployTo: é o diretório onde o Shipit implantará o código do seu aplicativo no servidor remoto. Aqui, você usa a pasta /home/ para um usuário não raiz com privilégios sudo (/home/sammy) uma vez que é segura e evitará problemas de permissão. O componente /your-domain é uma convenção de nomenclatura para distinguir a pasta das demais na pasta inicial do usuário.
      • repositoryUrl: é a URL do repositório completo do Git. O Shipit usará essa URL para garantir que os arquivos do projeto estejam em sincronia antes da implantação.
      • keepReleases: é o número de lançamentos a ser mantido no servidor remoto. Um release (lançamento) é uma pasta com data que contém os arquivos do seu aplicativo no momento do lançamento. Eles podem ser úteis para rollback (reverter) uma implantação.
      • shared: é a configuração que corresponde aos keepReleases que permite que os diretórios sejam shared (compartilhados) entre os lançamentos. Nessa instância, temos uma única pasta node_modules, que é compartilhada entre todos os lançamentos.
      • production: representa um servidor remoto para o qual implantar seu aplicativo. Nessa instância, você tem um servidor (servidor app) único, ao qual você dá o nome de production, com o servers: configuração que correspondendo ao seu user SSH e ao public ip address do SSH. O nome production corresponde ao comando de implantação do Shipit usado ao final deste tutorial (npx shipit server name deploy ou, no seu caso, npx shipit production deploy).

      No repositório Github para o Shipit, você pode encontrar mais informações sobre a o objeto de Configuração da implantação do Shipit.

      Antes de continuar a atualizar seu shipitfile.js, vamos analisar o seguinte exemplo de trecho de código para entender as tarefas do Shipit:

      Example event listener

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

      Esse é um exemplo de tarefa que utiliza o método shipit.on para se inscrever no evento deploy (implantar). Essa tarefa esperará que o evento deploy seja emitido pelo ciclo de vida do Shipit. Depois, quando o evento é recebido, a tarefa executa o método shipit.start, que diz ao Shipit para start (iniciar) a tarefa say-hello.

      O método shipit.on aceita dois parâmetros, o nome do evento para escutar e a função de callback a ser executada quando o evento for recebido.

      Sob a declaração do método shipit.on, a tarefa é definida com o método shipit.blTask. Isso cria uma tarefa do Shipit que irá bloquear outras tarefas durante sua execução (é uma tarefa síncrona). O método shipit.blTask também aceita dois parâmetros, o nome da tarefa que está definindo e uma função de callback para ser executada quando a tarefa for ativada pelo shipit.start.

      Dentro da função de callback dessa tarefa exemplo (say-hello), o método shipit.local executa um comando na máquina local. O comando local ecoa "hello from your local computer" no resultado do terminal.

      Se quisesse executar um comando no servidor remoto, usaria o método shipit.remote. Os dois métodos, shipit.local e shipit.remote, fornecem uma API para emitir comandos tanto localmente quanto remotamente como parte de uma implantação.

      Agora, atualize o shipitfile.js para incluir ouvintes de eventos para se inscrever no ciclo de vida do Shipit com shipit.on. Adicione os ouvintes de eventos ao seu shipitfile.js, inserindo-os após o espaço reservado para comentário da configuração inicial // Our tasks will go here:

      shipitfile.js

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

      Esses dois métodos estão escutando os eventos updated e published que são emitidos como parte do ciclo de vida da implantação do Shipit. Quando os eventos forem recebidos, cada qual irá iniciar tarefas usando o método shipit.start, assim como na tarefa exemplo.

      Agora que você agendou os ouvintes, adicionará a tarefa correspondente. Adicione a seguinte tarefa ao seu shipitfile.js, inserindo-a após seus ouvintes 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);
      });
      

      Primeiro, você declara uma tarefa chamada copy-config. Essa tarefa cria um arquivo local chamado ecosystem.config.js e, depois, copia esse arquivo para o seu servidor remoto app. O PM2 usa esse arquivo para gerenciar seu aplicativo Node.js. Ela fornece as informações necessárias do caminho do arquivo para o PM2 para garantir que ele esteja executando seus arquivos mais recentemente implantados. Mais adiante no processo de compilação, você criará uma tarefa que executa o PM2 com o ecosystem.config.js como configuração.

      Caso seu aplicativo precise de variáveis de ambiente (como uma string de conexão com banco de dados), você pode declará-las tanto localmente em env: quanto no servidor remoto em env_production: da mesma maneira que você definiu a variável NODE_ENV nesses objetos.

      Adicione a próxima tarefa ao seu shipitfile.js após a tarefa copy-config:

      shipitfile.js

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

      Em seguida, declare uma tarefa chamada npm-install. Essa tarefa usa um terminal bash remoto (via shipit.remote) para instalar as dependências do app (pacotes npm).

      Adicione a última tarefa ao seu shipitfile.js depois da tarefa 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 fim, declare uma tarefa chamada pm2-server. Essa tarefa também usa um terminal bash remoto para primeiro impedir o PM2 de gerenciar sua implantação anterior pelo comando delete e, em seguida, iniciar uma nova instância do seu servidor Node.js, fornecendo o arquivo ecosystem.config.js como uma variável. Além disso, você também avisa o PM2 que ele deve usar variáveis de ambiente a partir do bloco production na sua configuração inicial e pede ao PM2 para monitorar o aplicativo, reiniciando-o caso falhe.

      O arquivo completo do shipitfile.js:

      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: '[email protected]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`
          );
        });
      };
      

      Salve e saia do arquivo quando estiver pronto.

      Com seu shipitfile.js configurado, ouvintes de eventos e as tarefas finalizadas associadas, você pode prosseguir com a implantação para o servidor app.

      Passo 5 — Implantando seu aplicativo

      Neste passo, você implantará seu aplicativo remotamente e testará se a implantação disponibilizou seu aplicativo na internet.

      Como o Shipit clona os arquivos do projeto do repositório remoto do Git, você precisa mandar seus arquivos do aplicativo local Node.js da sua máquina local para o Github. Navegue até o diretório do seu aplicativo do projeto Node.js (onde seu hello.js e shipitfile.js estão localizados) e execute o seguinte comando:

      O comando git status mostra o estado do diretório de trabalho e a área de preparação. Ele permite que você veja quais alterações foram preparadas, quais não foram e quais arquivos o Git não está rastreando. Seus arquivos não estão rastreados e aparecem em vermelho no 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)

      Você pode adicionar esses arquivos ao seu repositório com o seguinte comando:

      Esse comando não gera nenhum resultado, embora caso fosse executar o git status novamente, os arquivos apareceriam em verde, com uma nota avisando que existem alterações a serem confirmadas.

      Você pode criar uma confirmação, executando o seguinte comando:

      • git commit -m "Our first commit"

      O resultado desse comando fornece algumas informações específicas do Git sobre os arquivos.

      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

      Agora, tudo o que resta é mandar sua confirmação para o repositório remoto, para que o Shipit clonar para o seu servidor app durante a implantação. Execute o seguinte comando:

      O resultado inclui informações sobre a sincronização com o repositório 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 implantar seu aplicativo, execute o seguinte comando:

      • npx shipit production deploy

      O resultado desse comando (que é grande demais para ser incluído por completo) fornece detalhes sobre as tarefas que estão sendo executadas e o resultado da função específica. O resultado que se segue à tarefa pm2-server mostra que o app Node.js foi 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 visualizar seu aplicativo como um usuário visualizaria, você pode digitar a URL your-domain do seu site em seu navegador para acessar o servidor web. Isso servirá o aplicativo Node.js, por proxy reverso, no servidor app, onde seus arquivos foram implantados.

      Você verá uma saudação Hello World.

      Nota: após a primeira implantação, seu repositório Git irá rastrear um arquivo recém-criado, chamado ecosystem.config.js. Como esse arquivo será recompilado a cada implantação, podendo conter segredos do aplicativo compilado, ele deve ser adicionado ao arquivo .gitignore, no diretório raiz do aplicativo em sua máquina local, antes da sua próxima confirmação do git.

      .gitignore

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

      Você implantou seu aplicativo Node.js em seu servidor app, que se refere à sua nova implantação. Com tudo em funcionamento, você pode prosseguir com o monitoramento dos processos do seu aplicativo.

      Passo 6 — Monitorando seu aplicativo

      O PM2 é uma ótima ferramenta para gerenciar seus processos remotos, mas também fornece recursos para monitorar o desempenho desses processos do aplicativo.

      Conecte-se ao seu servidor app remoto via SSH com este comando:

      Para obter informações específicas relacionadas aos seus processos gerenciados pelo PM2, execute o seguinte:

      Você verá um resultado parecido com este:

      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 │ └─────────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴──────┴───────────┴──────────┴──────────┘

      Você verá um resumo das informações que o PM2 coletou. Para ver informações detalhadas, execute:

      O resultado dá mais detalhes sobre as informações do resumo fornecidas pelo comando pm2 list. Ele também fornece informações sobre vários comandos auxiliares e fornece os locais dos arquivos 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 │ └───────────────────────────┴───────────────────────────────────────┘ . . .

      O PM2 também fornece uma ferramenta de monitoramento dentro do terminal, que pode ser acessada com:

      O resultado desse comando é um painel interativo, onde o pm2 fornece informações de processo, registros, métricas e metadados em tempo real. Este painel pode ajudar no monitoramento de recursos e registros de erro:

      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 │ └───────────────────────────────┘└───────────────────────────────────────────────────────────────────────────┘

      Com uma compreensão sobre como você pode monitorar seus processos com o PM2, você pode prosseguir e ver como o Shipit pode ajudar na reversão para uma implantação anterior em funcionamento.

      Termine sua sessão ssh no seu servidor app executando exit.

      Passo 7 — Revertendo uma implantação problemática

      Às vezes, as implantações revelam bugs imprevistos, ou problemas que fazem com que seu site falhe. Os desenvolvedores e mantenedores do Shipit previram esse problema e proporcionaram a capacidade de se fazer a reversão para implantações anteriores (em funcionamento) do seu aplicativo.

      Para garantir que sua configuração PM2 permaneça, adicione outro ouvinte de eventos ao shipitfile.js no evento rollback (reversão):

      shipitfile.js

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

      Adicione um ouvinte ao evento rollback para executar suas tarefas npm-install e copy-config. Isso é necessário, pois, ao contrário do evento published, o evento updated não é executado pelo ciclo de vida do Shipit quando se reverte uma implantação. Adicionar esse ouvinte de eventos garante que seu gerenciador de processos PM2 apontará para a implantação mais recente, mesmo no caso de uma reversão.

      Esse processo é semelhante ao da implantação, com uma pequena alteração no comando. Para tentar reverter para uma implantação anterior, execute o seguinte:

      • npx shipit production rollback

      Assim como o comando deploy, o rollback fornece detalhes sobre o processo de reversão e as tarefas que estão sendo executadas:

      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 ]

      Você configurou o Shipit para manter 5 versões de lançamento, através da configuração de keepReleases: 5 no shipitfile.js. O Shipit monitora essas versões internamente para garantir a capacidade de reverter quando necessário. O Shipit também fornece uma maneira conveniente de identificar as versões, criando um diretório nomeado na forma de um carimbo de data/hora (YYYYMMDDHHmmss – Exemplo: /home/deployer/your-domain/releases/20190420210548).

      Se quisesse personalizar ainda mais o processo de reversão, você poderia ouvir eventos específicos para a operação de reversão. Na sequência, você poderá usar esses eventos para executar tarefas que irão complementar sua reversão. Você pode consultar a lista de eventos fornecida no detalhamento do ciclo de vida do Shipit e configurar as tarefas/ouvintes dentro do seu shipitfile.js.

      Capacidade de reversão significa que sempre será possível oferecer aos seus usuários uma versão do seu aplicativo que funcione, mesmo se uma implantação introduzir erros/problemas inesperados.

      Conclusão

      Neste tutorial, você configurou um fluxo de trabalho que permite que crie uma alternativa altamente personalizável da Plataforma como um Serviço, tudo a partir de alguns servidores. Esse fluxo de trabalho leva em consideração implantações e configurações personalizadas, o monitoramento de processos com o PM2, a possibilidade de dimensionar e adicionar serviços, servidores adicionais ou ambientes à implantação, quando necessário.

      Se estiver interessado em continuar no desenvolvimento de suas habilidades com o Node.js, confira o material sobre Node.js da DigitalOcean, bem como a série sobre Como programar no Node.js.



      Source link

      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

      + [email protected]4.4.2 + [email protected]4.2.0 + [email protected]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:

      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: '[email protected]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:

      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

      Автоматизация развертывания Node.js в производственной среде с помощью Shipit в CentOS 7


      Автор выбрал фонд Electronic Frontier Foundation для получения пожертвований в рамках программы Write for DOnations.

      Введение

      Shipit — универсальный инструмент развертывания и автоматизации для разработчиков Node.js. В нем используется система потоков задач на базе популярного пакета Orchestrator, система входа и интерактивные команды SSH на базе OpenSSH, а также расширяемый API. Разработчики могут использовать Shipit для автоматизации рабочих процессов сборки и развертывания для разнообразных приложений Node.js.

      Рабочие процессы Shipit позволяют разработчикам не только настраивать задачи, но и указывать порядок их исполнения, необходимость синхронного или асинхронного выполнения, а также среду выполнения.

      В этом обучающем руководстве мы выполним установку и настройку Shipit для развертывания приложения Node.js из локальной среды разработки в производственной среде. Мы используем Shipit для развертывания приложения и настройки удаленного сервера посредством следующих шагов:

      • перенос файлов приложения Node.js из локальной среды в производственную среду (с использованием rsync, git и ssh).
      • установка зависимостей приложения (модули узла).
      • настройка и управление процессами Node.js на удаленном сервере с помощью PM2.

      Предварительные требования

      Перед началом выполнения этого обучающего руководства вам потребуется следующее:

      • Два сервера CentOS 7 (в этом обучающем руководстве мы будем использовать для них имена app и web) с настроенной частной сетью в соответствии с указаниями обучающего руководства Настройка приложения Node.js для производственной среды в CentOS 7.
      • Веб-сервер Nginx (на сервере web), защищенный протоколами TLS/SSL, как описано в обучающем руководстве Защита Nginx с помощью Let’s Encrypt в CentOS 7. Если вы выполните предварительные требования в хронологическом порядке, вам нужно будет только выполнить шаги 1, 4 и 6 на сервере web.
      • Установка Node.js и npm в производственной среде. В этом обучающем руководстве используется версия 10.17.0. Чтобы установить его в macOS или Ubuntu 18.04, следуйте указаниям руководства Установка Node.js и создание локальной среды разработки в macOS или раздела Установка с помощью PPA руководства Установка Node.js в Ubuntu 18.04. При установке Node.js также выполняется установка npm, в этом обучающем руководстве используется версия 6.11.3.
      • Локальный компьютер для разработки с установленными rsync и git.
        • В macOS вы можете использовать для их установки Homebrew.
        • Инструкции по установке git на дистрибутивах Linux содержатся в обучающем руководстве Установка Git.
      • Учетная запись на GitHub или другой системе хостинга служб git. В этом обучающем руководстве мы будем использовать GitHub.

      Примечание. Для выполнения описанных в этом руководстве команд пользователям Windows потребуется установить подсистему Windows Subsystem for Linux.

      Шаг 1 — Настройка удаленного репозитория

      Для синхронизации между локальным компьютером разработчика и удаленным сервером Shipit требуется репозиторий Git. На этом шаге вы создадите удаленный репозиторий на Github.com. Хотя все поставщики немного отличаются друг от друга, некоторые команды аналогичны.

      Для создания репозитория откройте сайт Github.com в браузере и введите учетные данные. В правом верхнем углу каждой странице располагается символ +. Нажмите +, а затем нажмите New repository.

      Github-new-repository

      Введите легко запоминающееся короткое имя репозитория, например hello-world. Любое указанное здесь имя будет воспроизведено в папке проекта, используемой при работе на локальном компьютере.

      Github-repository-name

      При желании вы можете добавить описание репозитория.

      Github-repository-description

      Укажите желаемую видимость репозитория (публичный или частный).

      Убедитесь, что для инициализации репозитория используется .gitignore, выберите пункт Node из выпадающего списка Add .gitignore. Этот шаг важен, чтобы предотвратить добавление в репозиторий ненужных файлов (например, папки node_modules).

      Github-gitignore-node

      Нажмите кнопку Create repository.

      Теперь необходимо клонировать репозиторий с Github.com на локальный компьютер.

      Откройте терминал и перейдите в место, где вы хотите хранить все файлы вашего проекта Node.js. Данный процесс создаст подпапку в текущей директории. Для клонирования репозитория на локальный компьютер используйте следующую команду:

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

      Вам потребуется заменить your-github-username и your-github-repository-name на свое имя пользователя Github и ранее заданное имя репозитория.

      Примечание. Если вы активировали двухфакторную аутентификацию (2FA) на Github.com, вы должны использовать персональный токен доступа или ключ SSH вместо пароля при доступе к Github из командной строки. Дополнительную информацию можно найти на странице справки Github по двухфакторной аутентификации.

      Результат будет выглядеть примерно так:

      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.

      Выполните следующую команду для перехода в репозиторий:

      • cd your-github-repository-name

      Внутри репозитория содержится отдельный файл и папка, используемые Git для управления репозиторием. Вы можете проверить это с помощью следующей команды:

      Результат будет выглядеть примерно так:

      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

      Мы настроили рабочий репозиторий git и теперь можем создать файл shipit.js для управления процессом развертывания.

      Шаг 2 — Интеграция Shipit в проект Node.js

      На этом шаге вы создадите образец проекта Node.js, а затем добавите пакеты Shipit. В этом обучающем руководстве приведен пример приложения — сервер Node.js web принимает запросы HTTP и отвечает на них текстовым сообщением Hello World. Для создания приложения запустите следующую команду:

      Добавьте следующий пример кода приложения в файл hello.js (измените значение переменной APP_PRIVATE_IP_ADDRESS на частный IP-адрес вашего сервера 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/');
      

      Создайте файл package.json для вашего приложения:

      Эта команда создает файл package.json, который вы будете использовать для настройки вашего приложения Node.js. На следующем шаге мы добавим в этот файл зависимости с помощью интерфейса командной строки 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" }

      Затем установите необходимые пакеты npm с помощью следующей команды:

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

      Здесь мы используем флаг --save-dev, поскольку пакеты Shipit требуются только на локальном компьютере. Результат будет выглядеть примерно так:

      Output

      + [email protected]4.4.2 + [email protected]4.2.0 + [email protected]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

      Эта команда также добавила в файл package.json три пакета как зависимости разработки:

      package.json

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

      Мы выполнили настройку локальной среды и теперь можем перейти к подготовке удаленного сервера app для развертывания на базе Shipit.

      Шаг 3 — Подготовка удаленного сервера App

      На этом шаге мы используем ssh для подключения к серверу app и установки удаленной зависимости rsync. Rsync — это утилита для эффективного перемещения и синхронизации файлов на дисках локального компьютера и сетевых компьютерах, которая выполняет сравнение времени изменения и размера файлов.

      Shipit использует rsync для передачи и синхронизации файлов между локальным компьютером и удаленным сервером app. Никакие команды rsync не нужно отправлять напрямую; Shipit обрабатывает их автоматически.

      Примечание. После выполнения обучающего руководства Настройка приложения Node.js для производственной среды в CentOS 7 у нас осталось два сервера, app и web. Эти команды следует выполнять только на сервере app.

      Подключитесь к удаленному серверу app через ssh:

      Установите на сервере rsync с помощью следующей команды:

      Подтвердите установку с помощью следующей команды:

      Вы увидите примерно такую строку с результатами выполнения команды:

      Output

      rsync version 3.1.2 protocol version 31 . . .

      Вы можете завершить сеанс ssh с помощью команды exit.

      Мы установили rsync и сделали эту утилиту доступной в командной строке. Теперь мы можем перейти к задачам развертывания и их связи с событиями.

      Шаг 4 — Настройка и выполнение задач по развертыванию

      События и задачи являются ключевыми компонентами развертывания с помощью Shipit, и поэтому важно понимать, как они дополняют процесс развертывания вашего приложения. Активируемые Shipit события отражают конкретные моменты жизненного цикла разработки. Задачи выполняются в ответ на эти события на основе последовательности жизненного цикла Shipit.

      Установка зависимостей приложения (node_modules) на удаленный сервер является обычным примером полезного использования этой системы задач и событий в приложении Node.js. Позднее на этом шаге мы сделаем так, что Shipit будет прослушивать событие updated (происходящее после передачи файлов приложения) и запускать задачу для установки зависимостей приложения (npm install) на удаленном сервере.

      Чтобы прослушивать события и выполнять задачи, Shipit требуется файл конфигурации, где хранится информация об удаленном сервере (сервер app) и регистрируются модули прослушивания событий и команды, выполняемые этими задачами. Этот файл размещается на локальном компьютере, используемом для разработки, в директории приложения Node.js.

      Для начала работы необходимо создать этот файл и добавить в него информацию об удаленном сервере, модулях прослушивания событий, на которые вы хотите подписаться, а также некоторые определения ваших задач. Создайте файл shipitfile.js в корневой директории приложения на локальном компьютере с помощью следующей команды:

      Мы создали файл, и теперь в него нужно внести исходную информацию о среде, которая требуется Shipit. Обычно эта информация включает расположение удаленного репозитория Git, публичный IP-адрес сервера app и учетную запись пользователя SSH.

      Добавьте эту начальную конфигурацию, изменив выделенные сроки в соответствии с параметрами вашей среды:

      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
      
      };
      

      При обновлении variables в методе shipit.initConfig Shipit получает сведения о конфигурации развертывания вашего приложения. Они представляют в Shipit следующее:

      • deployTo: директория, где Shipit размещает код вашего приложения на удаленном сервере. Здесь мы используем папку /home/ для пользователя sudo без привилегий root (/home/sammy), поскольку это защищенная папка, и с ней мы можем избежать проблем с разрешениями. Компонент имени /your-domain помогает отличить эту папку от других папок в домашней директории пользователя.
      • repositoryUrl: URL-адрес полного репозитория Git, Shipit использует этот URL-адрес для обеспечения синхронизации файлов проекта до начала развертывания.
      • keepReleases: количество выпусков приложения, хранящихся на удаленном сервере. Выпуск — это папка с указанием даты, содержащая файлы вашего приложения на момент выпуска. Они могут быть полезны, если потребуется провести откат развертывания.
      • shared: конфигурация, соответствующая keepReleases, позволяющая использовать общие директории для нескольких выпусков. В данном случае мы используем одну папку node_modules для всех выпусков.
      • production: представляет удаленный сервер, где вы хотите развернуть ваше приложение. В данном случае мы используем один сервер (сервер app) с именем production, и конфигурация servers: соответствует пользователю SSH и публичному IP-адресу. Имя production соответствует команде Shipit deploy, используемой ближе к концу этого обучающего руководства (npx shipit server name deploy или в данном случае npx shipit production deploy).

      Дополнительную информацию по объекту Shipit Deploy Configuration можно найти в репозитории Shipit на Github.

      Прежде чем продолжить обновление файла shipitfile.js, рассмотрим следующий пример кода, чтобы лучше понять задачи Shipit:

      Example event listener

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

      Это пример задачи, где метод shipit.on используется для подписки на событие deploy. Данная задача ожидает события deploy в жизненном цикле Shipit, а после получения этого события задача выполняет метод shipit.start, предписывающий Shipit запустить задачу say-hello.

      Метод shipit.on берет два параметра, название прослушиваемого события и функцию обратного вызова, выполняемую при получении события.

      При декларировании метода shipit.on задача определяется с помощью метода shipit.blTask. Так создается новая задача Shipit, которая будет блокировать другие задачи во время выполнения (это синхронная задача). Метод shipit.blTask также принимает два параметра, а именно имя определяемой задачи и функцию обратного вызова, которая выполняется при активации задачи shipit.start.

      В функции обратного вызова в этом примере (say-hello) метод shipit.local выполняет команду на локальном компьютере. Локальная команда выводит эхо-сообщение "hello from your local computer" на экран терминала.

      Если вам нужно запустить команду на удаленном сервере, используйте метод shipit.remote. Два метода, shipit.local и shipit.remote, предоставляют API для локальной или удаленной отправки команд в процессе развертывания.

      Обновите файл shipitfile.js для добавления в него модулей прослушивания событий для подписки на жизненный цикл Shipit с помощью shipit.on. Добавьте модули прослушивания событий в файл shipitfile.js, вставьте их после замещающего текста комментария в первоначальной конфигурации // Our tasks will go here:

      shipitfile.js

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

      Эти два метода прослушивают события updated и published в составе жизненного цикла развертывания Shipit. При получении события каждый метод инициирует задачи с помощью метода shipit.start аналогично приведенному примеру.

      Мы задали планирование модулей прослушивания и теперь можем добавить соответствующую задачу. Добавьте следующую задачу в файл shipitfile.js, вставив ее после модулей прослушивания событий:

      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);
      });
      

      Вначале декларируется задача с именем copy-config. Эта задача создает локальный файл с именем ecosystem.config.js, а затем копирует его на удаленный сервер app. PM2 использует этот файл для управления вашим приложением Node.js. Он передает PM2 необходимую информацию о пути к файлу, чтобы обеспечить использование последней версии развернутых файлов. Позднее в процессе сборки мы создадим задачу запуска PM2 с помощью ecosystem.config.js как конфигурации.

      Если для вашего приложения требуются переменные среды (например, строка подключения к базе данных), вы можете декларировать их локально через env: или на удаленном сервере через env_production: точно так же, как вы настраиваете переменную NODE_ENV в этих объектах.

      Добавьте следующую задачу в файл shipitfile.js после задачи copy-config:

      shipitfile.js

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

      Затем декларируйте задачу с именем npm-install. Данная задача использует удаленный терминал bash (через shipit.remote) для установки зависимостей приложения (пакетов npm).

      Добавьте последнюю задачу в файл shipitfile.js после задачи 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`
        );
      });
      

      В заключение декларируйте задачу pm2-server. Эта задача также использует удаленный терминал для остановки управления предыдущим развертыванием со стороны PM2 с помощью команды delete и для запуска нового экземпляра сервера Node.js с указанием файла ecosystem.config.js в качестве переменной. Также вы должны сообщить PM2 о необходимости использования в начальной конфигурации переменных среды из блока production и предписать PM2 следить за приложением и перезапускать его в случае сбоя.

      Полный файл shipitfile.js:

      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: '[email protected]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`
          );
        });
      };
      

      Сохраните и закройте файл, когда будете готовы.

      Мы внесли настройки в файл shipitfile.js, подготовили модули прослушивания событий и завершили соответствующие задачи, и теперь можем перейти к развертыванию на сервере app.

      Шаг 5 — Развертывание приложения

      На этом шаге мы выполним удаленное развертывание приложения и проверим доступность развернутого приложения через Интернет.

      Поскольку Shipit клонирует файлы проекта из удаленного репозитория Git, локальные файлы приложения Node.js следует отправить с локального компьютера на Github. Перейдите в директорию приложения проекта Node.js (где находятся файлы hello.js и shiptitfile.js) и запустите следующую команду:

      Команда git status показывает состояние рабочей директории и области размещения. С его помощью вы увидите, какие изменения были размещены, какие нет и какие файлы не отслеживаются Git. Ваши файлы не отслеживаются и выделяются красным цветом в выводимых результатах:

      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)

      Вы можете добавить эти файлы в репозиторий с помощью следующей команды:

      Эта команда не выводит никаких результатов, хотя если вы снова запустите команду git status, файлы будут помечены зеленым цветом с отметкой, что имеются изменения для записи.

      Для записи изменений используется следующая команда:

      • git commit -m "Our first commit"

      В результатах этой информации выводится определенная информация о файлах, относящаяся к Git.

      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

      Теперь остается только завершить запись в удаленный репозиторий, чтобы Shipit мог выполнить клонирование сервера app во время развертывания. Запустите следующую команду:

      В результатах выводится информация о синхронизации с удаленным репозиторием:

      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

      Чтобы развернуть приложение, запустите следующую команду:

      • npx shipit production deploy

      Эта команда выводит данные о выполняемых задачах (в слишком большом объеме, чтобы показать их полностью) и результат выполнения определенной функции. В результатах, выводимых после выполнения задачи pm2-server, показывается, что приложение Node.js запущено:

      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 ]

      Чтобы увидеть приложение как пользователь, вы можете ввести URL-адрес вашего сайта your-domain в браузере, чтобы получить доступ к серверу web. Приложение Node.js будет обслуживаться через обратный прокси-сервер на сервере app, где развернуты ваши файлы.

      Вы увидите приветствие Hello World.

      Примечание. После первого развертывания репозиторий Git будет отслеживать новый файл с именем ecosystem.config.js. Поскольку этот файл будет воссоздаваться заново при каждом развертывании и может содержать секретные данные скомпилированного приложения, его следует добавить в файл .gitignore в корневой директории приложения на локальном компьютере до следующей записи в git.

      .gitignore

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

      Мы развернули приложение Node.js на сервере app, проведя новую операцию развертывания. Теперь все работает, и мы можем переходить к мониторингу процессов вашего приложения.

      Шаг 6 — Мониторинг приложения

      PM2 — отличный инструмент для управления удаленными процессами, который также поддерживает мониторинг производительности этих процессов.

      Подключитесь к удаленному серверу app через SSH с помощью следующей команды:

      Для получения конкретной информации по управляемым процессам PM2 используйте следующую команду:

      Результат будет выглядеть примерно так:

      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 │ └─────────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴──────┴───────────┴──────────┴──────────┘

      Вы увидите сводную информацию, собранную PM2. Чтобы посмотреть подробную информацию, запустите следующую команду:

      В результатах раскрывается сводная информация, предоставляемая командой pm2 list. Также предоставляется информация по ряду вспомогательных команд и сведения о расположении файла журнала:

      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 │ └───────────────────────────┴───────────────────────────────────────┘ . . .

      PM2 также предоставляет инструмент для мониторинга через терминал:

      В результате выполнения этой команды выводится интерактивная панель, где pm2 в реальном времени предоставляет информацию о процессах, журналы, метрические показатели и метаданные. Информационная панель может помочь для мониторинга ресурсов и журналов ошибок:

      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 │ └───────────────────────────────┘└───────────────────────────────────────────────────────────────────────────┘

      Теперь мы знаем, как отслеживать выполнение процессов с помощью PM2, и можем перейти к изучению использования Shipit для отката к предыдущей работающей версии развертывания.

      Закройте сеанс ssh на сервере app с помощью команды exit.

      Шаг 7 — Откат при ошибках развертывания

      Иногда при развертывании могут возникнуть непредвиденные ошибки или проблемы, из-за которых сайт выходит из строя. Команда по разработке и обслуживанию Shipit предвидела эту ситуацию и поэтому предлагает возможность отката к предыдущей (работающей) версии вашего приложения.

      Чтобы обеспечить сохранение конфигурации PM2, добавьте еще один модуль прослушивания событий в файл shipitfile.js для события rollback:

      shipitfile.js

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

      Модуль прослушивания событий для события rollback добавляется для запуска заданий npm-install и copy-config. Это необходимо, т. к. в отличие от опубликованного события, обновленное событие не запускается в рабочем цикле Shipit при откате развертывания. При добавлении этого модуля прослушивания событий ваш диспетчер процессов PM2 указывает на последнюю развернутую версию, даже если был произведен откат.

      Этот процесс аналогичен развертыванию, есть лишь небольшое отличие в синтаксисе команд. Попробуйте выполнить откат к предыдущей развернутой версии с помощью следующей команды:

      • npx shipit production rollback

      Как и команда deploy, команда rollback предоставляет детальную информацию по процессу отката и выполняемым задачам:

      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 ]

      Вы настроили Shipit для сохранения 5 выпусков с помощью параметра конфигурации keepReleases: 5 в shipitfile.js. Shipit обеспечивает внутреннее отслеживание этих выпусков,чтобы обеспечить возможность отката в случае необходимости. Также Shipit обеспечивает удобную возможность идентификации выпусков посредством создания директории с временной меткой в составе имени (YYYYMMDDHHmmss — пример: /home/deployer/your-domain/releases/20190420210548).

      Если вам требуется дополнительная настройка процедуры отката, вы можете использовать прослушивание событий, связанных с операцией отката. Эти события вы можете использовать для выполнения задач, дополняющих процедуру отката. Вы можете использовать жизненный цикл событий, включенный в детальное описание рабочего цикла Shipit, а также настроить задачи и модули прослушивания в файле shipitfile.js.

      Возможность отката означает, что вы всегда можете предоставить пользователям работающую версию приложения, даже если при развертывании возникнут непредвиденные проблемы или ошибки.

      Заключение

      В этом обучающем руководстве мы научились настраивать рабочий процесс, позволяющий создать настраиваемую альтернативу модели «платформа как услуга» с использованием всего двух серверов. Этот рабочий процесс поддерживает персонализацию развертывания и настроек, мониторинг процессов с помощью PM2, возможность масштабирования и добавления служб, дополнительных серверов или сред, если возникнет такая необходимость.

      Если вы хотите продолжить развивать свои навыки Node.js, ознакомьтесь с материалами DigitalOcean по Node.js и с серией материалов по программированию на Node.js.



      Source link