🦺

[ニッチ]gcloud deploy run でビルド時に変数を無理矢理渡す(.envのstaging対応)

2025/02/26に公開

こんにちは。株式会社PoliPoliでエンジニアをしている kitoo です。

今回は、Next.js を Google Cloud Run にデプロイする際に、.env ファイルの staging 対応のためにビルド時に変数を渡そうとして苦労した話を共有します。

結論を見たい方は:
gcloud deploy run の実行前に環境名をそれ専用のファイルに書き込み、ビルド時に読み取る

前提は以下になります。

  • React、Next.js を初めて使う
    • 今までは Angular メイン
  • 利用クラウドは Google Cloud
    • Vercel は使わない
    • コンテナ実行環境としては Google Cloud Run を使う
  • 人数が少ないので学習コストを下げたい
    • できるだけ設定(config)レス
    • できるだけツールを追加しない、あるいは意識しないといけないツールを少なくしたい

gcloud deploy run の概要説明

まず、gcloud deploy run の説明を簡単にしておきます。

これは、デプロイ対象のアプリケーションコードやデプロイ先のプロジェクトなどの基本的な情報を指定するだけで、ビルド〜コンテナ実行環境へのデプロイまでを一括で行ってくれる便利なコマンドです。
Dockerfile なしでもいけます!


https://cloud.google.com/run/docs/deploying-source-code?hl=ja


https://qiita.com/sakas1231/items/423cd7efcce77f5e426a

とても便利なのですが、その分内部は隠蔽されているので、要件が少し増えただけで一気に難しくなってしまうことがあります……

今回やりたいこととその課題

今回やりたいことは、Next.js の .env ファイルを staging と production で切り替えることです。

Next.js に関する課題

Next.js (15系で確認) では、残念ながら .env ファイルの切り替えは、 productiondevelopmenttest とそれぞれに対する local のみの対応となっており、staging などの環境に対応する方法は用意されていません……


https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#environment-variable-load-order

要望自体は昔からあるようなので、単に未対応なのではなく、思想として対応しないということなんですかね。
https://github.com/vercel/next.js/issues/12772

もちろん、このような挙動は多くの人が必要としているはずなので、これを解決するサードパーティのパッケージはありました。

https://www.npmjs.com/package/env-cmd


https://zenn.dev/yuji/articles/cb4f2562cf70ae

ただ、たったこれだけのために依存を増やさないといけないのか……?という気持ちがなくもなく……

gcloud deploy run に関する課題

単純に、ビルド時に環境名(※)を渡して、その環境名に対応する .env.{ENV} ファイルで .env を上書きしてからビルドすればいいのでは?と考えました。
※環境名は、env-cmd を使用する場合と同様に、Next.js の既存の機構が動作しないようにするために、production を避けて prodstag 等とします

しかし、ビルド時に変数を渡すというのはデフォルトでは隠蔽されている gcloud builds submit 側の機能で、gcloud run deploy のオプションでは指定できなさそうです。


https://cloud.google.com/sdk/gcloud/reference/run/deploy
--set-env-vars はコンテナの環境変数を指定するオプションであって、ビルド時に使える変数ではありません

その上、gcloud builds submit を直接使う場合は cloudbuild.yaml というビルド構成ファイルを書かないといけないようです。
(今まで設定レスで使えていた部分を明示しなければいけない……)


https://cloud.google.com/build/docs/overview?hl=ja

`gcloud deploy run` の内部の挙動

※image 指定ではなく、かつ Dockerfile なしの場合

  • gcloud builds submit
    • ローカルにあるファイルが Google Cloud Storage にアップロードされる
    • Cloud Build 上で、Buildpack でコードが解析され、ビルド方法が決定される
    • Buildpack で解析された結果、npm cinpm run build 等が実行される
    • エントリポイントに、npm start が指定されたコンテナイメージが作成される
  • gcloud run deploy
    • 前段で作成されたイメージをデプロイする


https://cloud.google.com/docs/buildpacks/nodejs?hl=ja
※このドキュメント自体は pack コマンドのものなので、直接は使えない部分もありますが、内部の挙動の参考にはなるはず

実現したいことに対して必要なことが一気に増えてしまいました……

デプロイ前にファイルを書き換える方法の課題

いっそ、gcloud deploy run の実行前に .env ファイルを書き換える方法はどうかとも考えました。

ただ、それだと仮にローカルからデプロイする場合に、git 上での差分が出てしまいます。
ローカルからデプロイするケースとしてはデプロイ処理自体の動作確認があり、gcloud deploy run の実行後に元に戻す処理を入れるというのも考えられますが、途中でエラーが起きた場合は戻すのか戻さないのか?など複雑になっていってしまいます……

万策尽きたか……?

理想的な方法がないのであれば、何かを諦めるしかないかと思いつつ、ここまでのことを見直しました。

手続き的より宣言的が好ましいという思想には共感しつつも、各サービス固有の設定ファイルは学習コストに見合うか微妙です……
それであればシンプルなコマンド実行の方がまだローカルで部分的に動作させられるし扱いやすいのでマシという方向で検討してみました。

その方針で改めて考えていった結果、以下のような方法で行くことにしました。

結論:gcloud deploy run の実行前に環境名をそれ専用のファイルに書き込み、ビルド時に読み取る

コマンドで変更するのが既存のファイルでなければ、.gitignore に指定することで差分を無視することできます。
なので、「環境名」の情報を特定のファイルに保持させることにしました。

  1. [デプロイ指示環境(ローカルや GitHub Actions)]gcloud deploy run の実行前に、環境名をそれ専用のファイルに書き込む
  2. [Cloud Build 上]ビルド時にそのファイルの内容を読み取り、環境名に対応する .env.{ENV} ファイルで .env ファイルを上書きしてからビルドを実行する

これで、ビルド時に環境名を渡し、環境名ごとに使用する .env ファイルを切り替えることができるようになりました 🎉

ただ、追加で以下の点にハマりました……

  • デフォルトだと .gitignore の対象になっているファイルは、ビルド時の対象ファイルに含まれない……!
    • .gcloudignore を指定する必要がある


https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore

最終的に、実現のために必要な変更点は以下のようになりました。

  • package.json の変更
    • deploygcloud run deploy の前
      • echo ${ENV} > .build-env を追加
    • buildnext build の前
      • ENV=$(cat .build-env) を追加
      • mv .env.${ENV} .env を追加
  • .gitignore の変更
    • .build-env を追加
  • .gcloudignore の追加
    • 変更前の .gitignore の内容をコピー

最後に

記事としてまとめてみるとそこまで難しい話ではなかったようにも思えてきましたが、これを「本番リリース、今週中にできるの?できないの???」という状況で判断するのは結構大変でした 😅


採用情報

サーバーサイド、クライアントサイド、インフラ、はたまた プロダクトオーナー的な役割等々名目上の役割に関係なくなんでもやりたい・やるぜ!という方は、株式会社PoliPoliの話を一度聞いてみませんか?

https://polipoli.notion.site/PoliPoli-1a62d94ad8ea80c4a903eb2f9e83ccee

https://recruit.jobcan.jp/polipoli-recruit/job_offers/2160352

株式会社PoliPoli

Discussion