web-dev-qa-db-fra.com

éviter de reconstruire node_modules dans le beanstalk élastique

Nous avons une application node.js assez simple, mais en raison du mécanisme de déploiement AWS Elastic Beanstalk, il faut environ 5 minutes pour déployer une nouvelle version (via git aws.Push) même après une validation de fichier unique.

C'est à dire. la validation elle-même (et le téléchargement) est rapide (seulement 1 fichier à pousser), mais ensuite Elastic Beanstalk récupère le paquet entier de S3, le décompresse et exécute npm install, ce qui oblige node-gyp à compiler certains modules. Une fois l'installation/la construction terminée, Elastic Beanstalk essuie /var/app/current et le remplace par la nouvelle version de l'application.

Inutile de dire que la reconstruction constante de node_modules n'est pas nécessaire, et une reconstruction qui prend 30 secondes sur mon ancien Macbook Air, prend> 5 minutes sur une instance ec2.micro, pas amusant.

Je vois deux approches ici:

  1. Tordre /opt/containerfiles/ebnode.py et jouez avec l'emplacement de node_modules pour éviter sa suppression et sa reconstruction lors du déploiement.
  2. mettre en place un dépôt git sur l'instance Elastic Beanstalk EC2 et essentiellement réécrire la procédure de déploiement nous-mêmes, afin que/var/app/current reçoive des push et exécute npm install uniquement lorsque cela est nécessaire (ce qui fait qu'Elastic Beanstalk ressemble à OpsWorks ..)

Les deux options manquent de grâce et sont sujettes à des problèmes lorsque Amazon met à jour leurs crochets et leur architecture Elastic Beanstalk.

Peut-être que quelqu'un a une meilleure idée de la façon d'éviter la reconstruction constante de node_modules qui sont déjà présents dans le répertoire de l'application? Merci.

51
Kirill Kay

Merci Kirill, c'était vraiment utile!

Je partage juste mon fichier de configuration pour les personnes qui recherchent simplement la solution simple au npm install. Ce fichier doit être placé dans le .ebextensions dossier du projet, il est plus léger car il ne comprend pas la dernière version de l'installation du noeud, et prêt à l'emploi.

Il vérifie également dynamiquement la version du nœud installée, donc pas besoin de l'inclure dans le fichier 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
      }

      #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
41
Tronix117

25/01/13 REMARQUE: scripts mis à jour pour exécuter la mise à niveau de la version npm -g (une seule fois, lors du déploiement ou de la reconstruction de l'instance initiale) et pour éviter les opérations NPM lors du changement de configuration EB (lorsque le répertoire d'application n'est pas présent, pour éviter les erreurs et accélérer les mises à jour de la configuration).

D'accord, Elastic Beanstalk se comporte de manière douteuse avec les versions récentes de node.js (y compris vraisemblablement prise en charge v.0.10.10), j'ai donc décidé d'aller de l'avant et Tweak EB pour faire ce qui suit:

  1. pour installer TOUTE version node.js selon votre env.config (y compris les plus récentes qui ne sont pas encore prises en charge par AWS EB)
  2. pour éviter de reconstruire les modules de nœuds existants, y compris le répertoire node_modules dans l'application
  3. pour installer node.js globalement (et tout module souhaité également).

Fondamentalement, j'utilise env.config pour remplacer les crochets de déploiement et de configuration par des crochets personnalisés (voir ci-dessous). De plus, dans une configuration de conteneur EB par défaut, certaines variables env sont manquantes ($HOME par exemple) et node-gyp échoue parfois pendant la reconstruction à cause de cela (il m'a fallu 2 heures pour googler et réinstaller libxmljs pour résoudre ce problème).

Vous trouverez ci-dessous les fichiers à inclure avec votre build. Vous pouvez les injecter via env.config sous forme de code en ligne ou via source: URL (comme dans cet exemple)

env.vars (la version du nœud et Arch souhaités sont inclus ici et dans env.config, voir ci-dessous)

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 (récupérer et décompresser la version node.js souhaitée, créer des liens symboliques globaux, mettre à jour la version globale de 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 (crée/var/node_modules, le relie symboliquement au répertoire de l'application et exécute npm install. Vous pouvez installer n'importe quel module globalement à partir d'ici, ils atterriront dans /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 (notez la version du nœud ici aussi, et pour être sûr, mettez également la version du nœud souhaitée dans la configuration env dans la console AWS. Je ne sais pas lequel de ces paramètres aura la priorité.)

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/....

Et voilà: le déploiement d'une instance t1.micro prend désormais 20 à 30 secondes au lieu de 10 à 15 minutes! Si vous déployez 10 fois par jour, ce Tweak vous fera gagner 3 (trois) semaines par an. J'espère que cela aide et merci au personnel AWS EB pour mon week-end perdu :)

38
Kirill Kay

Il existe un package npm qui remplace le comportement EB par défaut pour npm install commande en tronquant les fichiers suivants:

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

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

Cela pourrait être mieux que simplement copier le script depuis SO, car ce paquet est maintenu et sera probablement mis à jour lorsque le comportement d'EB changera.

5
panK

J'ai trouvé une solution rapide à cela. J'ai parcouru les scripts de construction qu'Amazon utilise et ils n'exécutent que npm install si package.json est présent. Ainsi, après votre déploiement initial, vous pouvez le remplacer par _package.json et npm install ne fonctionnera plus! Ce n'est pas la meilleure solution mais c'est une solution rapide si vous en avez besoin!

1
Peter