はじめに
npmパッケージの管理手法の一つにmonorepoがあります。
単一のGitリポジトリの中に、複数のパッケージを置くやり方です。
例えば以下の様なディレクトリ構造をとります。package.jsonが複数あります。
/
package.json (ルートパッケージのpackage.json)
packages/ (このディレクトリの下に各パッケージが並ぶ)
my-web-app/ (子パッケージの一つ)
package.json
my-lib/ (これも子パッケージの一つ)
package.json
この記事では、こうしたmonorepoを採用しているプロジェクトにおいて、ある子パッケージ(Webアプリ)をAWS Elastic Beanstalkにデプロイする際の留意点(トライ&エラー体験談)について書いていきます。
上記の例でいうと、my-web-appをデプロイするケースを考えます。
my-web-appは、兄弟パッケージであるmy-libに依存しているとします。
monorepoやElastic Beanstalk (EB)自体の説明はあまりしません。
どこでeb deployを実行するか
my-web-appをEBにデプロイしたいので、仮にmonorepoでなかったとしたら、普通にmy-web-appをカレントディレクトリとしてeb deploy
のコマンドを実行するでしょう。
しかし今回はmy-web-appが兄弟のmy-libを参照したいわけです。
my-web-appディレクトリ直下でeb deploy
をすると、デプロイ先(EC2インスタンス)にはmy-libディレクトリが存在しないわけで、my-web-appを正常に起動できません。
となると、代わりにルートディレクトリ直下でeb deploy
をして、monorepoを丸ごとデプロイする方法が考えられます。
これならmy-libもデプロイされます。
ルートパッケージのpackage.jsonには、例えば以下の様に書くことになるでしょう。
"scripts": {
"start": "cd packages/my-web-app && npm start"
}
各パッケージにてnpm installさせるのは困難
monorepoでなかったら、EBにデプロイすると自動的にnpm install
が実行されて、次に(デフォルトでは)npm start
が実行されるのが、EBの基本の流れですよね。
ところが前節にて、eb deploy
をするのはmonorepoのルート直下としたので、このままだと各パッケージにおいてはnpm install
が実行されません。
単純な対策例として、ルートパッケージのpackage.jsonに以下の様に書くことが考えられます。
postinstall
に愚直に書いて、各パッケージでのnpm install
の実行を図ります。
"scripts": {
"postinstall": "npm run install:lib && npm run install:web",
"install:lib": "cd packages/my-lib && npm install",
"install:web": "cd packages/my-web-app && npm install"
}
ところがこれはうまく動きません。どうもEBでは、postinstall
は実行されないようなのです。
prestart
は実行されるようなので、強引ですがそちらで代用することはできます。
(このあたりに言及している参考URLがこちらにあります)
が、仮にprestart
においてnpm install
をさせようとしても、今度はEC2上のファイルシステムのパーミッションの関係で(?)、以下の様にnpm install
が失敗します。
> cd packages/my-lib && npm install
npm WARN locking Error: EACCES: permission denied, open '/tmp/.npm/_locks/staging-1f97e5e51d5dce3f.lock'
npm WARN locking /tmp/.npm/_locks/staging-1f97e5e51d5dce3f.lock failed { Error: EACCES: permission denied, open '/tmp/.npm/_locks/staging-1f97e5e51d5dce3f.lock'
npm WARN locking stack: 'Error: EACCES: permission denied, open \'/tmp/.npm/_locks/staging-1f97e5e51d5dce3f.lock\'',
結局、自前のnpm scriptsによってnpm install
を実行させることはできないのです。
EBの仕組みとして用意されているnpm install
(デプロイ直後にルート直下で実行されるnpm install
)しかできません。
このあたりの挙動はAWS公式のドキュメントなどで言及している箇所が見つけられず、やってみたらそうなったという結果です。
いっそnode_modules込みでデプロイしちゃおう
以上から、各パッケージの依存性(node_modulesディレクトリ)をデプロイ後にインストールさせるのは難しいことがわかりました。
そこで、もういっそnode_modulesをデプロイ用zipに含めた上で、EBにデプロイする方針を採ります。
大抵の場合、EBにデプロイする時はnode_modulesをzipに含めたくないですから、.ebignore
ファイルに以下の様に書くことで除外しているでしょう。
node_modules
今回は逆に含めたいので、単純に上記の記述を削除すればよいです。
もしも、ルート直下のnode_modulesだけはいつも通り除外したければ、例えば以下の様に行頭にスラッシュを書けばよいです。
/node_modules
まとめ
EBはWebアプリを簡単にデプロイできる便利なサービスですが、monorepoとはやや相性が良くないようです。
そこで、各パッケージのnode_modules込みのzipをEBにデプロイすることで、monorepoでもEBの仕組みに乗ることができました。