Nuxt.js アプリケーションを CircleCI 経由で App Engine Standard 環境へと自動デプロイする

Nuxt.js が遂に v2.0 になりましたね :tada:
私も個人的にほしい機能を Pull Request で出していたものが入って、ようやく使えるようになって嬉しい限りです。

v2 が出たことによって、 v1 である程度安定しているものの、 nuxt-edge とどっちを使うべきかで悩んでいた層が、「v2 から Nuxt.js をはじめてみよう」とはじめることも多くなるかと思います。

そんなときに悩みのタネになるのが、 Nuxt.js 特有のサーバー管理事情です。 SSR モードで立ち上げる場合、 Node.js サーバーが必要となるため、これまでのような静的サイトホスティングにてホストできるわけではありません。

ランニングコストや安定性、パフォーマンスを考えてサーバーを選択する必要があります。サーバー選定は非常に多くの選択肢があるかと思いますが、私個人としては、 Google App Engine の Standard 環境をオススメしております。

今回は、そんな App Engine 環境の魅力と、実際の自動デプロイ環境の構築までをご紹介したいと思います。

## App Engine の魅力について

Nuxt.js アプリケーションのデプロイ方法は、非常に多くの手法が存在します。まずは App Engine 以外の手法を簡単にまとめてご紹介します。

- IaaS サーバーへのデプロイ
    - いわゆる普通のサーバー管理
    - メンテナンスは大変であり、特に Nuxt.js はメモリリークが発生することも多いため気をつける必要がある
- Heroku へのデプロイ
    - 一番簡単な PaaS 管理
    - ランニングコストも安め
    - 海外リージョンしかないことによるレイテンシとパフォーマンスの低さが課題感
- コンテナ管理
    - ECS/EKS や GKE など
    - 柔軟な管理が可能でメリットは多くある
    - とはいえクラウドに慣れている人でなければ構築や管理のハードルは高い
- サーバーレス環境へのデプロイ
    - AWS Lambda や CloudFunctions 上で動かす
    - どちらのクラウドでもレイテンシ問題などがないのが魅力
    - やりかたとして遠回りであることと、特に AWS 環境に構築する場合トリッキーな設定が必要
    - 内部コンテナ数が増えるとき(スピンアップ)の起動が遅い

ざっとこのような形となります。どれもそれぞれ違った魅力がある反面、レイテンシやメンテナンスコスト、スピンアップの待ちの課題があります。また、 SLA 面でもつきまといます。

この点を App Engine は解決しています。それぞれの課題についての解決状況を以下に挙げます。

- 管理コスト
  - 完全なマネージドの環境であり、管理コストは非常に低い
  - 構築にあたっても、ローカルの Node.js 環境と特別な違いなく利用可能
- レイテンシやスピンアップの問題
  - 日本リージョンあり
  - 他のサービスや、同じ App Engine でも他の言語環境と比較して高速に起動する
- インフラ費用
  - サーバーレスなどと同じ実行時間課金なので必要最低限のコストがかかる
- SLA
  - 現状はベータであるが、 App Engine は正式版となった言語ランタイムは全て uptime SLA が保証される

上記のような魅力があり、金銭的なコストでも、管理コストでも、また、環境としてのカタログスペックの面でも非常に優良な環境となっています。

もちろん、他の手段をとったほうが良いことも場合によってはありますが、ひとまず「ふつうのNuxt.jsアプリケーション」を開発するときは、 App Engine で良いでしょう。

## 今回の構築環境について

そんな App Engine を使ってデプロイしていきたいところですが、管理者権限を使って手元でデプロイしている記事は多くあり、そういった記事を参考に、管理することも可能です。

