0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CloudBuild + Nuxt:SSR + Firebase でアプリをリリースする

Last updated at Posted at 2020-10-09

この記事を書いた背景

現在、FirebaseでWebアプリを作っており、CI/CDにCloud Buildを使って環境を構築した。
全体的にCloud Buildのドキュメントが少なく、ハマったポイントが多かったので、メモとして残すことにする。

Cloud BuildをCI/CDで採用した理由

CircleCI、github Actions、Cloud Build で比較検討した結果、Cloud Buildを使うことにした。
主な理由としては、作成中のアプリではGCPの他サービスも使っており、それらと連携しやすい && 請求書を1つにまとめられるのが大きな理由。

個人的な見解としては、、、上記のような特に理由が無ければ、現時点ではCloud Buildはまだ使わない方がいいかも。Slackとの連携が面倒だったり、ドキュメントの数が他のCIサービスに比べると少ない。Google公式のドキュメントもあるが、テキストだけの説明なので、理解するのにハードルが高い。

2018年のブログ記事ですが、各種CI/CDのサービスを比較しているので、参考にするといいかも。
https://swet.dena.com/entry/2018/08/20/170836

サービスの構成

Nuxt(SSR)===> github(pushがトリガー) ===> Cloud Build ===> Firebase ===> 公開

少し余談だが、今回はSSRでページを動的に表示させたかったので、buildしたNuxt Appをfunctions上に配置している。
詳しくは、この記事を参考に。
https://qiita.com/sychocola1/items/c3f329da3a14c85c3a73

構築の手順

下記の参考に実施すると分かりやすい。
https://yanou.jp/deploy-firebase-hosting-by-cloud-build/
前提として、Local環境ではNuxt Appが動いている状態から説明。
ざっくりと下記の手順で作成する。

  1. GCPのCloud Builderにて、トリガーを設定。その際に、githubのレポジトリーを登録
  2. Cloud Builderで利用するDockerイメージを作成して、gcloudにsubmit
  3. cloudbuild.yaml に実行コマンドを登録して、githubにpushする

Dockerイメージを作成する

今回、cloud buildでデプロイする際に、下記を実施したかった。

  1. Slackでデプロイの成功/失敗を通知させる
  2. FirestoreのSecurity Rulesをjestで実行する
  3. dotenvで利用している.env.XXXX ファイルを、GCPの「Secret Manager」経由で取得する(githubには、.gitignoreを設定してpushしていないので。)

2を実行するためには、Dockerのコンテナ上にfirestoreのエミュレーターをバックグランドで起動させる必要があるので、javaがインストールされている必要がある。

ポイントは、nodeとopenJDKのみがインストールされており、それ以外の不要なパッケージは存在しないミニマルなDockerコンテナが欲しかった。(不要なパッケージがあると、イメージのbuild時間に影響する。)
ちょうど、いい感じのDockerイメージを公開している人がいたので、今回はこちらを採用。
https://github.com/devayansarkar/maven-node-openjdk

プロジェクトのルートディレクトリーから、下記コマンドで利用できるようになる。

$ cd ROOT_DIRECTORY
$ git clone git@github.com:devayansarkar/maven-node-openjdk.git
$ cd maven-node-openjdk
$ gcloud builds submit --tag gcr.io/${PROJECT_ID}/maven-node-openjdk --project ${PROJECT_ID}
$ cd ..
$ rm -fr maven-node-openjdk

cloud buildの成功/失敗をslackで通知する

下記の公式ドキュメントに通知方法が書いてあるのですが、Pub/Subを発行して、IAMを設定したりとかなり大変なので、下記の方法で簡単に実装。
https://qiita.com/tnagao3000/items/ff7dd2e89fd8cb42ad5a
自分で、slackに通知する文面やiconも変更できるので、特に困ることはない。

「Secret Manager」経由で環境変数を取得する

dotenvを利用して、production/staging/developmentの環境変数を使い分けており、local環境でbuildして、直接Firebaseにdeployする分には問題なかったのだが、github経由でfirebaseにdeployとなると、事前に登録した.env.xxxxファイルを、gcloudコマンドで取得することにした。
詳しくは、公式ドキュメントが参考になる。
https://cloud.google.com/cloud-build/docs/securing-builds/use-encrypted-secrets-credentials?hl=ja

