我的帐户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将CTAS查询语句发送到Athena。我知道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
另一种方法是用(original source)递归更新acl
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)}'
但是这两个选项将需要向S3发出额外的GET
和PUT
请求,因此需要更多的钱来支付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查询中授予存储桶的所有者完全控制权?
刚刚发现similar post,但似乎他们使用了IAM角色,这不是我的选择
我发现:1)无法在CTAS查询中更改ACL。相反,可以使用新所有权将S3对象复制到自身上(由于John Rotenstein和Theo的注释)。
回顾一下。我从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__
在一起,因此我可以直接更改其ACL,而无需使用aws cp
复制文件成功执行CTAS查询后,在同一个python脚本中运行。
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查询,因此我已经实现了自动提交新查询并执行一些其他操作的逻辑,例如更新目标胶水表并记录。因此,包括这些代码行是很简单的。
答案 0 :(得分:0)
我建议您执行复制。
“其他GET和PUT请求”将是次要的:
或者,您从帐户B运行aws s3 cp --recursive
命令以更改所有权(是,还需要进行其他更改,例如将元数据设置为复制命令)来将文件复制到自身(是!)。 。这与您对put-object-acl
提出的建议类似。
答案 1 :(得分:0)
当前,唯一做到这一点的唯一方法是在帐户A中使用IAM角色,并使用允许帐户B承担角色的信任策略。您提到这对您的情况是不可能的,这是不幸的。当前无法通过其他方式进行操作的原因是,Athena不会使用“ bucket-owner-full-control”选项来写入文件,因此帐户A将永远不会完全拥有由帐户B中的角色发起的操作创建的任何文件
由于您在目标存储桶中被授予的策略允许所有操作,因此您可以做的一件事是在CTAS操作完成后运行一项任务,该任务列出了创建的对象,并使用以下命令将每个对象复制到自身(相同的源密钥和目标密钥) “存储桶拥有者完全控制” ACL选项。像这样复制对象是更改S3对象的存储和ACL属性的常用方法。正如您所说,这会产生额外的费用,但是与CTAS费用以及与将来查询数据有关的费用相比,这些费用微不足道。
真正的缺点是必须编写一些内容以在CTAS操作之后运行并进行协调。我建议您看一下Step Functions来做到这一点,您可以制作出很好的工作流程来自动化Athena,并且运行成本很小。我的应用程序使用Step Functions,Lambda和Athena并花费几分钱(实际上我使用IAM角色来进行跨帐户工作),或多或少地完全按照您的意图进行操作。