Избежать пересборки node_modules в Elastic Beanstalk
У нас есть довольно простое приложение на 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, что совсем не весело.
Я вижу два подхода в этой ситуации:
- Изменить файл
/opt/containerfiles/ebnode.py
и поработать с расположениемnode_modules
, чтобы избежать его удаления и пересборки при развертывании. - Настроить репозиторий Git на экземпляре EC2 Elastic Beanstalk и фактически переписать процедуру развертывания, так чтобы
/var/app/current
получал обновления и запускалnpm install
только при необходимости (что сделает Elastic Beanstalk похожим на OpsWorks).
Оба варианта не идеальны и подвержены рискам при обновлениях от Amazon в Elastic Beanstalk, так как могут возникать проблемы с их хуками и архитектурой.
Может, у кого-то есть лучшее решение, как избежать постоянной пересборки node_modules
, которые уже присутствуют в директории приложения? Спасибо.
4 ответ(ов)
Спасибо, Кирилл, это действительно полезно!
Я просто делюсь своим конфигурационным файлом для тех, кто ищет простое решение для 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
Надеюсь, это поможет!
Привет!
Если у вас возникают проблемы с Elastic Beanstalk и новыми сборками Node.js, включая, возможно, поддерживаемую v.0.10.10, вам может помочь следующая конфигурация. Я внес несколько поправок в Elastic Beanstalk, которые позволяют:
- Устанавливать любую версию Node.js в соответствии с вашим
env.config
(включая самые новые, еще не поддерживаемые AWS EB). - Избежать перестройки существующих Node.js модулей, включая директорию
node_modules
приложения. - Установить 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 за мой потерянный уикенд! 😃
Существует пакет npm, который переопределяет стандартное поведение Elastic Beanstalk для команды npm install
, обрезая следующие файлы:
- /opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh
- /opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh
Вы можете ознакомиться с ним здесь.
Использование этого пакета может быть более целесообразным, чем просто копирование скрипта из Stack Overflow, так как он поддерживается и, вероятно, будет обновляться в случае изменений в поведении Elastic Beanstalk.
Я нашел быстрое решение этой проблемы. Изучив сборочные скрипты, которые использует Amazon, я заметил, что npm install
запускается только при наличии файла package.json
. Поэтому после вашего первоначального развертывания вы можете переименовать его в _package.json
, и тогда npm install
больше не будет выполняться! Это не лучшее решение, но оно является хорошим временным выходом, если вам он срочно нужен!
Как узнать версию установленного npm пакета?
В чем разница между тильдой (~) и каретом (^) в package.json?
Как задать переменные окружения из файла package.json?
Что делает npm install --legacy-peer-deps? Когда рекомендуется использовать и какой потенциальный случай?
Не удалось проверить обновление npm