Tutorial

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

CentOSNode.jsGitJavaScriptAutomated Setups

Автор выбрал фонд 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 для управления репозиторием. Вы можете проверить это с помощью следующей команды:

  • ls -la

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

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. Для создания приложения запустите следующую команду:

  • nano hello.js

Добавьте следующий пример кода приложения в файл 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 World\n');
}).listen(8080, 'APP_PRIVATE_IP_ADDRESS');
console.log('Server running at http://APP_PRIVATE_IP_ADDRESS:8080/');

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

  • npm init -y

Эта команда создает файл 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
+ shipit-shared@4.4.2 + shipit-cli@4.2.0 + shipit-deploy@4.1.4 updated 4 packages and audited 21356 packages in 11.671s found 62 low severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details

Эта команда также добавила в файл 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:

  • ssh deployer@your_app_server_ip

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

  • sudo yum install rsync

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

  • rsync --version

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

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 в корневой директории приложения на локальном компьютере с помощью следующей команды:

  • nano 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: 'deployer@YOUR_APP_SERVER_PUBLIC_IP'
    }
  });

  const path = require('path');
  const ecosystemFilePath = path.join(
    shipit.config.deployTo,
    'shared',
    'ecosystem.config.js'
  );

  // Our listeners and tasks will go here
  shipit.on('updated', async () => {
    shipit.start('npm-install', 'copy-config');
  });

  shipit.on('published', async () => {
    shipit.start('pm2-server');
  });

  shipit.blTask('copy-config', async () => {
    const fs = require('fs');
    const ecosystem = `
module.exports = {
  apps: [
    {
      name: '${appName}',
      script: '${shipit.releasePath}/hello.js',
      watch: true,
      autorestart: true,
      restart_delay: 1000,
      env: {
        NODE_ENV: 'development'
      },
      env_production: {
        NODE_ENV: 'production'
      }
    }
  ]
};`;

    fs.writeFileSync('ecosystem.config.js', ecosystem, function(err) {
      if (err) throw err;
      console.log('File created successfully.');
    });

    await shipit.copyToRemote('ecosystem.config.js', ecosystemFilePath);
  });

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

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

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

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

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

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

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

  • git status

Команда 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 add --all

Эта команда не выводит никаких результатов, хотя если вы снова запустите команду 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 во время развертывания. Запустите следующую команду:

  • git push origin master

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

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 с помощью следующей команды:

  • ssh deployer@your_app_server_ip

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

  • pm2 list

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

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 show hello

В результатах раскрывается сводная информация, предоставляемая командой 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 monit

В результате выполнения этой команды выводится интерактивная панель, где 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.

Creative Commons License