[ニッチ]gcloud deploy run でビルド時に変数を無理矢理渡す(.envのstaging対応)
こんにちは。株式会社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 なしでもいけます!
とても便利なのですが、その分内部は隠蔽されているので、要件が少し増えただけで一気に難しくなってしまうことがあります……
今回やりたいこととその課題
今回やりたいことは、Next.js の .env ファイルを staging と production で切り替えることです。
Next.js に関する課題
Next.js (15系で確認) では、残念ながら .env ファイルの切り替えは、 production
、development
、test
とそれぞれに対する local
のみの対応となっており、staging
などの環境に対応する方法は用意されていません……
要望自体は昔からあるようなので、単に未対応なのではなく、思想として対応しないということなんですかね。
もちろん、このような挙動は多くの人が必要としているはずなので、これを解決するサードパーティのパッケージはありました。
ただ、たったこれだけのために依存を増やさないといけないのか……?という気持ちがなくもなく……
gcloud deploy run
に関する課題
単純に、ビルド時に環境名(※)を渡して、その環境名に対応する .env.{ENV}
ファイルで .env を上書きしてからビルドすればいいのでは?と考えました。
※環境名は、env-cmd
を使用する場合と同様に、Next.js の既存の機構が動作しないようにするために、production
を避けて prod
、stag
等とします
しかし、ビルド時に変数を渡すというのはデフォルトでは隠蔽されている gcloud builds submit
側の機能で、gcloud run deploy
のオプションでは指定できなさそうです。
--set-env-vars
はコンテナの環境変数を指定するオプションであって、ビルド時に使える変数ではありません
その上、gcloud builds submit
を直接使う場合は cloudbuild.yaml
というビルド構成ファイルを書かないといけないようです。
(今まで設定レスで使えていた部分を明示しなければいけない……)
`gcloud deploy run` の内部の挙動
※image 指定ではなく、かつ Dockerfile なしの場合
-
gcloud builds submit
- ローカルにあるファイルが Google Cloud Storage にアップロードされる
- Cloud Build 上で、Buildpack でコードが解析され、ビルド方法が決定される
- Buildpack で解析された結果、
npm ci
、npm run build
等が実行される - エントリポイントに、
npm start
が指定されたコンテナイメージが作成される
-
gcloud run deploy
- 前段で作成されたイメージをデプロイする
pack
コマンドのものなので、直接は使えない部分もありますが、内部の挙動の参考にはなるはず
実現したいことに対して必要なことが一気に増えてしまいました……
デプロイ前にファイルを書き換える方法の課題
いっそ、gcloud deploy run
の実行前に .env ファイルを書き換える方法はどうかとも考えました。
ただ、それだと仮にローカルからデプロイする場合に、git 上での差分が出てしまいます。
ローカルからデプロイするケースとしてはデプロイ処理自体の動作確認があり、gcloud deploy run
の実行後に元に戻す処理を入れるというのも考えられますが、途中でエラーが起きた場合は戻すのか戻さないのか?など複雑になっていってしまいます……
万策尽きたか……?
理想的な方法がないのであれば、何かを諦めるしかないかと思いつつ、ここまでのことを見直しました。
手続き的より宣言的が好ましいという思想には共感しつつも、各サービス固有の設定ファイルは学習コストに見合うか微妙です……
それであればシンプルなコマンド実行の方がまだローカルで部分的に動作させられるし扱いやすいのでマシという方向で検討してみました。
その方針で改めて考えていった結果、以下のような方法で行くことにしました。
gcloud deploy run
の実行前に環境名をそれ専用のファイルに書き込み、ビルド時に読み取る
結論:コマンドで変更するのが既存のファイルでなければ、.gitignore に指定することで差分を無視することできます。
なので、「環境名」の情報を特定のファイルに保持させることにしました。
- [デプロイ指示環境(ローカルや GitHub Actions)]
gcloud deploy run
の実行前に、環境名をそれ専用のファイルに書き込む - [Cloud Build 上]ビルド時にそのファイルの内容を読み取り、環境名に対応する
.env.{ENV}
ファイルで .env ファイルを上書きしてからビルドを実行する
これで、ビルド時に環境名を渡し、環境名ごとに使用する .env ファイルを切り替えることができるようになりました 🎉
ただ、追加で以下の点にハマりました……
- デフォルトだと .gitignore の対象になっているファイルは、ビルド時の対象ファイルに含まれない……!
- .gcloudignore を指定する必要がある
最終的に、実現のために必要な変更点は以下のようになりました。
-
package.json
の変更-
deploy
:gcloud run deploy
の前-
echo ${ENV} > .build-env
を追加
-
-
build
:next build
の前-
ENV=$(cat .build-env)
を追加 -
mv .env.${ENV} .env
を追加
-
-
-
.gitignore
の変更-
.build-env
を追加
-
-
.gcloudignore
の追加- 変更前の
.gitignore
の内容をコピー
- 変更前の
最後に
記事としてまとめてみるとそこまで難しい話ではなかったようにも思えてきましたが、これを「本番リリース、今週中にできるの?できないの???」という状況で判断するのは結構大変でした 😅
採用情報
サーバーサイド、クライアントサイド、インフラ、はたまた プロダクトオーナー的な役割等々名目上の役割に関係なくなんでもやりたい・やるぜ!という方は、株式会社PoliPoliの話を一度聞いてみませんか?

国民と政治・行政が政策を共に創る社会を。政策共創プラットフォームを運営する株式会社PoliPoli(ポリポリ)の公式開発ブログです。公式サイト: polipoli.work
Discussion