【问题标题】:AWS Athena: cross account write of CTAS query resultAWS Athena:跨账户写入 CTAS 查询结果
【发布时间】:2019-11-07 13:43:21
【问题描述】:

我在 帐户 A 中有大量历史数据集。此数据集为 csv 格式,并由year/month/day/hour/ 分区。我的目标是通过额外的标准化步骤和额外的分区级别将这些数据转换为镶木地板,例如year/month/day/hour/product/,并将其写回到processed/“目录”下的账户A的同一桶。所以“目录”树看起来像

S3_bucket_Account_A

dataset
|
├── raw
│   ├── year=2017
|   │   ├── month=01
|   |   │   ├── day=01
|   │   |   |   ├── hour=00
|   │   |   |   └── hour=01
|                                 
├── processed
│   ├── year=2017
|   │   ├── month=01
|   |   │   ├── day=01
|   |   |   │   ├── hour=00
|   |   │   |   |   ├── product=A
|   |   │   |   |   └── product=B
|   |   |   │   ├── hour=01
|   |   │   |   |   ├── product=A
|   |   │   |   |   └── product=B

为了做到这一点,我使用 boto3 API 向 Athena 发送 CTAS 查询语句。我知道limitations of CTAS queries,例如在同一个查询中最多可以写入 100 个分区,CTAS 查询结果的位置必须为空/唯一。因此,我当时处理一个原始分区,并考虑到这些限制,即时生成 CTAS 查询的内容。

由于我使用账户 B 来执行这些 CTAS 查询,但这些查询的结果应该写入到 账户 A 拥有的 S3 存储桶中。我已获得在账户 A 的存储桶策略级别指定的以下权限。

{
    "Effect": "Allow",
    "Principal": {
        "AWS": "__ARN_OF_ACCOUNT_B__"
    },
    "Action": [
        "s3:*"
    ],
    "Resource": [
        "arn:aws:s3:::dataset",
        "arn:aws:s3:::dataset/*"
    ]
}

问题是帐户 A(存储桶所有者)无权访问由于帐户 B 的 Athena 执行的 CTAS 查询而写入的文件。

据我了解,账户 A 可以选择为我创建一个 IAM 角色,然后我会像账户 A 一样执行此任务。但不幸的是,这个选项是不可能的。

我找到了有关如何转移 S3 对象的所有权/更改 ACL 的方法。一种方法是在账户 B 的 S3 存储桶中输出 CTAS 查询结果,然后将这些文件复制到账户 A 的存储桶中(original source)

aws s3 cp s3://source_awsexamplebucket/ s3://destination_awsexamplebucket/ --acl bucket-owner-full-control --recursive

另一种方法是递归更新 acl,例如 (original source)

aws s3 ls s3://bucket/path/ --recursive | awk '{cmd="aws s3api put-object-acl --acl bucket-owner-full-control --bucket bucket --key "$4; system(cmd)}'

但这两个选项需要额外的 GET 和 PUT 请求 S3,因此需要更多的钱来支付 AWS。但更重要的是,在 CTAS 查询成功后,我使用创建的表中的分区更新了 账户 A 的 AWS Glue 表(目标表)。这样,账户 A 中的 IAM 用户可以立即开始查询转换后的数据。这是我如何更新destination_table 的总体思路

response = glue_client.get_partitions(
    CatalogId="__ACCOUNT_B_ID__",
    DatabaseName="some_database_in_account_B",
    TableName="ctas_table"
)

for partition in response["Partitions"]:
    for key in ["DatabaseName", "TableName", "CreationTime"]:
        partition.pop(key)
        
glue_client.batch_create_partition(
    CatalogId="__ACCOUNT_A_ID__",
    DatabaseName="some_database_in_account_A",
    TableName="destination_table",
    PartitionInputList=response["Partitions"]
)

我以这种方式而不是MSCK REPAIR TABLE destination_table 这样做,因为后者由于某种原因需要很长时间。如您所见,如果我选择使用aws s3 cp,我在复制有关分区的元信息时也需要考虑到这一点

所以我真正的问题是如何在另一个帐户执行的 CTAS 查询中向存储桶的所有者授予完全控制权?

2019-06-25 更新:

刚刚找到 similar post,但似乎他们使用 IAM 角色,这不是我的选择

2019-06-27 更新

我发现:1) 在 CTAS 查询中无法更改 ACL。相反,可以使用新的所有权将 S3 对象复制到自身(感谢来自 John Rotenstein 和 Theo 的 cmets)。

2019-06-30 更新

只是回顾一下。我从account B 运行CTAS 查询,但结果保存在account A 拥有的存储桶中。这就是 CTAS 查询“标题”的样子:

