0

Избежать пересборки node_modules в Elastic Beanstalk

10

У нас есть довольно простое приложение на Node.js, однако из-за механизма развертывания AWS Elastic Beanstalk обновление новой версии занимает около 5 минут (через команду git aws.push), даже если мы изменяем всего один файл.

То есть само коммитирование (и загрузка) происходит быстро (только 1 файл для отправки), но затем Elastic Beanstalk загружает весь пакет из S3, распаковывает его и выполняет команду npm install, что вызывает компиляцию некоторых модулей с использованием node-gyp. После завершения установки/сборки Elastic Beanstalk очищает папку /var/app/current и заменяет её новой версией приложения.

Необходимо отметить, что постоянная пересборка node_modules не требуется, и процесс сборки, который занимает 30 секунд на моем старом Macbook Air, длится более 5 минут на экземпляре ec2.micro, что совсем не весело.

Я вижу два подхода в этой ситуации:

  1. Изменить файл /opt/containerfiles/ebnode.py и поработать с расположением node_modules, чтобы избежать его удаления и пересборки при развертывании.
  2. Настроить репозиторий Git на экземпляре EC2 Elastic Beanstalk и фактически переписать процедуру развертывания, так чтобы /var/app/current получал обновления и запускал npm install только при необходимости (что сделает Elastic Beanstalk похожим на OpsWorks).

Оба варианта не идеальны и подвержены рискам при обновлениях от Amazon в Elastic Beanstalk, так как могут возникать проблемы с их хуками и архитектурой.

Может, у кого-то есть лучшее решение, как избежать постоянной пересборки node_modules, которые уже присутствуют в директории приложения? Спасибо.

4 ответ(ов)

0

Спасибо, Кирилл, это действительно полезно!

Я просто делюсь своим конфигурационным файлом для тех, кто ищет простое решение для npm install. Этот файл необходимо разместить в папке .ebextensions вашего проекта. Он легче, так как не включает последнюю версию установки Node.js и готов к использованию.

Кроме того, файл динамически проверяет установленную версию Node.js, что устраняет необходимость добавлять это в файл env.vars.

.ebextensions/00_deploy_npm.config

files:
  "/opt/elasticbeanstalk/env.vars" :
    mode: "000775"
    owner: root
    group: users
    content: |
      export NPM_CONFIG_LOGLEVEL=error
      export NODE_PATH=`ls -td /opt/elasticbeanstalk/node-install/node-* | head -1`/bin
  "/opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh" :
    mode: "000775"
    owner: root
    group: users
    content: |
      #!/bin/bash
      . /opt/elasticbeanstalk/env.vars
      function error_exit
      {
        eventHelper.py --msg "$1" --severity ERROR
        exit $2
      }

      # установка node_modules, если они еще не установлены
      if [ ! -d "/var/node_modules" ]; then
        mkdir /var/node_modules ;
      fi
      if [ -d /tmp/deployment/application ]; then
        ln -s /var/node_modules /tmp/deployment/application/
      fi

      OUT=$([ -d "/tmp/deployment/application" ] && cd /tmp/deployment/application && $NODE_PATH/npm install 2>&1) || error_exit "Не удалось выполнить npm install.  $OUT" $?
      echo $OUT
  "/opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh" :
    mode: "000666"
    owner: root
    group: users
    content: |
       # нет необходимости выполнять npm install во время configdeploy

Надеюсь, это поможет!

0

Привет!

Если у вас возникают проблемы с Elastic Beanstalk и новыми сборками Node.js, включая, возможно, поддерживаемую v.0.10.10, вам может помочь следующая конфигурация. Я внес несколько поправок в Elastic Beanstalk, которые позволяют:

  1. Устанавливать любую версию Node.js в соответствии с вашим env.config (включая самые новые, еще не поддерживаемые AWS EB).
  2. Избежать перестройки существующих Node.js модулей, включая директорию node_modules приложения.
  3. Установить Node.js глобально (а также любые желаемые модули).

В основном, я использую файл env.config, чтобы заменить стандартные хуки развертывания и конфигурации на свои собственные (см. ниже). Также в стандартной настройке контейнера EB отсутствуют некоторые переменные окружения (например, $HOME), что иногда вызывает сбои в node-gyp при перестройке (на это у меня ушло 2 часа поиска в интернете и переустановки libxmljs).

Ниже представлены файлы, которые необходимо включить в вашу сборку. Вы можете внедрить их через env.config как встроенный код или через source: URL (как в этом примере):

env.vars (желаемая версия Node и архитектура указаны здесь и в env.config, смотрите ниже):

export HOME=/root
export NPM_CONFIG_LOGLEVEL=error
export NODE_VER=0.10.24
export ARCH=x86
export PATH="$PATH:/opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/:/root/.npm"

40install_node.sh (скачивает и распаковывает нужную версию Node.js, создает глобальные символические ссылки, обновляет версию npm):

