Ruby:使用AWS实例配置文件进行S3访问

问题描述

我有一个附加了配置文件的ec2实例。我可以使用awscli,它可以上传到存储桶中。

root@ocr-sa-test:/# aws s3 ls s3://company-ocr-east/
                           PRE 7_day_expiry/

root@ocr-sa-test:/# touch foo
root@ocr-sa-test:/# aws s3 cp foo s3://company-ocr-east/foo
upload: ./foo to s3://company-ocr-east/foo
root@ocr-sa-test:/# aws s3 rm s3://company-ocr-east/foo
delete: s3://company-ocr-east/foo

但是我无法将其与ruby中的aws-sdk一起使用。我无法访问。

irb(main):001:0> require "aws-sdk"
=> true
irb(main):002:0>
irb(main):003:0> credentials = Aws::InstanceProfileCredentials.new
irb(main):004:1* client = Aws::S3::Client.new(
irb(main):005:1*   region: "us-east-1",irb(main):006:1*   credentials: credentials,irb(main):007:0> )
irb(main):008:0>
irb(main):009:0>
irb(main):010:0>
irb(main):011:1* begin
irb(main):012:2*   client.put_object(
irb(main):013:2*     key: 'hello.txt',irb(main):014:2*     body: 'Hello World!',irb(main):015:2*     bucket: 'company-ocr-east',irb(main):016:2*     content_type: 'text/plain'
irb(main):017:1*   )
irb(main):018:1* rescue Exception => e
irb(main):019:1*   puts "S3 Upload Error: #{e.class} : Message: #{e.message}"
irb(main):020:0> end
S3 Upload Error: Aws::S3::Errors::AccessDenied : Message: Access Denied

解决方法

这些命令并不完全等效,因此确定线路上的确切差异将是有益的。特别是,SDK 被指示使用特定区域并从 IMDS 获取 STS 令牌,而 CLI 则根据其自己的默认值或配置文件配置来解决问题。除此之外,它们的行为也不完全相同。

要找出实际发生的情况,意味着使用适用的调试标志重新运行两者,即:

aws --debug s3 cp hello.txt s3://bucketname/hello.txt

credentials = Aws::InstanceProfileCredentials.new(http_debug_output: $stdout)
client = Aws::S3::Client.new(region: 'us-east-1',credentials: credentials,http_wire_trace: true)
client.put_object(key: 'hello.txt',body: 'Hello World!',bucket: 'bucketname',content_type: 'text/plain')

这些将产生大量输出,但它们都是相关的,而且至关重要的是,一旦您忽略了噪音,它们就具有可比性。首先要验证的是,CLI 肯定在与 IMDS 对话(它会向 http://169.254.169.254 发出请求,这些请求以“找到来自 IAM 角色的凭据”之类的内容告终。如果不是,则实例未配置如何你想过,日志中会有一些线索来解释它是如何获得凭据的,例如意外的配置文件或环境变量。你还需要检查它们是否获得了相同的角色。

要比较的第二件事是他们都尝试的 PUT 的后续序列。在调试的这一点上,几乎所有其他内容都相同,因此您很可能可以调整 Ruby SDK 客户端的设置以匹配 CLI 成功的任何设置。

第三种可能性是系统防火墙,或某种进程级强制访问控制、用户权限、cgroups/容器等。然而,调试您的操作系统内核和配置将是一个深而黑暗的兔子洞,并且在任何情况下,您都说过这是“一个 EC2 实例”,因此它大概是一个普通的旧 EC2 实例。如果实际上上面的 Ruby 命令在不同的用户 ID 下运行,或者在容器内运行,那么您可能已经有了答案,由于用户/容器/安全控制或需要的类似操作系统级配置,这很可能是网络问题修复。

强制性警告:如果您选择发布任何日志数据,请小心覆盖所有凭据!我不相信这些调试跟踪是特别可重播的,但如果我错了,你不想找出困难的方法。

,

访问被拒绝错误可能是由 Aws::InstanceProfileCredentials 中“非常激进”的默认超时造成的。

尝试使用更长的超时时间或额外的重试来初始化它:

credentials = Aws::InstanceProfileCredentials.new({
retries: 2,# Integer,default: 1
http_open_timeout: 2.5,# Float,default: 1
http_read_timeout: 2.5      # Float,default: 1
}) 

文档没有说明超时选项是以秒还是其他持续时间给出的。考虑到默认值,2.5 似乎很保守。可能需要进一步调整。


v3 Ruby API 的 AWS 文档讨论了 Aws::S3::Client docs 中的主动超时,您可以看到配置 Aws::InstanceProfileCredentials 的选项。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...