GAミント至上主義

Web Monomaniacal Developer.

Vue.jsとFirebaseで本番環境、ステージング環境をなるべく節約して構築する

yagish履歴書は、システム1人、デザイン1人、イラスト1人、計3人の小さなチームで作っていて、開発は非常に身軽なので、本番公開時もローカルの開発環境+Firebase本番環境だけだった。

rirekisho.yagish.jp

でもさすがに大きな変更を本番でやるのは怖いので、手間と費用をなるべく抑えつつ、Firebaseを使ったステージング環境を構築することにした。

開発環境はVue CLI 3で構築。

cli.vuejs.org

環境変数

これは3ファイルで分ける

.env.production
.env.development
.env.staging

ステージング環境について公式ドキュメントに書いてあるので、参考にする。
cli.vuejs.org

.env.staging

NODE_ENV=production
VUE_APP_TITLE=My App (staging)

ただNODE_ENVを上書きしてしまうので、ステージングの時に切り替えたい部分でNODE_ENV === 'staging'を判定に使うことができないため、VUE_APP_MODEという変数を追加して使うことにした。
あんまりきれいじゃない。
.env.staging

VUE_APP_MODE=staging

ビルド時に書き出しディレクトリを変えて、Dockerコンテナは一つで済ませる

開発環境はDockerコンテナで行っているが、これを本番、ステージングなどで分けてしまうと面倒くささが圧倒的に増すのでやりたくない。
開発サーバーの実行、ステージング用ビルド、本番用ビルドもすべて一つのコンテナで行う。

まず、buildコマンドを明示的にbuild-staging、build-productionでそれぞれ--modeをつけて分ける。

package.json

{
  "name": "yagish",
  "version": "1.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build-staging": "vue-cli-service build --mode staging",
    "build-production": "vue-cli-service build --mode production",
    // etc...
  },
// etc...

これだけだとビルドしたファイルが同じdistディレクトリに入ってしまい、間違えてステージングのファイルを本番にデプロイなんてことも起きそうなので分ける。

書き出し先ディレクトリはvue.config.jsのoutputDirで行う。
上記で書いた環境変数を使ってディレクトリを変える関数を作って切り替えた。

vue.config.js

const getOutputDir = () => {
  if (process.env.VUE_APP_MODE === "production") {
    return 'dist-production'
  } else if (process.env.VUE_APP_MODE === "staging") {
    return 'dist-staging'
  }
}
module.exports = {
  outputDir: getOutputDir(),
  // etc...

この設定でステージングはdist-stagingディレクトリに、
本番はdist-productionに書き出されるようになった。
distディレクトリは間違わないよう消しておいた

Firebaseにデプロイ

次にFirebase Hostingでデプロイ先プロジェクトを分ける設定。
もちろん事前に本番用、ステージング用でプロジェクトを作っておく。

公式にあった
Firebase CLI リファレンス  |  Firebase

firebase use --add

でステージング、本番用の設定を追加しておく

firebae.jsonに書くpublicはdeploy時、-pオプションで上書きできるのでこれで分ける。

最終的にステージングへのビルド、デプロイは下記2つで完了する

yarn build-staging
firebase deploy --project your-staging-project-name -p dist-staging

本番はこんな感じ

yarn build-production
firebase deploy --project your-production-project-name -p dist-production

これを実行中の開発用Dockerコンテナでexecするためシェルスクリプト化した。

staging_deploy.sh

sudo docker exec -it \
    $(sudo docker ps -a -q --filter="name=your-container-name") \
    yarn build-staging

sudo docker exec -it \
    $(sudo docker ps -a -q --filter="name=your-container-name") \
    firebase deploy --project your-staging-project-name  -p dist-staging
date

本番用はビルドでこけたらデプロイしないように止める必要がありそう。

API用のGKEとロードバランサーは本番と同じのを使う

GCPHTTPSロードバランサーは最低2000円/月ぐらいしてしまうので、分けるのは避けたい。
またドメインを分けるとSSL証明書とかの手間も増えるのでドメインも同じにしたい。

今回はHTTPSロードバランサーの設定でアクセスしてきたパスでバックエンドを分けた。

/api → 本番用バックエンド
/api-staging → ステージングバックエンド

というように振り分けるようにして、APIではどちらのパスも同じ処理へルーティングして同じコンテナでどちらでも動くようにした。
Vue側は環境変数で切り分ける。

まだ使ったことないけどIngressを使えばもっと簡単にできそうなので調査中。

CORSの設定

許可ドメインの設定はFirebaseや、GoogleAPIキー、Cloud StorageとかDjangoとか使ってるサービスごとにやるので何気にめんどう。
chromeのdeveloper consoleのエラーを見ながら設定しておく。