完成したcloudbuild.yaml

cloudbuild.yaml
steps:
  - id: "Watch:slackbot"
    name: "gcr.io/$PROJECT_ID/slackbot"
    args:
      [
        "--build",
        "$BUILD_ID",
        "--webhook",
        "https://hooks.slack.com/services/XXXXXXXX",
      ]
  - id: "Install:npm_packages"
    name: "gcr.io/$PROJECT_ID/maven-node-openjdk"
    args: ["npm", "install"]
    dir: "src"
    waitFor: ["-"]
  - id: "Install:functions_npm_packages"
    name: "gcr.io/$PROJECT_ID/maven-node-openjdk"
    args: ["npm", "install"]
    dir: "functions"
    waitFor: ["-"]
  - id: "Fetch:dotenv"
    name: gcr.io/cloud-builders/gcloud
    entrypoint: "bash"
    args:
      [
        "-c",
        "gcloud secrets versions access latest --secret=dotenv_file --format='get(payload.data)' --project $PROJECT_ID | tr '_-' '/+' | base64 -d > .env.${_FIREBASE_ENV}",
      ]
    dir: "src/config"
    waitFor: ["Watch:slackbot"]
  - id: "Run:test"
    name: "gcr.io/$PROJECT_ID/maven-node-openjdk"
    args: ["npm", "run", "ci-test"]
    dir: "src"
    waitFor: ["Install:npm_packages", "Install:functions_npm_packages"]
  - id: "Build:App"
    name: "gcr.io/$PROJECT_ID/maven-node-openjdk"
    args: ["npm", "run", "build-nuxt:${_FIREBASE_ENV}"]
    dir: "src"
  - id: "Deploy:Firebase"
    name: "gcr.io/$PROJECT_ID/maven-node-openjdk"
    args: ["npm", "run", "ci-deploy:${_FIREBASE_ENV}"]
    dir: "src"
timeout: 1200s

${_FIREBASE_ENV} は、githubをトリガー登録した際に、変数として独自に登録。
production or staging の文字列が入る。

src/package.jsonの一部ですが、公開します。

src/package.json
{
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "copy-nuxt": "rm -rf ../functions/.nuxt && cp -R .nuxt/ ../functions/.nuxt && cp nuxt.config.js ../functions/src/nuxt.config.js && rm -rf ../public/* && mkdir -p ../public/_nuxt && cp -R .nuxt/dist/client/ ../public/_nuxt && cp -a static/. ../public/ && cp -R ./config/ ../functions/src/config",
    "build-nuxt:production": "NODE_ENV=\"production\" nuxt build && npm run copy-nuxt",
    "build-nuxt:staging": "NODE_ENV=\"staging\" nuxt build && npm run copy-nuxt",
    "start:production": "NODE_ENV=\"production\" nuxt start",
    "start:staging": "NODE_ENV=\"staging\" nuxt start",
    "generate": "nuxt generate",
    "test": "jest",
    "ci-test": "firebase emulators:start --only firestore & sleep 10 && jest",
    "ci-deploy:staging": "firebase deploy --non-interactive --force --project staging",
    "ci-deploy:production": "firebase deploy --non-interactive --force --project production"
  },
}

ここでのポイントは、firestoreのエミュレーターをバックグランドで立ち上げた後に10秒待ってから、テストを実行していること。エミュレーターが立ち上がるまで数秒かかるので。
この辺りのノウハウは、下記の記事を参考にすると分かりやすい。
https://rightcode.co.jp/blog/information-technology/test-dirven-firestore-security-rules-ci

また、普通にfirebaseをdeployすると、不要なファイルを削除する?などのメッセージが出るので、 --force を付けて、強制的にYesにしています。

Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N) 

最後に 。。。

これ以外にも、cloud buildを構築する上で、いくつか悩んだポイントがあるのですが、今後、時間が出来たら記載したいと思います。
この記事が誰かの役に立ったら、嬉しいです!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?