【发布时间】: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