はじめに
ローカルでは問題なく動いていたNext.jsアプリを、Google CloudのCloud Runにデプロイしたところ、作成したアプリ内で実装していたOpenAIのAPI呼び出しだけが 401 Incorrect API key provided になってしまうという問題に遭遇しました。
最終的には
--set-build-env-varsと--set-env-varsを両方に指定する
というシンプルな解決策で動きましたが、ここに至るまでの試行錯誤がなかなか大変だったので、備忘録としてまとめます。
デプロイするアプリについて
今回 Cloud Run にデプロイしたアプリは、アプリ上でサービスの名前を入れて、競合をChatGPTを用いて探すというシンプルなものです。
また、構成は以下の通りです:
.env.local
.gitignore
.next/ (ビルド成果物:ローカル専用)
next-env.d.ts
next.config.js
node_modules/ (ローカル専用)
package-lock.json
package.json
postcss.config.js
README.md
src/ (アプリ本体)
tailwind.config.js
tsconfig.json
前提条件
- まずローカルにて
npm run devで動作確認済み - ローカルにて、本番ビルド確認として
npm run build && npm startで動作確認済み -
.env.localにはOPENAI_API_KEY=sk-proj-...を設定
Cloud Runとは?
Cloud Run は、Google Cloud が提供する サーバーレスなコンテナ実行環境 です。
ソースコードをアップロードするだけで、アプリをコンテナ化して自動的に公開できます。
主な特徴
- サーバー管理不要:インフラ構築や設定なしでデプロイ可能
- 自動スケーリング:アクセス量に応じて自動で起動・停止
- どんな言語でも対応:Dockerコンテナ化できるものであれば動作可能
- HTTPリクエスト駆動:リクエストが来たときだけ起動する
動作の流れ
- ソースコードを Cloud Run にデプロイ
- 内部でビルド → コンテナ化
- コンテナが起動して公開URLが発行される
Cloud Run へのデプロイ手順
ローカルで動作確認したアプリを、下記の手順でCloud Run にデプロイしました。
① ソースを準備
以下のファイルを残し、node_modules/ と .next/ は削除してZIP化します。
.env.local
package.json
package-lock.json
next.config.js
tsconfig.json
tailwind.config.js
src/ などアプリ本体
② Cloud Shell でアップロード&解凍
Cloud Shell の上部にある「⋮」から ファイルをアップロード を選び、 Mac で動いたソースを ZIP にして丸ごとアップロードします
unzip myapp.zip
cd myapp
③ Cloud Run にデプロイ
gcloud run deploy my-next-app \
--source . \
--region=asia-northeast1 \
--allow-unauthenticated
④ 環境変数を設定
gcloud run services update my-next-app \
--region=asia-northeast1 \
--update-env-vars VAR1=value1,VAR2=value2
数分でビルドが終わり、発行されたURL(https://xxx.a.run.app)にアクセスするとアプリが表示されます。
最初に遭遇したエラー
Cloud Run にアップロードしてビルドは通ったものの、API を叩くと以下のエラーに。
401 Incorrect API key provided: sk-proj-***********************************PBXj. You can find your API key at https://platform.openai.com/account/api-keys.
ローカルでは動くのに Cloud Run では失敗する
- ローカル:
.env.localにOPENAI_API_KEY=sk-proj-...→ OK - Cloud Run:
OPENAI_API_KEYを環境変数に設定 → 401エラー
という状況でした。
試してダメだったこと
- Cloud Run の「環境変数」に
OPENAI_API_KEYを設定 - デプロイ時に
--set-env-vars OPENAI_API_KEY=...を指定 -
.env.localをそのままアップロード
どれも、ビルドは成功するのに実行時に 401 になりました。
なぜ失敗していたのか
- Cloud Run は ビルド環境(Cloud Build) → 実行環境(Cloud Run) の2段階構成
-
.env.localはローカル開発専用で、Cloud Build では無視される -
--set-env-varsは 実行時 用で、ビルド時には適用されない - Cloud Runは環境変数を ビルド時に焼き込む ので、ビルド時に空だと、そのまま空のキーが固定される
つまり
ビルド時に APIキー が空 → 空のままビルドされる → 実行時にキーがあっても無視される
ということのようでした。
解決した方法
ビルド時(Cloud Build)と実行時(Cloud Run)の両方に同じ API キーを指定することで解決しました。
gcloud run deploy insightlab-cloud \
--source . \
--region=asia-northeast1 \
--allow-unauthenticated \
--set-build-env-vars OPENAI_API_KEY=sk-proj-******LAIkA \
--set-env-vars OPENAI_API_KEY=sk-proj-******LAIkA
--set-build-env-vars: Cloud Build(ビルド時)に渡す
--set-env-vars: Cloud Run(実行時)に渡す
デプロイ後、APIを保護するためにSecret Managerに変数を移動
上記のままだと、APIキーがCloud Runの環境変数にそのまま格納されています。
よりセキュリティを担保するために、デプロイ後にSecret Managerに環境変数を追加し、Cloud Runはそこに格納されているAPIキーを参照するように修正しました。
【現状】
- Cloud Run > デプロイしたサービス > リビジョン > コンテナ > 環境変数を確認すると、APIキーがそのまま格納されている
手順①
- Secret Managerを選択し、「シークレットを作成」から新しいシークレットを作成し、APIキーを格納する
手順②
- Cloud Run > デプロイしたサービス > 「新しいリビジョンの編集とデプロイ」を選択
- コンテナ > 変数とシークレット > 環境変数として公開されるシークレットから、SecretManagerで追加したシークレットを指定する
※この時、「名前」はソース内で参照される名前にする
まとめ
- sk-proj-... のような OpenAI の Project API Key を使うときは、ビルド時と実行時の両方に設定が必要
- ローカルで動くのに Cloud Run で 401 になるときは、環境変数の食い違いを疑う
- .env.local はアップロードしても無視される
- セキュリティのため最終的には Secret Manager を使うと安心