CREATE TABLE some_database_in_account_B.ctas_table
WITH (
  format = 'PARQUET',
  external_location = 's3://__destination_bucket_in_Account_A__/__CTAS_prefix__/',
  partitioned_by = ARRAY['year', 'month', 'day', 'hour', 'product']
) AS (
    ...
    ...
)

由于我使用boto3 提交CTAS 查询,并且我知道__destination_bucket_in_Account_A__ 和__CTAS_prefix__,因此我可以在成功执行后直接在同一个python 脚本中更改它们的ACL,而不是使用aws cp 复制文件CTAS 查询。

s3_resource = aws_session.resource('s3')
destination_bucket = s3_resource.Bucket(name="__destination_bucket_in_Account_A__")

for obj in destination_bucket.objects.filter(Prefix="__CTAS_prefix__"):
    object_acl = s3_resource.ObjectAcl(destination_bucket.name, obj.key)
    object_acl.put(
        ACL='bucket-owner-full-control'
    )

注意,由于我需要提交超过 AWS Athena 限制的多个 CTAS 查询,我已经实现了自动提交新查询并执行一些附加操作的逻辑,例如更新目标 Glue 表和日志记录。因此,包含这些代码行非常简单。

【问题讨论】:

  • 嗯,问题本身有很多选项。但是,如果 CTAS 复制到 accountA 的存储桶的数据正在被使用 AccountA 角色的某些查询服务(如 hive 或 athena 等)持续访问,那么我可能不得不在这里寻求第一个解决方案以避免任何“访问”正在纠正对象的 acl 属性时出现 Denied 错误..

标签: amazon-web-services amazon-s3 permissions acl amazon-athena


【解决方案1】:

目前,干净利落地执行此操作的唯一方法是使用账户 A 中的 IAM 角色和允许账户 B 代入该角色的信任策略。您提到这对于您的情况是不可能的,这很不幸。目前不可能以任何其他方式进行的原因是 Athena 不会使用“bucket-owner-full-control”选项写入文件,因此账户 A 永远不会完全拥有由账户 B 中的角色发起的操作创建的任何文件.

由于您在目标存储桶中授予的策略允许一切,您可以做的一件事是在 CTAS 操作完成后运行一个任务,列出创建的对象并将每个对象复制到自身(相同的源和目标键) “bucket-owner-full-control”ACL 选项。像这样复制对象是更改 S3 对象的存储和 ACL 属性的常用方法。正如您所说,这会产生额外费用,但与 CTAS 费用以及与未来数据查询相关的费用相比,这些费用微不足道。

真正的缺点是必须编写一些东西在 CTAS 操作之后运行,并协调它。我建议看看 Step Functions 来做这件事,你可以制作非常好的工作流程来自动化 Athena,而且运行成本很低。我的应用程序或多或少完全符合您的要求,这些应用程序使用 Step Functions、Lambda 和 Athena,而且成本很低(不过,我使用 IAM 角色进行跨账户工作)。

【讨论】:

  • 我需要在 CTAS 查询之后运行一些东西,所以我已经实现了一些逻辑来编排这些任务。现在,我不是直接复制每个文件,而是直接更改其 ACL(请参阅更新后的帖子)。理想情况下,我想使用 S3 批处理作业(一项一项似乎很慢),但找不到如何使用 boto3 而不是 AWS 控制台来设置和运行它。
【解决方案2】:

我建议您执行复制。

“额外的 GET 和 PUT 请求”是次要的:

  • GET 为每 1000 个请求 0.0004 美元
  • PUT 为每 1000 个请求 0.005 美元

或者,您可以从帐户 B 运行 aws s3 cp --recursive 命令以将文件复制到自己(是的!)并更改所有权(它还需要进行另一项更改,例如将元数据设置为被接受为复制命令)。这与您使用put-object-acl 提出的建议类似。

【讨论】:

  • 是的,这个选项将是最后的手段。额外的价格(大约 15 美元)实际上并不是什么大问题。我更新了帖子以反映为什么我真的不想使用此选项。
  • 你有 3 亿个对象?
  • 不,应该是 300 万左右。不是每个对象都有一个PUT 请求吗?如果是这种情况,那么 100 万个 PUT 请求的成本为 5 美元。 => 300 万 15 美元。
  • 糟糕!我把我的美元和美分弄糊涂了。尽管如此,300 万个物体还是相当多的!你确定你需要这么多级别的分区吗?给定小时内的文件有多少个文件,有多少 MB?最好停在day 并连接文件。
  • Atm,一小时内有 125-175 个对象,总大小为 200 - 300 MB,大约有 40 个分区。我们确实考虑过停在不同的水平,例如year/month/day/product 或 year/month/day/hour,但在初步运行期间压缩效果不佳。此外,由于未来查询的性质和频率,我们决定设置 5 个分区级别以提高成本效益。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-17
  • 2021-08-13
相关资源
最近更新 更多