avoid rebuilding node_modules in elastic beanstalk

前端 未结 5 1292
礼貌的吻别
礼貌的吻别 2020-12-12 10:00

We have a fairly simple node.js app, but due to AWS Elastic Beanstalk deployment mechanism, it takes about 5 minutes to roll-out a new version (via git aws.push

相关标签:
5条回答
  • 2020-12-12 10:37

    There's npm package that's overwriting default EB behaviour for npm install command by truncating following files:

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

    https://www.npmjs.com/package/eb-disable-npm

    Might be better than just copying script from SO, since this package is maintained and probably will be updated when EB behaviour will change.

    0 讨论(0)
  • 2020-12-12 10:48

    Thanks Kirill, it was really helpful !

    I'm just sharing my config file for people who just look the simple solution to the npm install. This file needs to be placed in the .ebextensions folder of the project, it is lighter since it doesn't include last version of node installation, and ready to use.

    It also dynamically checks the node version installed, so no need for it to be included in the env.vars file.

    .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
          }
    
          #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 && $NODE_PATH/npm install 2>&1) || error_exit "Failed to run npm install.  $OUT" $?
          echo $OUT
      "/opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh" :
        mode: "000666"
        owner: root
        group: users
        content: |
           #no need to run npm install during configdeploy
    
    0 讨论(0)
  • 2020-12-12 10:49

    I had 10+ minute builds when I would deploy. The solution was much simpler than others have came up with... Just check node_modules into git! See http://www.futurealoof.com/posts/nodemodules-in-git.html for the reasoning

    0 讨论(0)
  • 2020-12-12 10:54

    25/01/13 NOTE: updated scripts to run npm -g version upgrade (only once, on initial instance roll out or rebuild) and to avoid NPM operations during EB configuration change (when app dir is not present, to avoid error and to speed up configuration updates).

    Okay, Elastic Beanstalk behaves dodgy with recent node.js builds (including presumably supported v.0.10.10), so I decided to go ahead and tweak EB to do the following:

    1. to install ANY node.js version as per your env.config (including the most recent ones that are not yet supported by AWS EB)
    2. to avoid rebuilding existing node modules, including in-app node_modules dir
    3. to install node.js globally (and any desired module as well).

    Basically, I use env.config to replace deploy&config hooks with customized ones (see below). Also, in a default EB container setup some env variables are missing ($HOME for example) and node-gyp sometimes fails during rebuild because of it (took me 2 hours of googling and reinstalling libxmljs to resolve this).

    Below are the files to be included along with your build. You can inject them via env.config as inline code or via source: URL (as in this example)

    env.vars (desired node version & 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 (fetch and ungzip desired node.js version, make global symlinks, update global npm version)

    #!/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, symlinks it to app dir and runs npm install. You can install any module globally from here, they will 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 (note node version here too, and to be safe, put desired node version in env config in AWS console as well. I'm not certain 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: |
           #no need to run npm install during configdeploy
      "/opt/elasticbeanstalk/hooks/appdeploy/pre/40install_node.sh" :
        mode: "000775"
        owner: root
        group: users
        source: https://raw.github.com/....
    

    There you have it: on t1.micro instance deployment now takes 20-30 secs instead of 10-15 minutes! If you deploy 10 times a day, this tweak will save you 3 (three) weeks in a year. Hope it helps and special thanks to AWS EB staff for my lost weekend :)

    0 讨论(0)
  • 2020-12-12 10:57

    I've found a quick solution to this. I looked through the build scripts that Amazon are using and they only run npm install if package.json is present. So after your initial deploy you can change it to _package.json and npm install won't run anymore! It's not the best solution but it's a quick fix if you need one!

    0 讨论(0)
提交回复
热议问题