You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13.5.6部署GAE时Datastore连接权限错误排查

问题:Next.js 13.5.6 App Router部署GAE标准环境时,预渲染路由访问Datastore权限被拒

我有一个使用App Router的Next.js 13.5.6项目,部署在Google App Engine(Standard)环境,采用Google Firestore in Datastore mode存储数据。本地Windows环境构建运行一切正常,但部署至GAE时触发权限错误,具体表现为/robots.txt等路由预渲染时无法访问Datastore,报错PERMISSION_DENIED: Missing or insufficient permissions。

错误日志

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 62 files to Google Cloud Storage               ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build [hash] status: FAILURE
> myproject@3.1.0 build
> next build

Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:   
https://nextjs.org/telemetry

   Creating an optimized production build ...
 ✓ Compiled successfully
   Linting and checking validity of types ...
   Collecting page data ...
   Generating static pages (0/19) ...
   Generating static pages (4/19)
   Generating static pages (9/19)
   Generating static pages (14/19)

Error occurred prerendering page "/robots.txt". Read more: https://nextjs.org/docs/messages/prerender-error
Error: 7 PERMISSION_DENIED: Missing or insufficient permissions.
    at callErrorFromStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:192:76)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
    at /workspace/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
for call at
    at ServiceClientImpl.makeUnaryRequest (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:160:32)
    at ServiceClientImpl.<anonymous> (/workspace/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19)
    at /workspace/node_modules/@google-cloud/datastore/build/src/v1/datastore_client.js:224:29
    at /workspace/node_modules/google-gax/build/src/normalCalls/timeout.js:44:16
    at repeat (/workspace/node_modules/google-gax/build/src/normalCalls/retries.js:80:25)
    at /workspace/node_modules/google-gax/build/src/normalCalls/retries.js:118:13
    at OngoingCall.call (/workspace/node_modules/google-gax/build/src/call.js:67:27)
    at NormalApiCaller.call (/workspace/node_modules/google-gax/build/src/normalCalls/normalApiCaller.js:34:19)
    at /workspace/node_modules/google-gax/build/src/createApiCall.js:84:30
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
 ✓ Generating static pages (19/19)

> Export encountered errors on following paths:
        /robots.txt/route: /robots.txt
Full build logs: https://console.cloud.google.com/cloud-build/builds;region=[region]/[hash]?project=[project-id]

相关代码与配置

package.json

{  
  "name": "myproject",  
  "version": "3.1.0",  
  "description": "...description...",  
  "private": true,  
  "author": "Ivan Melnyk",  
  "main": "next start",  
  "scripts": {  
    "dev": "next dev",  
    "build": "next build",  
    "start": "next start",  
    "lint": "next lint",  
    "deploy": "gcloud app deploy"  
  },  
  "dependencies": {  
    "@google-cloud/datastore": "^8.2.1",  
    "cookies-next": "^4.0.0",  
    "date-fns": "^2.30.0",  
    "next": "^13.5.6",  
    "react": "^18.2.0",  
    "react-dom": "^18.2.0",  
    "react-icons": "^4.11.0",  
    "server-only": "^0.0.1",  
    "sharp": "^0.32.6"  
  },  
  "devDependencies": {  
    "@types/node": "20.6.5",  
    "@types/react": "18.2.22",  
    "autoprefixer": "^10.4.16",  
    "cssnano": "^6.0.1",  
    "eslint-config-next": "^13.5.3",  
    "eslint-config-prettier": "^9.0.0",  
    "postcss": "^8.4.30",  
    "sass": "^1.68.0",  
    "tailwindcss": "^3.3.3",  
    "typescript": "5.2.2"  
  }  
}

robots.ts

import { General } from 'data'

export default async function robots() {
    const data = await General()
    return {
        rules: {
            userAgent: '*',
            allow: '/',
            disallow: '/email',
        },
        sitemap: `${data.url}/sitemap.xml`,
    }
}

data.ts(General函数)

import 'server-only'
import { Datastore } from '@google-cloud/datastore'
export async function General() {
    const datastore = new Datastore()
    const [[records]] = await datastore.runQuery(datastore.createQuery('general'))
    return records
}

原因分析

本地运行正常是因为你使用的是gcloud auth登录的个人账号,该账号拥有Datastore的访问权限;而部署时的构建过程由Cloud Build执行,它使用的是独立的服务账号(格式为[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com),这个账号默认没有Datastore的读写权限,因此在构建阶段预渲染路由时调用Datastore会触发权限错误。

注意:GAE运行时的默认服务账号([PROJECT_ID]@appspot.gserviceaccount.com)确实拥有Datastore权限,但这是在应用运行阶段生效,和构建阶段的Cloud Build服务账号不是同一个。

解决方案

方案1:给Cloud Build服务账号添加Datastore权限

  1. 打开Google Cloud控制台,进入IAM与管理员 > IAM页面
  2. 搜索Cloud Build的服务账号:格式为[你的项目编号]@cloudbuild.gserviceaccount.com
  3. 点击该账号右侧的编辑按钮,添加Cloud Datastore Viewer(仅读取,足够你的场景)或Cloud Datastore User角色
  4. 保存后重新执行部署命令

方案2:修改路由为动态生成,避免构建时预渲染

如果不想调整权限,可以修改robots.ts等路由,强制Next.js在运行时生成内容,而不是构建阶段预渲染:

import { General } from 'data'

// 强制动态生成,避免构建时预渲染
export const dynamic = 'force-dynamic'

export default async function robots() {
    const data = await General()
    return {
        rules: {
            userAgent: '*',
            allow: '/',
            disallow: '/email',
        },
        sitemap: `${data.url}/sitemap.xml`,
    }
}

这样路由会在GAE运行时处理,此时使用的是GAE的默认服务账号,拥有Datastore权限,就能正常访问数据。

内容的提问来源于stack exchange,提问作者Ivan Melnyk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:24:58