ひとまずデプロイしてみたい！というかたは、 @SatoTakumi さんの [Nuxt.js on Google App Engine（GAE)](https://qiita.com/SatoTakumi/items/4f418dc5c700b9d66d7d) を参考とするのが非常に役に立つはずです。

この記事では、そこからもう少し踏み込んで、人間が手動でデプロイすることは一切なく、1stデプロイからずっと機械的にデプロイしていく環境の構築についてご紹介します。

記事内では CircleCI を利用し、 GitHub と連携、 master ブランチへのコミットを契機として、自動でデプロイを行ってみます。

## プロジェクトの作成とキーの設定

早速デプロイ環境を構築していきます。まずは Google Cloud のコンソールの操作から。

### 前提条件

以下を完了している前提で進めます。

- Google アカウントを所持している
  - GCP のアカウントを有効化済みで、いつでも利用できる
- GitHub アカウントを所持している
- CircleCI に登録している
  - Follow Project を押すだけで CI をスタートできる状態にある

### プロジェクトの作成

まずはプロジェクトを作成します。

https://console.cloud.google.com/ へとアクセスし、プロジェクトを新規作成してください。

![Screen Shot 2018-09-27 at 13.30.40.png](https://qiita-image-store.s3.amazonaws.com/0/75633/aaa9f206-e669-01fa-b83d-da1b1b3a6aa7.png)

プロジェクト名は適当で問題ありません。私は nenecchi.work というドメインを持っているので、今回はこれを利用することとします。

少し時間がかかりますが、完了するとコンソールトップへと遷移するはずです。

![Screen Shot 2018-09-27 at 13.33.06.png](https://qiita-image-store.s3.amazonaws.com/0/75633/15958522-7f94-cdcb-3d66-b53ba5a91466.png)

これでプロジェクトが作成できたため、ここに App Engine アプリケーションを紐づけていきます。

### App Engine アプリケーションの有効化

まずは App Engine のアプリケーションの有効化だけを行っておきます。Google Cloud の CLI ツールからこれを行うこともできるのですが、今回は CLI ツールをローカルに入れず、CI 上で deploy コマンドを打つだけに統一するため、ここだけは GUI で行っておきます。

GIF のように、 App Engine へと遷移して Node.js 環境を選択肢てください。

[![Screenshot from Gyazo](https://gyazo.com/bc12793c19094996f4580aba9848066d/raw)](https://gyazo.com/bc12793c19094996f4580aba9848066d)

選択すると、更にリージョンを選択する画面となりますが、画面に表示されている世界地図を見ながら、日本を選択するようにお願いします。選択すると、以下のような画面となるため、1分ほど待ちます。

![Screen Shot 2018-09-27 at 13.36.44.png](https://qiita-image-store.s3.amazonaws.com/0/75633/29b5d9e1-2cb9-770a-40c2-5f0866c867d7.png)

初期化が完了すると、チュートリアルに入ります。ここに入った時点でプロジェクトは有効化されているため、以後このコンソールに用事はありません。特にチュートリアルを行う必要もないため、つぎの設定へと移りましょう。

![Screen Shot 2018-09-27 at 13.37.13.png](https://qiita-image-store.s3.amazonaws.com/0/75633/52ca1ec1-186d-c206-f8af-196c59a16dc1.png)

### APIの有効化

次に、CIからデプロイするための API を有効化します。App Engineへのアクセスと同じように、ハンバーガーから「APIとサービス」へ遷移します。

![Screen Shot 2018-09-27 at 13.40.16.png](https://qiita-image-store.s3.amazonaws.com/0/75633/194e6e09-1ea3-8e45-2938-d32cb0a6e1cc.png)

遷移するとこのような画面が表示されますので、上部の「APIとサービスの有効化」を押して検索まどを表示させます。

![Screen Shot 2018-09-27 at 13.40.29.png](https://qiita-image-store.s3.amazonaws.com/0/75633/3655e253-de36-1983-7eb6-4a8a71d0b053.png)

表示された検索窓に「App Engine」と入力すると、 Admin API へのアクセスが出現するため、これを有効化します。

これでこのプロジェクト内において App Engine への Admin API が解放されました。

![Screen Shot 2018-09-27 at 14.41.10.png](https://qiita-image-store.s3.amazonaws.com/0/75633/326dc312-ab85-f10b-0cd5-52fd5e48b969.png)

同時にビルドに必要な、 Cloud Build の API も有効化しておいてください。

### サービスアカウントの作成

最後に、自動デプロイする CI のための仮想ユーザーを作成します。ハンバーガーから「IAMと管理」へとアクセスしてください。そのうえで、画面上部の「サービスアカウントを作成」をクリックし、作成画面へと遷移します。

![Screen Shot 2018-09-27 at 13.43.33.png](https://qiita-image-store.s3.amazonaws.com/0/75633/447d3a12-4835-1b91-71e0-faece337b030.png)

はじめにサービスアカウントの名前の入力を求められますが、適当に circleci などで構いません。

![Screen Shot 2018-09-27 at 13.44.03.png](https://qiita-image-store.s3.amazonaws.com/0/75633/2e53cf76-0437-a0cc-ec8c-6650eb3ff599.png)

次に、権限を求められますが、ひとまず以下の権限としておいてください。 App Engine は、デプロイにあたって初回はバケットの新規作成へのアクセスが必要など、いくつか Write 権限が必要なものがありますので、少々強めの権限としています。

この権限の範囲でできることは、 App Engine / GCS / Cloud Build への R/W アクセスとなります。

![Screen Shot 2018-09-27 at 13.45.26.png](https://qiita-image-store.s3.amazonaws.com/0/75633/dfc1cf14-d8a8-949a-874e-e9c13154937b.png)

最後に〆です。ここは今回は入力は無視してくださって構いません。ですが、最後の「キーを作成」だけは忘れずに押すようにしてください。

その上で、「JSON」形式のキーをダウンロードしてください。

![Screen Shot 2018-09-27 at 13.45.42.png](https://qiita-image-store.s3.amazonaws.com/0/75633/34a8a686-7977-215c-557c-40bb89aa87c3.png)

ダウンロードが完了したら、「完了」を押し、ユーザーの作成は終了です。

これで Google Cloud のコンソールでの操作は全て完了したため、次からはソースコードの準備と CircleCI の設定に取り掛かります。

## 自動デプロイ環境の構築

それではいよいよ自動デプロイを行っていきます。

### テスト用の Nuxt.js プロジェクトの作成

まずは Nuxt.js のプロジェクトを作成します。適当に create-nuxt-app で、プロジェクトを作ると良いでしょう。特にこだわりがない場合は、全て Enter で OK です。

```bash
$ create-nuxt-app nenecchi-work
> Generating Nuxt.js project in /Users/potato4d/Desktop/nenecchi-work
? Project name nenecchi-work
? Project description My exquisite Nuxt.js project
? Use a custom server framework none
? Use a custom UI framework none
? Choose rendering mode Universal
? Use axios module no
? Use eslint no
? Author name HANATANI Takuma
? Choose a package manager yarn
```

これでプロジェクトが作成されるはずですので、これをお好きなエディタで開いてください。

### App Engine と CircleCI 用の設定ファイルの追加

プロジェクトができたら、そこに App Engine と CircleCI 用の設定を追加します。

#### app.yaml の作成

App Engine で動作させるためには、 app.yaml というファイルが必要です。このファイルは多くの設定が記述できますが、 Nuxt.js アプリケーションを動かす場合は 2 つだけあれば十分なので、以下を設定しておいてください。

```app.yaml
runtime: node8
env: standard
```

#### NPM scripts の修正

次は NPM scripts を修正します。以下のように設定してください。

```diff:package.json
{
  "name": "nenecchi-work",
  "version": "1.0.0",
  "description": "My exquisite Nuxt.js project",
  "author": "HANATANI Takuma",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
-    "start": "nuxt start",
+    "start": "PORT=8080 HOST=0.0.0.0 nuxt start",
    "generate": "nuxt generate"
  },
  "dependencies": {
    "nuxt": "^1.0.0"
  },
  "devDependencies": {
    "cross-env": "^5.0.1",
    "autoprefixer": "^8.6.4"
  }
}
```

#### CircleCI の設定の追加

最後に CircleCI の設定を追加します。今回は CircleCI の設定がメインテーマではないため詳細は割愛しますが、 build と deploy がわかれており、コメントアウトの箇所を外すと全ブランチで build が、 master ブランチで deploy が走るようになります。

```.circleci/config.yml
version: 2
jobs:
  build:
    working_directory: ~/app
    docker:
      - image: circleci/node:8.10.0
    steps:
      - checkout
      - run: echo 1 # ここにあなたのプロジェクトにあわせたテストを記述
  deploy:
    working_directory: ~/app
    docker:
      - image: google/cloud-sdk:217.0.0-alpine
    steps:
      - checkout
      - run: apk add --no-cache nodejs npm yarn
      - run: yarn
      - run: yarn build
      - run: echo $GCLOUD_SERVICE_KEY > ${HOME}/gcloud-service-key.json
      - run: gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json
      - run: gcloud app deploy --quiet --project nenecchi-work

workflows:
  version: 2
  main:
    jobs:
#      - build # プロジェクトにあわせたテストを記述した場合はここのコメントアウトを外す
      - deploy:
          filters:
            branches:
              only: master
```

ここまでできたら、 GitHub にあるリモートレポジトリに push しておいてください。

### CircleCI へのサービスアカウント情報の追加

次に、先程ダウンロードした App Engine のサービスアカウント情報を、 CircleCI に載せます。ターミナルで対象を cat して、 pbcopy してやります。

```bash
$ cat nenecchi-work-124rfqf3.json | pbcopy
```

コピーしたら、 CircleCI の環境変数の画面へとアクセスします。URL の形式は、以下のとおりです。

https://circleci.com/gh/:user-or-org-name/:repo-name/edit#env-vars

アクセスすると、環境変数の管理の画面となるため、ここの Add  Variable を選択し、 GCLOUD_SERVICE_KEY の名前で先程の JSON をコピーしてきた内容を貼り付けます。

![Screen Shot 2018-09-27 at 14.25.55.png](https://qiita-image-store.s3.amazonaws.com/0/75633/20c0f966-6985-591c-6d26-a1876f3062d3.png)


### CircleCI のビルド発火

ここまでできたら、後は CircleCI のビルドの発火のみとなります。まだFollowしていないプロジェクトの場合はそのまま Follow Project をクリック。

もし Follow して既にビルドが走っている場合、初回は GCLOUD_SERVICE_KEY が存在せず失敗しているはずなので、リトライを実行してください。

![Screen Shot 2018-09-27 at 14.00.28.png](https://qiita-image-store.s3.amazonaws.com/0/75633/0daf871a-2685-e494-6870-bdbf2e047926.png)

しばらく待つこととなりますが、デプロイが完了するはずです。

### デプロイ確認

完了したら、 `http://<project-name>.appspot.com` へとアクセスしましょう。

![Screen Shot 2018-09-27 at 14.52.23.png](https://qiita-image-store.s3.amazonaws.com/0/75633/93e85547-1274-3ca2-f1c0-21887b2eaaff.png)


プロジェクト名の Nuxt.js のデフォルト画面が表示されるはずです。サンプルでは、せっかくなので公式フリー素材のアイコン画像にしてみました。
http://newgame-anime.com/special/twticon.html

## おわりに

初期設定のためにいくつかサービスをまたぐ必要がありますが、全てをあわせても 10 分程度で自動デプロイ環境が構築できるはずです。

手動でデプロイする場合は当然ある程度待ち時間もできてしまいますし、何より個人にデプロイフローが依存してヒューマンエラーの原因にもなりますので、何かサービスを本番稼働させる場合は、このように自動デプロイ環境を構築しておくと良いでしょう。

今回使った App Engine の Standard 環境は、アクセスが全く無い場合は課金が走りませんので、ステージング環境としてはじめは作っておくだけでも非常に有用です。百円に満たない程度のランニングコストで構築できる、軽量かつ安定したステージング環境として利用していき、本番デプロイではそのコードを使い回すという流れは非常に有効に左右するはずです。

Nuxt.js アプリケーションの運用は、非常に頭を悩ませる分野ではありますが、マネージド環境を有効活用してラクをしていきましょう。