GCPのCloud Buildからnodejs8 on GAEを動かす時にはyarnでなくnpmを使おうというお話

Cloud Buildいいですよね。
今まではデプロイの自動化をしようとすると、CircleCIとか、werckerで同等機能を外部で用意するか、それともGCEでインスタンスを立ててとかやる必要があったんですが、GCP内でサーバレスで完結できるので構成がとてもシンプルにできます！
AWSだったらcode deployがあるのになぁと羨んでいたんですが、それも過去の話に:v:

そんな便利なCloud Buildなんですが……cloud buildからnodejs8のソースをGAEにdeployする時にちょっとした罠っぽいものがあったので備忘録もかねて:sob:

# 問題のソース:alien:

https://github.com/fumihiko-hidaka/nodejs8-by-yarn

## package.json
```json
{
  "name": "nodejs8-by-yarn",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "engines": {
    "node": "8.x.x"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "express": "*",
    "pug": "^2.0.3"
  }
}
```

## yarn.lock
```yaml
express@*:
  version "4.16.2"
  resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
```

## cloudbuild.yaml
```yaml
steps:
- name: 'gcr.io/cloud-builders/yarn'
  args: ['install']
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['app', 'deploy', '--version', '1', 'app.yaml']
```

と、こういう構成になっています。
expressの最新版が4.16.2だった頃に\*でinstallしたという設定のpackage.jsonです。
cloudbuildでもyarn installした後にデプロイしているので大丈夫そうですね。
(\*でinstallするのはどうなんだろう？　というのはあるんですが）

# 実際にアクセスしてみると:scream:

ローカルでの表示
![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/c5587806-f51e-fc9b-dbef-6f82f56fb093.png)

サンプルページは[こちら](https://neko-tech-test.appspot.com/nodejs8-by-yarn)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/5abda8fd-243d-4e83-4bbf-a360c6a29661.png)

*あ…ありのまま今起こった事を話すぜ！
yarn.lockに書いて4.16.2をインストールしたと思ったのに、4.16.3がインストールされていた
な…　何を言っているのか　わからねーと思うが　おれも　何をされたのか　わからなかった…
頭がどうにかなりそうだった…　催眠術だとか超スピードだとか そんなチャチなもんじゃあ　断じてねえ
もっと恐ろしいものの片鱗を　味わったぜ…*

# localからデプロイしてみる:robot:

頭がポルポル状態なので、まずはローカルの状態をデプロイしてみようと思います。

```sh
$ gcloud app deploy --project neko-tech-test --version 1 app.yaml
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/07453031-49d5-1625-b7b5-4327b3979da5.png)

がっ……ダメっ……！
結果は変わらない……!

# 原因を調べてみる:thinking:

という訳で原因を調べてみようと思います。
まず、localでgcloudコマンドを使ってdeployしようとすると、**.gcloudignore**というファイルが作成されるので中を覗いてみると、node_modulesをdeployしないという設定になっているじゃないですか！

```bash
# Node.js dependencies:
node_modules/
```

確かにアップロードの速度がpython27 on GAEの場合と比べて明らかに早いのです。
python27の場合はpip installしたライブラリをlibディレクトリかどこかに追加した上でGAEにアップロードする必要があるのですが、そういったライブラリを全部含んだアップロードの速度とは思えない速さ！

つまり、nodejs8 on GAEをアップロードする時にはnode_modulesのディレクトリは除外されている……！

# node_modulesのインストールはどこで実行されている？:space_invader:

つまり、node_modulesのインストールがどこで実行されているのか、ということになります。

と思ってcloud buildを見てみると……

![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/1b293f9c-d501-f1b3-cbc2-2b576dd1539e.png)

nodejs8のruntimeをGAEにdeployした履歴が残っていました。
転送元が「-」になっているのが、対象の履歴になります！

## 中で何をしているのか:spy:

中で何をしているのかが気になるので見てみます。

```
Step #1 - "builder": INFO starting: build process for FTL image
Step #1 - "builder": INFO starting: rm_node_modules
Step #1 - "builder": INFO rm_node_modules rm -rf /workspace/node_modules
Step #1 - "builder": INFO `rm_node_modules` stdout:
Step #1 - "builder": 
```

> Step #1 - "builder": INFO rm_node_modules rm -rf /workspace/node_modules

node_modulesを全て削除するという不穏な履歴が！
そしてこのままlogを読み進めてみると！

```
Step #1 - "builder": INFO npm_install npm install --production
Step #1 - "builder": INFO `npm_install` stdout:
Step #1 - "builder": added 113 packages in 5.001s
Step #1 - "builder": 
Step #1 - "builder": INFO `npm_install` had stderr output:
Step #1 - "builder": npm notice created a lockfile as package-lock.json. You should commit this file.
```

npm installでnode_modulesを用意し……package-lock.jsonがないよ！と警告を出されていました:frowning2:

# まとめ:clipboard:

- nodejs8のruntimeをGAEにdeployする時には、Google側のdeployの処理の段階でnpm installが走る
- rm -rf /workspace/node_modulesも走る
- なので、gcloud app deployをする前にyarnする必要はない
- というか、yarn.lockではなくてpackage-lock.jsonで管理するようにしましょう

ということでした！！

# 最後に
[株式会社ネコカリ](https://www.nekokari.co.jp/)では猫の手も借りたい🔥炎上中🔥なお仕事を募集しています！
一緒に働くメンバーも募集していますので、よかったら是非！