非Dataproc Hadoop集群Spark BigQuery Connector写BigQuery报元数据令牌错误
核心原因
这是spark-bigquery-connector 0.19.1版本的已知逻辑缺陷,和你当前的生产环境配置完全匹配:
- 报错的直接触发点是GCS文件系统初始化时,尝试访问GCE专属元数据服务器
169.254.169.254获取默认服务账号token,本质是你代码中传入的自定义服务账号凭据,没有被透传到写入流程的GCS文件系统初始化环节。 - 测试环境正常、生产环境报错的差异来源:0.19.1版本的连接器存在硬编码分支判断,当配置的
project参数和parentProject参数取值不一致时,会直接跳过用户通过option传入的credentials配置加载流程,切到GCS默认凭据探测逻辑。你的集群是部署在非GCP环境的普通Hadoop集群,根本没有对应的元数据服务接口,因此直接抛出连接失败的IO异常。 - 同凭据可以正常读表的原因:BigQuery读数据的代码路径不会触发上述分支判断,会正常加载你传入的凭据,因此读操作完全不受影响。
解决方案
- 首选修复方案:升级连接器版本
将当前使用的spark-bigquery-with-dependencies_2.11-0.19.1.jar升级到0.22.0及以上版本。该版本已经修复了project与parentProject取值不一致时丢失自定义凭据的缺陷,原有代码逻辑不需要做任何调整,传入的credentials参数会被正常透传到全流程,不会再触发元数据服务器的探测请求。 - 临时兜底方案(暂时无法升级版本时使用)
- 删掉代码里通过
.option("credentials", credentials)传入凭据的逻辑,改为将服务账号配置同步到集群所有节点的core-site.xml:- 开启服务账号认证:设置
google.cloud.auth.service.account.enable = true - 指定服务账号密钥路径:设置
google.cloud.auth.service.account.json.keyfile为服务账号JSON密钥文件在节点上的绝对路径,提前将对应密钥文件分发到所有节点的该路径下 - 明确GCS文件系统实现类:设置
fs.gs.impl = com.google.cloud.hadoop.fs.gcs.GoogleHadoopFileSystem
- 开启服务账号认证:设置
- 该配置方式的凭据加载优先级高于连接器内部的默认探测逻辑,不管代码走哪个分支,都会优先读取Hadoop配置里的服务账号凭据,不会再触发元数据服务器的请求。
- 删掉代码里通过
- 前置排查校验
0.19.1版本对Base64编码格式的服务账号凭据解析容错性很差,当凭据解析失败时也会触发默认凭据探测逻辑。测试环境因为project/parentProject取值相同,该问题被掩盖。可以在生产环境提交任务前,打印credentials参数的实际解析对象,确认其为合法的ServiceAccountCredentials实例,而非空对象或解析失败的占位值。
提醒:不要尝试通过网络代理、加白
169.254.169.254地址的方式解决问题,非GCE环境没有对应的元数据服务返回,只会增加无意义的排查成本。
内容的提问来源于stack exchange,提问作者thej
相关产品推荐
相关产品推荐

