GAE Nodeのgcp-buildでNuxt.jsのdeployを簡略化しようとして断念している話

これは[Google Cloud Platform その２ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/gcp2) 23日目の記事です。

# tl;dr

- GAE SE Nodeで`gcp-build`を実行してくれるようになりました
- でも使えていません←イマココ

# 倉科カナさんとわたし

**ここは読み飛ばしてよいです**

今日は12月23日です。
12月23日といえばみなさんはなにを思い浮かべますか?
天皇誕生日だから祝日だやっぽい!でしょうか?

しかしながらこの日誕生日を迎えるのはなにも天皇に限ったことではありません。
そうです、あの倉科カナさんのお誕生日でもあるのです:tada:
倉科さんとわたしは同郷(熊本出身)なのですが、なにを隠そう誕生日も一緒なのです:tada::tada:
これは運命かもしれません…!
ちなみに今年生まれた次女はカナと名付けました。

↓以下本文:bow:

# GAE Node SE 10月のアップデート

今年(2018年)の10月31日、ベータリリース中のGAE Standard Environment Nodeについて以下のページでにアップデートが紹介されました。
[Node\.js 10 available for App Engine, in lockstep with Long Term Support \| Google Cloud Blog](https://cloud.google.com/blog/products/application-development/announcing-nodejs-10-for-app-engine)

LTSになったNode10が使えるようになり、NPMしか使えなかったのがYarnもサポートされるようになりました。

あと地味な感じ(?)で以下もリリースされました。

> Custom build step: If a script named gcp-build is present in package.json, this script will be executed at deployment time with access to dependencies declared in devDependencies.

`package.json`に`scripts.gcp-build`を書いておくと、`gcloud app deploy`時に実行してくれるというものです。

## Nuxt.jsのbuildで`gcp-build`を使ってみる

JSでSPA開発をしているとdeployの前にwebpack等でbuildする必要があると思います。
Nuxt.jsだと`nuxt build`コマンドなどです。
`gcp-build`以前はCloud BuildやCircleCIなどで構築したdeploy pipeline上でこれを実行していたと思いますが、基本的には不要になります。

GAEで動かすためのNuxt.jsのサンプルリポジトリを用意しました。
https://github.com/iwata/nuxt-gae-se

```json:package.json
{
  "scripts": {
    "gcp-build": "nuxt build",
    ...
  },
  ...
}
```

`gcp-build`の中で`nuxt build`を実行するようにしています。
こうしておくことでdeploy時には`gcloud app deploy`だけを実行すればよくなります。
これでdeploy pipelineがかなりシンプルにできることが分かります。
`cloudbuild.yaml`で考えてみましょう。

`gcp-build`以前だと以下のようになります。

```yaml:cloudbuild.yaml
steps:
- id: yarn-install
  name: node:10
  entrypoint: yarn
  args: ['install']
- id: build
  name: node:10
  entrypoint: yarn
  args: ['build']
- id: deploy
  name: gcr.io/cloud-builders/gcloud
  args:
    - --quiet
    - app
    - deploy
```

それが`gcp-build`を使うようにするとstepが1つだけになります。

```yaml:cloudbuild.yaml
steps:
- id: deploy
  name: gcr.io/cloud-builders/gcloud
  args:
    - --quiet
    - app
    - deploy
```

`yarn install`, `yarn build`はFTL上で実行されるからです。便利ですね。
ちなみ`gcloud app deploy`でどういう処理が実行されているかはCloud BuildのHistoryが参照できるようになっています。
さきほどのrepositoryでdeployした場合のlogを一部抜粋すると以下のようになっています。

```
Step #1 - "builder": INFO yarn_run_gcp_build yarn run gcp-build
Step #1 - "builder": INFO `yarn_run_gcp_build` stdout:
Step #1 - "builder": yarn run v1.9.4
Step #1 - "builder": $ nuxt build
Step #1 - "builder": ℹ Production build 06:50:50
Step #1 - "builder": ✔ Builder initialized 06:50:50
Step #1 - "builder": ✔ Nuxt files generated 06:50:50
Step #1 - "builder": ℹ Compiling Client webpackbar 06:50:51
Step #1 - "builder": ✔ Client: Compiled successfully in 10.40s webpackbar 06:51:02
Step #1 - "builder": ℹ Compiling Server webpackbar 06:51:02
Step #1 - "builder": ✔ Server: Compiled successfully in 1.46s webpackbar 06:51:03
Step #1 - "builder": Done in 14.78s.
```

`gcp-build`の中で`nuxt build`が実行されていることがわかります。


## `gcp-build`のバグ

実はこの`gcp-build`にはちょっと前までバグがありまして、実行されるときと実行されないときがありました。
Cloud Build上のログをみると分かるのですが、buildプロセスを一部キャッシュして高速化していることが分かります。

該当のログを抜粋

```
Step #1 - "builder": DEBUG Checking cache for cache_key b627ceb6e74444d52e3dc5d8d8271de93739a96cb207c4d9b0229b5629c7ddea
Step #1 - "builder": INFO Found cached base image: asia.gcr.io/serverless-test-sms/app-engine-tmp/build-cache/ttl-7d/node-cache:b627ceb6e74444d52e3dc5d8d8271de93739a96cb207c4d9b0229b5629c7ddea.
Step #1 - "builder": INFO Found cached dependency layer for b627ceb6e74444d52e3dc5d8d8271de93739a96cb207c4d9b0229b5629c7ddea
Step #1 - "builder": INFO {"architecture": "amd64", "author": "Bazel", "config": {}, "created": "2018-12-21T06:00:00Z", "history": [{"author": "Bazel", "created": "1970-01-01T00:00:00Z", "created_by": "bazel build ..."}], "os": "linux", "rootfs": {"diff_ids": ["sha256:16d82ecd2fbff51e859e0e6970a41792a84527b5427c1a0ea1081c31fe103535"]}}
Step #1 - "builder": INFO [CACHE][HIT] v0.13.0:NODE->b627ceb6e74444d52e3dc5d8d8271de93739a96cb207c4d9b0229b5629c7ddea
Step #1 - "builder": INFO checking_cached_packages_json_layer took 0 seconds
Step #1 - "builder": INFO starting: Building app layer
Step #1 - "builder": INFO starting: tar_runtime_package
Step #1 - "builder": INFO tar_runtime_package tar -pcf /tmp/tmpXmT14F.tar --transform flags=r;s,^,/srv/, --exclude *.pyc .
```

`[CACHE][HIT]`とあるとキャッシュが使われたことを意味するのですが、キャッシュヒット時には`gcp-build`が実行されないという状態でした。
ということでIssue Trakerにあげて対応してもらいました:smile:
[\[Standard Environment\]\[Node\] Not execute gcp\-build \[119335120\] \- Visible to Public \- Issue Tracker](https://issuetracker.google.com/issues/119335120)

この修正がrolling outされたのがここ数日のようで試したところちゃんと動くようになっていました。

## `gcp-build`の要望

いまGAE上にNuxt.jsでSSRしているプロジェクトがあるのですが、実はまだ`gcp-build`を使えていません。
なぜかというと、`gcp-build`実行時に環境変数を指定できないからです。
いま`nuxt build`時にクライアントサイド用の環境変数を埋め込んでいるのですが、これは開発環境と本番環境で違う値を埋め込む必要があります。
サーバサイドでは`gcloud app deploy`時に指定する`app.yaml`を切り替えることで環境変数を切り替えています。(app.yamlとapp_dev.yamlなど)
`gcp-build`実行時には`app.yaml`の`env_variables`をみてくれたりはしないので、`gcp-build`内で環境変数を指定するしかありません。

```json:package.json
"scripts": {
  "gcp-build": "cross-env APP_ENV=prod nuxt build"
}
```

こうすると`package.json`を置換したりする必要があります。
これはさすがにイケてないので移行できずにいます。
こちらもissue trackerにあげてはいますが対応されるのかは不明です。

[\[Standard Enviroment\]\[Node\] execute gcp\-build with flexible environment variables \[119568232\] \- Visible to Public \- Issue Tracker](https://issuetracker.google.com/issues/119568232)[^1]

[^1]: みなさんで要望挙げてもらうと積極的に対応されると思うんで追記お願いします:bow: