生产环境配置

配置 S3 存储

师成师成· 更新于 2026-09-29· 阅读 19 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

配置 S3 存储

本页介绍如何将 AWS S3 以及 S3 兼容对象存储(MinIO、Apache Ozone S3 网关、Ceph RGW 等)配置为 Polaris 目录的存储后端。在 AWS S3 上,所有读写操作均通过凭证发放(credential vending)完成:Polaris 通过 STS 假定客户的 IAM 角色,并向客户端返回限定作用域的短期凭证。IAM 角色、其信任策略以及存储桶本身都必须在创建目录之前配置好。

本页仅涉及 Polaris 原生认证。Polaris 也支持外部身份提供方,但此处暂不涉及;下面的配置模式在其他方面保持不变。

涉及的 IAM 身份

S3 凭证发放流程涉及三个不同的 IAM 身份。将它们混为一谈是导致创建目录或加载表时出现 AccessDenied 错误的最常见原因。

#身份使用方用途
1Polaris 服务身份Polaris 服务器进程对 Polaris 发出的每个 sts:AssumeRole 请求进行签名
2目录访问角色Polaris(假定该角色)持有目录存储桶上实际的 S3 / KMS 权限
3发放的凭证Iceberg 客户端(Spark/Trino/PyIceberg)Polaris 在加载表时返回的短期会话密钥

在运行时,客户端调用 Polaris 加载表,Polaris 以身份 1 的身份、针对身份 2 对 sts:AssumeRole 请求进行签名,AWS STS 返回限定作用域的临时凭证(身份 3),客户端使用这些凭证直接与 S3 和 KMS 通信。

身份 1 在 Polaris 部署时配置一次。身份 2 按目录创建,其 ARN 在创建目录时注册。身份 3 在每次加载表时生成,从不持久化。

Polaris 服务身份

Polaris 服务器本身需要一个 AWS 身份,以便针对目录访问角色调用 STS。该身份在 Polaris 之外配置——通过标准的 AWS SDK 凭证链——并且与任何目录无关。

选择与部署方式相匹配的发现机制:

  • EKS / IRSA — 将 IAM 角色 ARN(eks.amazonaws.com/role-arn)以注解形式添加到 Polaris 的 ServiceAccount 上。Pod 会收到一个投影令牌,并自动用它换取 STS 凭证。
  • EC2 — 将 IAM 实例配置文件附加到 EC2 实例上。SDK 会从 IMDS 读取凭证。
  • 静态凭证 — 在 Polaris 容器的环境中设置 AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY,可选设置 AWS_SESSION_TOKEN 和 AWS_REGION。仅适用于本地开发。

任何其他参与标准 AWS SDK 凭据链的 AWS 计算环境也应该同样可用,尽管上述模式是我们已经验证过的。

无论采用哪种机制,最终的身份只需要一项权限即可与 STS 通信:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::123456789012:role/polaris-warehouse-access"
    }
  ]
}

Resource 应列出 Polaris 预期代入的每一个目录访问角色;只有当角色名称遵循由 AWS 账户所有者严格控制的命名约定时,才应使用通配符(例如 arn:aws:iam::123456789012:role/polaris-*)。

该身份的 ARN 就是目录访问角色的信任策略必须信任的 Principal——详见下一节。一个常见的错误是:只更新了目录角色的权限,却没有同时将 Polaris 服务身份加入其信任策略;其表现是即使目录角色拥有正确的 S3 权限,STS:AssumeRole 仍然返回 AccessDenied。

关于在 EC2 上实际使用该身份的端到端部署示例,请参阅 在 AWS 上部署 Polaris。

目录访问角色与信任策略

当客户端请求凭据时,Polaris 会通过 STS 代入一个由客户管理的 IAM 角色。该角色必须:

  1. 为支撑该目录的存储桶及前缀授予对象访问所需的操作(s3:GetObject、s3:PutObject、s3:DeleteObject、s3:ListBucket,以及在使用加密时相关的 kms:* 操作)。
  2. 信任 Polaris 服务主体——通常是 Polaris 服务器运行所使用的 IAM 角色。如果配置了 externalId,Polaris 会在 sts:AssumeRole 请求中附带该值,信任策略必须接受相同的外部 ID。