#!/bin/bash
# Загружаем переменные окружения, включая версию Node
. /opt/elasticbeanstalk/env.vars

function error_exit {
  eventHelper.py --msg "$1" --severity ERROR
  exit $2
}

# Удалите комментарий, чтобы обновить npm, иначе он будет обновлен при инициализации или перестройке экземпляра
# rm -f /opt/elasticbeanstalk/node-install/npm_updated

# Скачиваем и распаковываем нужную версию Node.js
OUT=$([ ! -d "/opt/elasticbeanstalk/node-install" ] && mkdir /opt/elasticbeanstalk/node-install ; cd /opt/elasticbeanstalk/node-install/ && wget -nc http://nodejs.org/dist/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && tar --skip-old-files -xzpf node-v$NODE_VER-linux-$ARCH.tar.gz) || error_exit "Не удалось ОБНОВИТЬ версию Node. $OUT" $?.
echo $OUT

# Убедитесь, что бинарные файлы Node доступны глобально
if [ ! -L /usr/bin/node ]; then
  ln -s /opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/node /usr/bin/node
fi

if [ ! -L /usr/bin/npm ]; then
  ln -s /opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/npm /usr/bin/npm
fi

if [ ! -f "/opt/elasticbeanstalk/node-install/npm_updated" ]; then
  /opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/npm update -g
  touch /opt/elasticbeanstalk/node-install/npm_updated
  echo "Ура! Обновлена глобальная версия NPM до `npm -v`"
else
  echo "Пропускаем обновление версии NPM -g. Чтобы обновить, удалите комментарий в строке 12 файла 40install_node.sh"
fi

50npm.sh (создает /var/node_modules, создает символическую ссылку на директорию приложения и выполняет npm install):

#!/bin/bash
. /opt/elasticbeanstalk/env.vars
function error_exit {
  eventHelper.py --msg "$1" --severity ERROR
  exit $2
}

# Установка неустановленных yet модулей node приложения
if [ ! -d "/var/node_modules" ]; then
  mkdir /var/node_modules ;
fi
if [ -d /tmp/deployment/application ]; then
  ln -s /var/node_modules /tmp/deployment/application/
fi

OUT=$([ -d "/tmp/deployment/application" ] && cd /tmp/deployment/application && /opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/npm install 2>&1) || error_exit "Не удалось выполнить npm install. $OUT" $?
echo $OUT

env.config (обратите внимание на версию Node здесь тоже; для уверенности, укажите желаемую версию Node и в конфигурации среды в консоли AWS. Я не уверен, какие из этих настроек будут иметь приоритет):

packages:
  yum:
    git: []
    gcc: []
    make: []
    openssl-devel: []

option_settings:
  - option_name: NODE_ENV
    value: production
  - option_name: RDS_HOSTNAME
    value: fill_me_in
  - option_name: RDS_PASSWORD
    value: fill_me_in
  - option_name: RDS_USERNAME
    value: fill_me_in
  - namespace: aws:elasticbeanstalk:container:nodejs
    option_name: NodeVersion
    value: 0.10.24

files:
  "/opt/elasticbeanstalk/env.vars" :
    mode: "000775"
    owner: root
    group: users
    source: https://dl.dropbox.com/....
  "/opt/elasticbeanstalk/hooks/configdeploy/pre/40install_node.sh" :
    mode: "000775"
    owner: root
    group: users
    source: https://raw.github.com/....
  "/opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh" :
    mode: "000775"
    owner: root
    group: users
    source: https://raw.github.com/....
  "/opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh" :
    mode: "000666"
    owner: root
    group: users
    content: |
       # нет необходимости запускать npm install во время configdeploy
  "/opt/elasticbeanstalk/hooks/appdeploy/pre/40install_node.sh" :
    mode: "000775"
    owner: root
    group: users
    source: https://raw.github.com/....

Вот и все: развертывание на экземпляре t1.micro теперь занимает 20-30 секунд вместо 10-15 минут! Если вы выполняете развертывание 10 раз в день, эта оптимизация сэкономит вам 3 недели в году.
Надеюсь, это будет вам полезно, и особенно спасибо команде AWS EB за мой потерянный уикенд! 😃

0

Существует пакет npm, который переопределяет стандартное поведение Elastic Beanstalk для команды npm install, обрезая следующие файлы:

  • /opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh
  • /opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh

Вы можете ознакомиться с ним здесь.

Использование этого пакета может быть более целесообразным, чем просто копирование скрипта из Stack Overflow, так как он поддерживается и, вероятно, будет обновляться в случае изменений в поведении Elastic Beanstalk.

0

Я нашел быстрое решение этой проблемы. Изучив сборочные скрипты, которые использует Amazon, я заметил, что npm install запускается только при наличии файла package.json. Поэтому после вашего первоначального развертывания вы можете переименовать его в _package.json, и тогда npm install больше не будет выполняться! Это не лучшее решение, но оно является хорошим временным выходом, если вам он срочно нужен!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь