【Cloud Functions】デプロイできない時に私が試したこと【開発備忘録】
状況
Cloud Functionsがなかなかうまくデプロイできず困り果てていました。
私がぶち当たったエラーメッセージは
Could not build the function due to a missing permission on the build service account.
Precondition failed.
の2種類でした。
特に後者のエラーに関しては抽象的すぎて何がダメなのか全くわからない状況でした。
まずはログを確認
デプロイを試みると、関数を記述するindex.js等と同じ階層のディレクトリにfirebase-debug.logという名前のログファイルが生成・更新されます。
その中身を見るとPrecondition failedのPreconditionが何を指しているのかも判ります。
a missing permission on the build service accountの詳細も判明することがあります。
しかし、ログを自力で読むのは大変です。一定の知識がないと何が何を意味しているのかさっぱり解りません。
まして、エラーが出る度にデプロイを試行しているとログがどんどん溜まっていき、最新のデプロイのログがどこから始まるのか探すのも一苦労です。
そこで私は、VSCodeの拡張機能のGitHub Copilotにログファイルを投げました。
すると帰ってきた返答が以下の通りです(要約)。
- サービスアカウントに必要なロールが不足している
- Cloud Storageバケットが未作成 or 権限不足
1.については、[project number]@cloudbuild.gserviceaccount.comまたは自身で作成したサービスアカウントに「Cloud Functions 開発者」「サービスアカウントユーザー」「ストレージ管理者」「Artifact Registry 管理者」のロールを付与すべきだとのことでした。
2.については、私がCloud Storageを利用していたために発生したエラーです。Firebaseコンソールからバケットを作成して、組織ポリシーでバケット関係が制限されていないか確認せよ、ということでした。
指示通り両方試したのですが、それでもmissing permissionかPreconditionかのどちらかのエラーは依然として出てしまい、ログの内容も変化がありませんでした。
最終的な解決方法
どうしても解決できないので、同じエラーにぶち当たっている人がいないかインターネットで検索してみたところ、海外の方が似た内容のことを質問していました。
その質問に対する回答に基づき、GCPコンソールのCloud Shell上で以下のコマンドを打つことで解決しました。
$ gcloud projects add-iam-policy-binding [project-id] --member="serviceAccount:[project-number]-compute@developer.gserviceaccount.com" --role="roles/logging.logWriter" --condition="None"
$ gcloud projects add-iam-policy-binding [project-id] --member="serviceAccount:[project-number]-compute@developer.gserviceaccount.com" --role="roles/cloudbuild.builds.builder" --condition="None"
$ gcloud projects add-iam-policy-binding [project-id] --member="serviceAccount:[project-number]-compute@developer.gserviceaccount.com" --role="roles/storage.objectViewer" --condition="None"
どうやらエラーの根本的な原因となっていたサービスアカウントとは、
[project-number]-compute@developer.gserviceaccount.com
だったようです。
index.jsのinitializeAppで定義したサービスアカウントやデフォルトのサービスアカウントとは無関係に、上記のアカウントの設定が最重要とは。。。自力では絶対に気づけませんね。
注意喚起
ここまで、私がCloud Functionsのデプロイに関するトラブルシューティングの過程と解決策を紹介してきましたが、これはあくまでも一例です!
開発環境やソースコードの中身によって解決方法は異なる場合が往々にしてあります。
それだけ忘れないようにお願いしたいと思います。