web-dev-qa-db-ja.com

Elastic Beanstalkでnode_modulesの再構築を避ける

かなり単純なnode.jsアプリがありますが、AWS Elastic Beanstalkのデプロイメカニズムにより、単一のファイルコミットの後でも(git aws.Pushを介して)新しいバージョンをロールアウトするには約5分かかります。

つまりコミット自体(およびアップロード)は高速(Pushに1ファイルのみ)ですが、Elastic Beanstalkはパッケージ全体をS3から取得し、解凍してnpm installを実行します。これにより、node-gypがいくつかのモジュールをコンパイルします。インストール/構築が完了すると、Elastic Beanstalkは/var/app/currentを消去し、新しいアプリバージョンに置き換えます。

言うまでもなく、node_modulesを継続的に再構築する必要はありません。古いMacbook Airでは30秒かかり、ec2.microインスタンスでは5分以上かかりますが、再構築は面白くありません。

ここには2つのアプローチがあります。

  1. /opt/containerfiles/ebnode.pyを微調整し、node_modulesの場所を操作して、デプロイメント時の削除と再構築を回避します。
  2. elastic Beanstalk EC2インスタンスでgitリポジトリを設定し、基本的にデプロイ手順を自分で書き直します。したがって、/ var/app/currentはプッシュを受け取り、必要な場合にのみnpm installを実行します(Elastic BeanstalkはOpsWorksのようになります。)

どちらのオプションにも猶予がなく、AmazonがElastic Beanstalkフックとアーキテクチャを更新するときに問題が発生しやすくなります。

たぶん、誰かがアプリディレクトリにすでに存在するnode_modulesの継続的な再構築を避ける方法をより良いアイデアを持っていますか?ありがとうございました。

51
Kirill Kay

ありがとう、キリル、本当に助かりました!

npm installの簡単な解決策を見ているだけの人のために設定ファイルを共有しています。このファイルは、プロジェクトの.ebextensionsフォルダーに配置する必要があります。ノードインストールの最新バージョンが含まれておらず、すぐに使用できるため、より軽量です。

また、インストールされているノードのバージョンを動的にチェックするため、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注:npm -gバージョンアップグレードを実行するスクリプトを更新し(1回のみ、初期インスタンスのロールアウトまたは再構築時)、EB構成変更中のNPM操作を回避します(アプリディレクトリが存在しない場合、エラーを回避し、構成の更新を高速化します)。

さて、Elastic Beanstalkは最近のnode.jsビルド(おそらくサポートされているv.0.10.10を含む)で危険な動作をするので、先に進んでTweak EBで次のことを行うことにしました。

  1. env.config(AWS EBでまだサポートされていない最新のものを含む)に従って、node.jsの任意のバージョンをインストールする
  2. アプリ内のnode_modules dirを含む既存のノードモジュールの再構築を回避するため
  3. node.jsをグローバルにインストールします(必要なモジュールも同様)。

基本的に、env.configを使用してdeploy&configフックをカスタマイズされたフックに置き換えます(以下を参照)。また、デフォルトのEBコンテナセットアップでは、いくつかの環境変数が欠落しています($HOME例)およびnode-gyp再構築中に失敗することがあります(これを解決するためにlibxmljsを2時間グーグルして再インストールしました)。

以下は、ビルドに含まれるファイルです。 env.configを介してインラインコードとして挿入するか、source: URL(この例のように)

env.vars(望ましいノードのバージョンとArchはここと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バージョンを取得してungzipし、グローバルシンボリックリンクを作成し、グローバル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(/ var/node_modulesを作成し、app dirにシンボリックリンクし、npm installを実行します。ここから任意のモジュールをグローバルにインストールでき、/ 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(ここにもノードのバージョンに注意してください。安全のために、AWSコンソールのenv configにも目的のノードのバージョンを入れてください。これらの設定のどちらが優先されるかはわかりません。)

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

そこにあります:t1.microでは、インスタンスの展開に10〜15分ではなく20〜30秒かかります。 1日に10回展開する場合、この調整により1年で3週間節約できます。私の失われた週末をAWS EBスタッフに感謝し、特別な感謝を捧げます:)

38
Kirill Kay

次のファイルを切り捨てることにより、npm installコマンドのデフォルトのEB動作を上書きするnpmパッケージがあります。

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

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

このパッケージは維持されており、おそらくEBの動作が変更されたときに更新されるため、スクリプトをSOからコピーするよりも優れている可能性があります。

5
panK

これに対する簡単な解決策を見つけました。 Amazonが使用しているビルドスクリプトを調べたところ、package.jsonが存在する場合にのみnpm installが実行されます。したがって、最初のデプロイ後に_package.jsonに変更すると、npm installは実行されなくなります!これは最善の解決策ではありませんが、必要な場合は簡単に解決できます!

1
Peter