25/01/13 NOTE: updated scripts to start updating the npm -g version (only once, when deploying or restoring the original instance) and to avoid NPM actions during EB configuration changes (when the application is absent, to avoid the error and speed up the configuration update )
Well, Elastic Beanstalk is dodgy with recent node.js lines (including the supposedly supported v.0.10.10), so I decided to go and fine-tune EB to do the following:
- to install ANY version of node.js according to your env.config (including the most recent ones that are not yet supported by AWS EB)
- to avoid restoring existing node modules, including in-app node_modules dir
- install node.js globally (and any module you want).
Basically, I use env.config to replace & config config with interceptors with custom ones (see below). In addition, in the default configuration of the EB container, some env variables are missing (for example, $HOME ), and node-gyp sometimes fails during recovery due to this (I needed 2 hours to find and reinstall libxmljs to solve this problem) .
Below are the files that will be included with your build. You can enter them through env.config as inline code or through source: URL (as in this example)
env.vars (the desired version of the node and arch are included here and in env.config, see below)
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 (fetching and deploying the desired version of node.js, creating global symbolic links, updating the global version of npm)
#!/bin/bash #source env variables including node version . /opt/elasticbeanstalk/env.vars function error_exit { eventHelper.py --msg "$1" --severity ERROR exit $2 } #UNCOMMENT to update npm, otherwise will be updated on instance init or rebuild #rm -f /opt/elasticbeanstalk/node-install/npm_updated #download and extract desired node.js version 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 "Failed to UPDATE node version. $OUT" $?. echo $OUT #make sure node binaries can be found globally 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/ && /opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/npm update npm -g touch /opt/elasticbeanstalk/node-install/npm_updated echo "YAY! Updated global NPM version to `npm -v`" else echo "Skipping NPM -g version update. To update, please uncomment 40install_node.sh:12" fi
50npm.sh (creates / var / node_modules, symbolizes it in the dir application and runs npm install. You can install any module around the world here, they land in /root/.npm)
#!/bin/bash . /opt/elasticbeanstalk/env.vars function error_exit { eventHelper.py --msg "$1" --severity ERROR exit $2 } #install not-installed yet app 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 && /opt/elasticbeanstalk/node-install/node-v$NODE_VER-linux-$ARCH/bin/npm install 2>&1) || error_exit "Failed to run npm install. $OUT" $? echo $OUT
env.config (also note the version of node) and, to be safe, put the desired version of node in the env configuration in the AWS console. I'm not sure which of these settings will take precedence.)
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: |
There you have it: on t1.micro, deploying an instance now takes 20-30 seconds instead of 10-15 minutes! If you deploy 10 times a day, this setting will save you 3 (three) weeks per year. Hope this helps and special thanks to AWS EB staff for my lost weekend :)