对于跨账户或托管式 Polaris 部署,建议使用 externalId 以缓解混淆代理人(confused deputy)问题。一个最小化的信任策略如下所示:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::123456789012:role/polaris-server" },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": { "sts:ExternalId": "polaris-prod" }
      }
    }
  ]
}

目录存储配置

创建目录时,请提供角色 ARN、区域和 externalId。下方 Authorization 头部中的令牌是从 /api/catalog/v1/oauth/tokens 获取的 Polaris 管理员承载令牌(关于如何初始化并签发管理员令牌,请参阅为生产环境配置 Polaris)。

curl -X POST https://<polaris-host>/management/v1/catalogs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "catalog": {
          "type": "INTERNAL",
          "name": "warehouse_s3",
          "properties": { "default-base-location": "s3://warehouse-bucket/prod/" },
          "storageConfigInfo": {
            "storageType": "S3",
            "roleArn": "arn:aws:iam::123456789012:role/polaris-warehouse-access",
            "externalId": "polaris-prod",
            "region": "us-east-1"
          }
        }
      }'

角色 ARN 会按照 AwsStorageConfigurationInfo 所强制的模式进行校验;格式不正确的 ARN 将在创建 catalog 时被拒绝。

使用 KMS 进行服务端加密

当存储桶使用 SSE-KMS 时,需要同时提供 currentKmsKey(Polaris 写入时应使用的密钥)和 allowedKmsKeys(该 catalog 被允许从中读取的所有密钥)。这两个字段在 AwsCredentialsStorageIntegration 中是独立处理的,因此如果你希望通过凭证分发(vended credentials)读取写入密钥,就必须把该密钥也包含在 allowedKmsKeys 中:

"storageConfigInfo": {
  "storageType": "S3",
  "roleArn": "...",
  "region": "us-east-1",
  "currentKmsKey": "arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb",
  "allowedKmsKeys": [
    "arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb",
    "arn:aws:kms:us-east-1:123456789012:key/cccc-dddd"
  ]
}

IAM 角色的策略必须包含针对 currentKmsKey 的 kms:GenerateDataKey 和 kms:Decrypt 权限,以及针对 allowedKmsKeys 中列出的每个密钥的 kms:Decrypt 权限;同时,每个密钥策略都必须向该角色主体授予相同的权限。

如果部署不使用 KMS,请将 kmsUnavailable 设置为 true,这样 Polaris 就不会请求与 KMS 相关的会话权限:

"kmsUnavailable": true

S3 兼容端点

Polaris 可以指向 S3 兼容的对象存储(MinIO、Ceph RGW、Apache Ozone S3 网关)。可用字段如下:

  • endpoint —— Polaris 及其客户端应当调用的 S3 API 端点。
  • endpointInternal —— 可选,当集群内端点与返回给客户端的端点不同时,由 Polaris 服务器使用。
  • pathStyleAccess —— 对于不支持虚拟主机风格寻址的后端,设置为 true。
  • stsEndpoint —— STS 端点;未设置时,先回退到 endpointInternal,再回退到 endpoint。
  • stsUnavailable —— 当后端未实现 STS 时,设置为 true。

本页开头所述的凭证颁发保证以后端实现 STS 为前提。对于 AWS S3 以及暴露了 STS API 的 S3 兼容后端(例如 MinIO),请保持 stsUnavailable 未设置(或设为 false),上述凭证颁发流程即可原样使用。

"storageConfigInfo": {
  "storageType": "S3",
  "endpoint": "https://s3.internal.example.com",
  "pathStyleAccess": true,
  "region": "us-east-1"
}

对于不支持 STS 的 S3 兼容后端(如 Apache Ozone 的 S3 网关,或未启用 STS 的 Ceph RGW),请设置 stsUnavailable: true。此时 Polaris 将完全跳过子作用域凭据发放,客户端必须去掉 X-Iceberg-Access-Delegation: vended-credentials 请求头,并直接向对象存储进行认证。Polaris 关于 Apache Ozone 和 Ceph 的指南展示了这种模式的完整流程。

"storageConfigInfo": {
  "storageType": "S3",
  "endpoint": "https://s3.internal.example.com",
  "pathStyleAccess": true,
  "stsUnavailable": true,
  "region": "us-east-1"
}

客户端配置

引擎通过 Iceberg REST API 进行连接,并让 Polaris 在表加载时动态签发凭据;当 STS 可用时,它们无需静态 AWS 凭据。

