Spark 如何通过 AssumeRole 跨账户查询 non-Iceberg Glue 表?
结论
可以通过 AssumeRole 访问跨账户 Glue Catalog 中的 non-Iceberg 表,前提是 Spark 的 Hive metastore 客户端使用 RoleB 的凭证。题目中的 AssumeRoleAwsClientFactory 是 Iceberg 配置,只影响对应 Iceberg Catalog 创建的 AWS 客户端,不会自动接管普通 Hive 表的访问。
“不从 S3 读取数据”有两种含义:
- 不想手写 S3 路径:可以直接使用
SELECT * FROM database.table。 - 查询 S3 上的表,但完全不读取 S3 数据:做不到。Glue Data Catalog 保存的是元数据,Spark 执行查询时仍需访问表指向的数据文件。AWS Glue Data Catalog 说明
AssumeRole 的具体配置取决于 Glue job 实际使用的 metastore 客户端和 S3 文件系统实现,不能直接照搬 Iceberg 的配置。
为什么能列出表,却无法查询
当前配置将 idt_catalog 注册为 Iceberg 的 SparkCatalog:
spark.sql.catalog.idt_catalog = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.idt_catalog.catalog-impl = org.apache.iceberg.aws.glue.GlueCatalog
这个 Catalog 按 Iceberg 的方式加载表。普通 Hive/external 表即使出现在表名列表中,也不代表它能加载这些表。
SHOW TABLES 成功只说明枚举操作成功,后续的表加载和数据读取仍可能失败。按题目中的配置,应先检查 Catalog 实现和名称解析。仅凭 [TABLE_OR_VIEW_NOT_FOUND],还无法判断是 IAM 问题。
Iceberg 的 SparkSessionCatalog 可以将 non-Iceberg 表交给 Spark 内置 Catalog 加载。不过,这条回退路径仍需单独配置 Hive metastore 凭证,只替换类名无法解决 AssumeRole 凭证的传递问题。Iceberg Spark 配置文档
第一步:让普通表通过 Hive metastore 解析
在 Glue job 中启用:
--enable-glue-datacatalog
通过 job arguments 配置时,写法如下:
{
"--enable-glue-datacatalog": ""
}
该参数会将 Glue Data Catalog 配置为 Spark 的外部 Hive metastore,并启用 Hive 支持。Glue Spark SQL 配置文档
在启动阶段指定 Account B 的 Catalog ID:
spark.hadoop.hive.metastore.glue.catalogid=222222222222
将 222222222222 替换为 Account B 的账户 ID。catalogid 只负责选择目标 Catalog,不会执行 AssumeRole。AWS 跨账户访问示例
这些配置应在 SparkContext 和 metastore 客户端初始化之前传入。会话启动后再追加配置,可能无法改变已创建客户端的行为。
第二步:让 metastore 客户端使用 RoleB
如果必须在 Account A 中运行 job,再代入 RoleB,需要先确认运行时的 Glue Hive metastore 客户端是否支持注入凭证提供器。
AWS 开源的 Glue metastore 客户端提供了以下扩展点:
aws.catalog.credentials.provider.factory.class
这里需要配置一个实现 AWSCredentialsProviderFactory 的工厂类,由它返回访问 Glue 所用的凭证提供器。客户端实现
以下示例使用 AWS SDK for Java v1,要求运行时接口兼容,且默认凭证链能够取得 RoleA 的作业凭证:
package example;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.glue.catalog.metastore.AWSCredentialsProviderFactory;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import org.apache.hadoop.conf.Configuration;
public class RoleBCredentialsProviderFactory
implements AWSCredentialsProviderFactory {
@Override
public AWSCredentialsProvider buildAWSCredentialsProvider(
Configuration conf) {
String roleArn = conf.get("example.catalog.role.arn");
String region = conf.get("aws.region");
if (roleArn == null || region == null) {
throw new IllegalArgumentException(
"example.catalog.role.arn and aws.region are required");
}
AWSSecurityTokenService sts =
AWSSecurityTokenServiceClientBuilder.standard()
.withRegion(region)
.withCredentials(
DefaultAWSCredentialsProviderChain.getInstance())
.build();
return new STSAssumeRoleSessionCredentialsProvider.Builder(
roleArn, "glue-catalog-session")
.withStsClient(sts)
.build();
}
}
接口定义和 STS provider API 分别见 AWS Glue 客户端源码与 AWS SDK Java 文档。
将自定义类编译成与运行环境兼容的 JAR,通过 Glue 的 --extra-jars 加载,再通过 job 的 --conf 传入以下 Spark 启动配置:
spark.hadoop.hive.metastore.glue.catalogid=222222222222
spark.hadoop.aws.region=us-east-1
spark.hadoop.aws.catalog.credentials.provider.factory.class=example.RoleBCredentialsProviderFactory
spark.hadoop.example.catalog.role.arn=arn:aws:iam::222222222222:role/RoleB
example.catalog.role.arn 是示例代码自行读取的参数,不是 AWS 内置参数。Region 应替换为目标 Catalog 所在区域。
这一扩展方式来自开源客户端,不保证所有托管 Glue 版本都支持相同配置。部署前应核对作业内置客户端的接口、AWS SDK 版本和类加载方式,避免额外 JAR 覆盖运行时自带的依赖。
第三步:单独处理 S3 读取凭证
以上配置解决了 Glue 元数据访问所需的凭证问题。普通 Hive 表的数据通常由 Hadoop 文件系统连接器读取,不会使用题目中配置的 Iceberg S3FileIO。
即使 RoleB 已有 S3 权限,也需要确认实际读取文件的客户端是否使用 RoleB:
- 如果允许 RoleA 直接读取目标 S3 bucket,可以为 RoleA 配置跨账户 S3 访问。
- 如果 S3 访问也必须使用 RoleB,需要为实际使用的文件系统单独配置 AssumeRole 凭证提供器,并确保 executor 能取得和刷新凭证。
例如,Hadoop S3A 提供 AssumedRoleCredentialProvider,具体用法取决于 Hadoop 版本及其对源凭证的支持。只有作业确实使用 S3A 时,fs.s3a.* 配置才适用。不能假定 Glue 中所有 s3:// 路径都由 S3A 处理。Hadoop S3A AssumeRole 文档
在 Python 中调用一次 boto3.client("sts").assume_role(...),也不会自动改变 JVM 内部 Glue 客户端或 executor 使用的 S3 凭证。
如何验证查询
先显式指定 Spark 内置 Catalog,避免将普通表继续交给 idt_catalog:
spark.sql(
"SHOW TABLES IN spark_catalog.my_database"
).show()
spark.sql(
"DESCRIBE TABLE EXTENDED spark_catalog.my_database.my_table"
).show(truncate=False)
spark.sql(
"SELECT * FROM spark_catalog.my_database.my_table LIMIT 10"
).show()
确认表解析到了目标 Catalog 后,再使用两段式表名:
USE CATALOG spark_catalog;
SELECT * FROM my_database.my_table;
验证时,要分别检查表解析和数据读取。SHOW TABLES 成功后,如果查询出现 S3 AccessDenied,应检查文件系统凭证、bucket policy,以及使用 SSE-KMS 时的密钥权限。
如果普通表依赖额外的 Hive SerDe,还需要将对应类加入作业 classpath。启用 Glue Catalog 不会自动补齐所有 SerDe。Glue SerDe 要求
不强制 AssumeRole 时,可以简化配置
如果只需要跨账户查询,不要求请求必须以 RoleB 发出,可以直接授权 RoleA 访问 Account B 的 Glue Catalog 和数据,再配置 Hive metastore 的 catalogid。AWS 的跨账户示例也采用这种方式。AWS 跨账户访问说明
如果表受 Lake Formation 管理,还需按实际治理方式配置 Lake Formation 授权。题目未提供 Glue 版本、文件系统实现和 Lake Formation 状态,完整的 AssumeRole 参数需要结合这些信息确定,无法用一组配置覆盖所有运行环境。
备注:内容仅供参考。
