GCP帳號充值 GCP 數據庫權限不足導致無法寫入修復
第一章:先把問題定義清楚
「權限不足導致無法寫入修復」聽起來像一句泛泛的錯誤描述,但在 GCP 的世界裡,這句話背後通常藏著幾個關鍵差異:你到底在寫哪個資源、由誰來寫、透過什麼路徑寫、以及資料寫入時還牽涉到哪些附帶服務。只要有一個環節對不上,就會從表面上的“無法寫入”變成真正的“查不到原因、怎麼加權限都不對”。
因此第一步永遠不是急著加權限,而是把問題框進可判斷的範圍。你需要記下:錯誤訊息原文(包含 service name、permission、resource name)、發生在什麼服務或介面(例如 Cloud Run、GKE、App Engine、本地服務、CI/CD),以及寫入動作是什麼(INSERT/UPDATE/寫文件/寫入表資料/寫入快取)。如果你能取得錯誤報文中帶出的 permission(例如 cloudsql.instances.update、datastore.entities.create、bigquery.tables.updateData、firestore.databases.documents.create 之類),修復會快很多。
接著,確認目標資料庫類型。GCP 的資料庫很多:Cloud SQL(MySQL/PostgreSQL/SQL Server)、Firestore、Cloud Datastore、BigQuery、Spanner、甚至是 Cloud Storage 也常被誤當成“數據庫”。不同產品的權限模型不同,錯誤碼也不同。你如果把 Firestore 的建議套到 BigQuery,最後只會得到一串“加了權限仍不通”的挫折感。
第二章:常見錯誤的幾種真相
在排查過程中,我最常見到的狀況其實很固定。它們看似不同,根因卻重複出現:
2.1 寫入帳號不是你以為的那個
很多團隊以為自己是用某個服務帳號(service account)在跑,例如 Cloud Run 的 default service account 或你在程式裡指定的憑證。但實際上,Cloud Run/ GKE/ Workload Identity 的繫結方式可能讓程式用到“另一個身份”。於是你在 IAM 上對 A 帳號加了資料庫寫入權限,結果程式其實用的是 B 帳號,當然還是寫不進去。
最可靠的做法是:回頭看日誌(Cloud Logging)中針對錯誤請求那一筆,是否能看到“Principal / serviceAccount / actor”。若沒有,就要查應用的憑證載入方式、服務的 service account 設定、Workload Identity 交換規則、以及 CI/CD 部署後是否改過服務帳號。
2.2 權限加了,但作用範圍不對
即使你加對了服務帳號,也常遇到“作用範圍不對”的問題。IAM 綁定的是資源層級(project、dataset、table、firestore database、cloud sql instance 等)。你給了某個層級的權限,但寫入目標在另一個層級或不同資源上。
典型例子:你給了 BigQuery dataset 的 roles/bigquery.dataEditor,卻寫入的是另一個 dataset;或你給 Firestore 的整個 project 權限,但你的程式其實連到不同的 database ID(例如 default vs 非 default)。
2.3 需要的不是“寫入”,而是“寫入 + 附帶服務”
資料庫寫入有時會牽涉加密、暫存、索引維護或事件觸發。若你在 Cloud SQL 啟用了 CMEK(Customer-Managed Encryption Key),那麼除了資料庫寫入權限,你還需要對 KMS key 有使用權。當 KMS 權限不足時,錯誤有時不會直接說“KMS 權限不足”,而是表面呈現成寫入失敗或某種 generic 的 permission error。
同樣地,Firestore 有時牽涉到事件匯出(Export)、或觸發 Cloud Functions/Workflows。你以為你只是在寫文件,結果程式寫完後還需要另一個服務去讀或寫,這就會把權限問題“延伸”到另一個身份。
第三章:用錯誤訊息做路標,而不是用猜的
GCP 的權限錯誤通常都不會完全沒線索。關鍵是你要把訊息拆開來讀。即便你不是 IAM 專家,也能從錯誤中找到至少三個元素:
- 缺少的 permission 或角色(role)片段
- 目標 resource(在哪個 instance、哪個 dataset、哪個 table、哪個 collection/database)
- 執行的主體(service account / user / identity pool)
有了這三個元素,你就能判斷:問題是“身份不對”、還是“資源不對”、或是“角色不完整”。
3.1 先查日誌:找到正確的一筆 request
日誌不是用來“看心情”的。你要鎖定同一次請求、同一次錯誤、同一個錯誤層級。建議你從以下來源開始:應用的 stderr/stdout、服務的 request log、以及 GCP 的 error log。當錯誤發生在 Cloud Run 或 GKE,log 裡通常能看見 request-id 或 trace-id。你拿到 trace-id 後,能更快在相關服務中找到同一條鏈路。
很多團隊的誤區是“只看摘要”。摘要可能只告訴你“permission denied”,但不告訴你缺哪個 permission、不告訴你是哪個資源。你要找帶有完整 error details 的那一段。
3.2 把缺少的 permission 映射回 IAM 角色
當你看到具體 permission(例如 spanner.databaseUser 不是 permission 但角色名,或直接顯示 method permission),你可以用它來縮小候選角色。這一步的重點不是“完全記住所有角色”,而是把角色範圍判斷出來:它到底是資料層(data.*)、還是管理層(instances.*、datasets.*)、或是加密層(cloudkms.*)。
舉例來說:如果你是 BigQuery 寫入資料,一般不會只靠 roles/bigquery.jobUser;你需要能真正修改資料的權限(例如 bigquery.tables.updateData 或對應角色)。如果你只加了 jobUser,程式可以發起查詢或 load job,但在寫入 stage 失敗時仍會報權限不足。
第四章:分情境修復策略(Cloud SQL / Firestore / BigQuery 為主)
下面我用“你通常遇到什麼狀況、要怎麼驗證身份、需要補什麼權限”來講。你不必照抄每個角色名,但要理解它們對應的能力層級。
4.1 Cloud SQL:連上了但 INSERT 失敗
Cloud SQL 的權限分兩層:GCP 層的 IAM 與資料庫本身的 DB 權限(user/role)。你說“權限不足”,通常會發生在以下兩種之一:
- 應用的服務帳號沒有權限連到 instance(IAM 或網路策略)
- 服務帳號連到後,用來登入資料庫的 DB 帳號沒有 INSERT/UPDATE 權限
修復順序我建議這樣走:
- 先確認 Cloud SQL instance 的 IAM:檢查目標 instance 上是否授予正確服務帳號所需角色(常見如
roles/cloudsql.client)。 - 確認你是否使用了正確的連線方式(Unix socket/SSL/TCP)以及 Cloud SQL Auth Proxy 是否有使用對應憑證。
- 登入資料庫後檢查 DB 角色:例如 MySQL 的 GRANT、PostgreSQL 的權限與 schema 設定。
很多“加 IAM 仍不行”的案例,實際上是 DB 層沒有權限。反之亦然:DB 帳號有 INSERT 但沒有 cloudsql.instances.connect 之類能力,則連線都不會成功,更不可能寫入。
還有一種容易忽略:如果你用的是 CMEK 或某些加密/備份功能,需要對 KMS key 或其他相關資源授權。此時錯誤往往和你預期的 “INSERT” 不完全一致,但本質仍是權限不足。
4.2 Firestore / Datastore:寫文件被拒絕
Firestore 常見的情境是:程式能讀,但不能寫;或者寫某個 collection 失敗。這裡要注意三個控制點:
- IAM 角色(project 層級或 database 層級)
- Firestore 規則(如果使用 Security Rules,它會在資料層做拒絕)
- 身份(service account vs user 身份 vs token)
如果你是後端服務(Cloud Run/GKE)透過 Admin SDK 或直接服務端寫入,通常不會走 client side rules 的邏輯,但仍可能因為 IAM 缺少 firestore.databases.documents.create 或相關角色而被拒絕。
GCP帳號充值 修復步驟:
- 先確認寫入請求使用的身份是誰。若你是用 Admin SDK,確認你用的憑證是否是預期的 service account。
- 檢查該 identity 是否具備 documents create/update 权限。常見的做法是授予對應角色(例如
roles/datastore.user或 Firestore 等價角色),但要以錯誤訊息的 permission 為準。 - 若你也有使用 Firestore Security Rules(例如前端直連),則還要檢查 rules 是否允許該身份在目標 path 上的寫入。
4.3 BigQuery:能發 job 但 load 或 insert 失敗
GCP帳號充值 BigQuery 的權限常見“看起來差不多,實際差很多”。你可能已經能跑查詢 job(因為 jobUser 權限足夠),但寫入(load、insert、updateData)缺乏資料層權限,導致 job 在執行 stage 報 permission denied。
這類問題我通常建議你鎖定三件事:
- 你是要把資料寫到哪個 dataset/table?
- load job 的資料來源(例如 GCS)是否也需要讀或寫權限?
- 你的主體是否只有 jobUser,但缺少對 table 的 updateData 或對 dataset 的 dataEditor 類權限?
修復步驟:
- 確認呼叫端的 service account。
- 在 dataset 層或 table 層授予對應資料寫入權限(以錯誤訊息顯示的 permission 為準)。
- 若 load/append 依賴 GCS,確保 service account 同時具備對儲存桶的讀權限(例如 Storage Object Viewer)或對應 KMS 使用權限。
另外,很多團隊會遇到“寫入失敗但錯誤訊息指向的是 tables.updateData 或 jobs.create”。這並不矛盾:要麼你沒權跑 job,要麼你跑了 job 但 job 裡的寫入動作不允許。把兩者分清楚,你就知道要補哪一段。
GCP帳號充值 4.4 服務帳號沒掛上去:Workload Identity 與憑證交換
GCP帳號充值 當你用 GKE Workload Identity 或其他身份聯邦(例如 Workload Identity Pool),最常發生的是“身份看似正確,但實際 Principal 不對”。常見原因包括:attribute mapping 不匹配、Kubernetes service account 和 GCP service account 的綁定未生效、或部署到另一個 namespace 後沒有設定。
這種情況下,最好的排查方式通常是回到“錯誤訊息中的 principal”。你會在拒絕訊息看到主體名稱。你只要把主體名稱和你以為的 service account 對上,就能很快判斷是“綁定沒生效”還是“權限沒授予”。
第五章:制定一份可落地的修復清單
很多文章給的是理論,我們要的是能在今天把問題解掉的清單。下面這份清單你可以當作 SOP,用於未來任何“無法寫入”的權限錯誤。
5.1 收集資訊(先不改任何設定)
- 錯誤原文(包含 permission、resource、status code)
- 寫入流程所在服務(Cloud Run/GKE/函數/本地程式/批次 job)
- 呼叫端身份(service account / user / principal)
- 目標資源(instance、database、dataset、table、collection path)
5.2 驗證身份(確認“到底誰在寫”)
- 查看服務的 service account 設定(Cloud Run、GKE、App Engine)
- 若使用 Workload Identity,核對 Kubernetes SA ↔ GCP SA 的綁定與 namespace
- 在日誌中比對 principal 名稱是否和你要授權的帳號一致
5.3 最小權限授予(先補“缺的那一段”)
- 根據缺少 permission 對應資料層/管理層權限
- 授權範圍要對齊資源層級(project/dataset/table 或 instance)
- 避免“一次性給 admin”,除非只是短期排錯且有回收計畫
5.4 檢查附帶權限(KMS、GCS、觸發器)
- 若有 CMEK,檢查 KMS key 的 roles(通常需要能使用加密/解密的權限)
- GCP帳號充值 BigQuery load/導入常需要 GCS 讀取權限
- 寫入後若觸發其他服務,需補上“鏈路延伸”的身份權限
5.5 重新測試與回收
- 重新部署或重新觸發同一條寫入流程
- 確認錯誤消失後,把過度授權的臨時權限移除
- 把修復原因寫進運維筆記:為什麼會缺、缺在哪個 resource、誰的身份
GCP帳號充值 第六章:避免重演的工程做法
權限問題之所以反覆出現,不是因為人不細心,而是因為系統缺少“可預期性”。你可以用工程化方式降低不確定性。
6.1 讓憑證可觀測:把 principal 記錄在應用日誌
很多服務在寫入失敗時只記錄了錯誤堆疊,但沒有記錄執行身份。你可以在應用啟動時把服務帳號或憑證來源標出(例如從環境變數或身份資訊取得)。等之後再遇到權限不足,你不需要猜“是不是用錯帳號”,而是直接看到。
注意:不要在日誌中輸出敏感憑證內容;記錄身份名稱即可。
6.2 基礎設施即程式:讓 IAM 變更能被審查
若你的 IAM 設定散落在手動操作,未來一定會“有人加過但忘了加在哪”。把 IAM 設定納入版本管理(例如用 IaC),讓變更可以被 code review,並且能追溯“為什麼今天這個 service 寫不進去”。
GCP帳號充值 特別是多環境(dev/stage/prod)時,最容易出錯的是:你在 prod 加了權限,結果服務跑在 stage。
6.3 預先做權限壓測:用測試身份驗證寫入路徑
把“寫入測試”當作單元/整合測試的一部分。每次部署後,使用同一個身份去寫一筆測試資料,確保 permission 與規則都通過。這比等到正式業務流程跑起來才發現 permission denied 要省很多時間。
第七章:把修復講成結論,讓你下次更快
當你再次遇到「GCP 數據庫權限不足導致無法寫入」時,可以用一句話當作心法:
先確認“誰在寫”與“寫到哪裡”,再對照錯誤訊息中缺的 permission,在正確資源層級補上最小權限,同時檢查 KMS/GCS 等附帶依賴。
很多人把時間浪費在“加權限—仍然失敗—再加更多權限”。這不會收斂,因為你沒有把缺的那個環節釘住。你只要拿到錯誤訊息中的 principal 與 resource,就能把範圍縮到幾個可能性,修復自然會越來越快。
7.1 一個典型修復範例(概念版)
假設你的 Cloud Run 寫入 Cloud SQL 時收到 permission denied。你做了三步:
- 在日誌裡看到拒絕主體是某個 service account A,而你以為是另一個 service account B。
- 你在 Cloud SQL instance 上針對 A 補了 client 連線權限。
- 接著登入資料庫確認 DB user 沒有 INSERT 權限,於是補上 schema/table 的 GRANT。
最後寫入恢復正常。你看起來改了兩處地方,但本質是同一件事:身份與權限在兩個層級都要對齊。
結語:真正的修復是理解權限模型
權限不足的錯誤很常見,但它不應該讓你一直停在猜測。GCP 的權限模型核心其實就幾件事:身份(principal)、資源(resource)、能力(permission/role)、以及附帶依賴(KMS、GCS、規則與觸發)。你每次都用這個框架去拆解,就算換了資料庫產品或換了部署方式,排查效率也會越來越高。
下一次你再看到“無法寫入”,不要先想“要給哪些 admin 權限”。先想:錯誤訊息告訴我誰、寫在哪裡、缺哪種能力?把這三個答案找到,你的修復就會變成可驗證的行動,而不是運氣。