Spark 示例,与现有 MinIO / RustFS 指南中使用的属性名称保持一致:

bin/spark-sql \
    --packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.10.1,org.apache.iceberg:iceberg-aws-bundle:1.10.1 \
    --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
    --conf spark.sql.catalog.polaris=org.apache.iceberg.spark.SparkCatalog \
    --conf spark.sql.catalog.polaris.type=rest \
    --conf spark.sql.catalog.polaris.uri=https://<polaris-host>/api/catalog \
    --conf spark.sql.catalog.polaris.oauth2-server-uri=https://<polaris-host>/api/catalog/v1/oauth/tokens \
    --conf spark.sql.catalog.polaris.token-refresh-enabled=false \
    --conf spark.sql.catalog.polaris.warehouse=warehouse_s3 \
    --conf spark.sql.catalog.polaris.scope=PRINCIPAL_ROLE:ALL \
    --conf spark.sql.catalog.polaris.credential=<client-id>:<client-secret> \
    --conf spark.sql.catalog.polaris.header.X-Iceberg-Access-Delegation=vended-credentials

推荐配置 oauth2-server-uri:如果不配置,Iceberg REST 客户端会回退到硬编码的 /v1/oauth/tokens 路径,并输出一条弃用警告,因为这一自动回退行为计划在未来的 Iceberg 版本中移除。

对于 Trino,请使用 Iceberg 连接器配合 REST catalog。REST/OAuth2 相关属性用于与 Polaris 通信,而 Polaris 会在加载表的响应中一并下发端点、路径风格标志和区域信息以及限定范围的凭据(s3.endpoint、s3.path-style-access、client.region),因此客户端无需重复配置这些内容。不过,Trino 端仍然需要启用原生 S3 文件系统:

connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=https://<polaris-host>/api/catalog
iceberg.rest-catalog.warehouse=warehouse_s3
iceberg.rest-catalog.security=OAUTH2
iceberg.rest-catalog.oauth2.credential=<client-id>:<client-secret>
iceberg.rest-catalog.oauth2.scope=PRINCIPAL_ROLE:ALL
iceberg.rest-catalog.oauth2.server-uri=https://<polaris-host>/api/catalog/v1/oauth/tokens
iceberg.rest-catalog.vended-credentials-enabled=true
fs.native-s3.enabled=true

对于 PyIceberg,使用 rest 目录类型。相同的 Polaris 端属性(uri、warehouse、credential、scope、oauth2-server-uri)同样适用,并且代发凭证(vended credential)请求头必须作为 REST 请求头转发:

from pyiceberg.catalog.rest import RestCatalog

cat = RestCatalog(
    name="polaris",
    **{
        "uri": "https://<polaris-host>/api/catalog",
        "warehouse": "warehouse_s3",
        "credential": "<client-id>:<client-secret>",
        "scope": "PRINCIPAL_ROLE:ALL",
        "oauth2-server-uri": "https://<polaris-host>/api/catalog/v1/oauth/tokens",
        "header.X-Iceberg-Access-Delegation": "vended-credentials",
    },
)

Polaris 会在加载表时将发放的 S3 属性(s3.access-key-id、s3.secret-access-key、s3.session-token)返回给客户端,因此不应在 PyIceberg 侧配置静态凭据。

验证配置

在不向客户端提供任何长期有效的 AWS 凭据的情况下,应能成功完成端到端测试:

CREATE NAMESPACE warehouse_s3.demo;
CREATE TABLE warehouse_s3.demo.t (id BIGINT, name STRING) USING iceberg;
INSERT INTO warehouse_s3.demo.t VALUES (1, 'hello');
SELECT * FROM warehouse_s3.demo.t;

如果 INSERT 或 SELECT 返回 403 失败,最常见的原因有:

  • IAM 角色的信任策略与 Polaris 提供的 roleArn / externalId 不匹配。
  • 该角色授予了 S3 权限,但缺少针对 currentKmsKey 所需的 KMS 操作权限。
  • 存储桶策略拒绝了来自特定 VPC 端点之外的访问。

Polaris 会在 debug 级别记录担任角色的 STS 请求,这是确认当前提供了哪个身份的最快方式。

评论

登录后参与评论

正在加载评论…