PAYATHON 2026

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 参数需要结合这些信息确定,无法用一组配置覆盖所有运行环境。

备注:内容仅供参考。