场景
我创建了一个名为“my-role”的 AWS IAM 角色,将 EC2 指定为可信实体,即使用信任关系策略文档:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
该角色具有以下策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetBucketPolicy",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetLifecycleConfiguration",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectTorrent",
"s3:GetObjectVersion",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTorrent",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListBucketVersions",
"s3:ListMultipartUploadParts",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectVersionAcl",
"s3:RestoreObject"
],
"Resource": [
"arn:aws:s3:::my-bucket/*"
]
}
]
}
我使用 AWS CLI 从命令行启动一个 EC2 实例 (Amazon Linux 2014.09.1),将“我的角色”指定为实例配置文件,一切正常。我通过运行验证该实例是否有效地承担了“我的角色”:
-
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
查询实例元数据,我从中得到响应my-role
; -
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/my-role
我从中获得与“我的角色”关联的临时凭证。
此类凭据检索响应的示例如下:
{
"Code" : "Success",
"LastUpdated" : "2015-01-19T10:37:35Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "an-access-key-id",
"SecretAccessKey" : "a-secret-access-key",
"Token" : "a-token",
"Expiration" : "2015-01-19T16:47:09Z"
}
-
aws s3 ls s3://my-bucket/
从中我正确地得到了一个包含“我的桶”下的第一个子目录的列表。 (启动此 AMI 时默认安装和配置 AWS CLI。EC2 实例和 S3 存储桶在同一个 AWS 账户中)
我在这样的实例上运行/安装了一个 Tomcat7 服务器和容器,我在上面部署了一个 J2EE 1.7 servlet,没有任何问题。
这样的 servlet 应该从 S3 存储桶下载一个文件到本地文件系统,特别是从 s3://my-bucket/custom-path/file.tar.gz
使用 Hadoop Java API。 (请注意,我尝试了 hadoop-common artifact 2.4.x、2.5.x、2.6.x,但没有任何积极的结果。我将在使用 2.5.x 时出现的异常下面发布)
在 servlet 中,我从上面提到的实例元数据 URL 中检索新的凭据,并使用它们来配置我的 Hadoop Java API 实例:
...
Path path = new Path("s3n://my-bucket/");
Configuration conf = new Configuration();
conf.set("fs.defaultFS", path.toString());
conf.set("fs.s3n.awsAccessKeyId", myAwsAccessKeyId);
conf.set("fs.s3n.awsSecretAccessKey", myAwsSecretAccessKey);
conf.set("fs.s3n.awsSessionToken", mySessionToken);
...
显然,myAwsAccessKeyId
, myAwsSecretAccessKey
, 和 mySessionToken
是我之前用实际值设置的 Java 变量。
然后,我有效地获得了一个 FileSystem 实例,使用:
FileSystem fs = path.getFileSystem(conf);
我能够检索与文件系统 (fs.getconf().get(key-name)) 相关的所有配置,并验证所有配置均按假设进行。
问题
我无法下载 s3://my-bucket/custom-path/file.tar.gz
使用:
...
fs.copyToLocalFile(false, new Path(path.toString()+"custom-path/file.tar.gz"), outputLocalPath);
...
如果我使用 hadoop-common 2.5.x,我会得到 IOException
:
org.apache.hadoop.security.AccessControlException: Permission denied: s3n://my-bucket/custom-path/file.tar.gz at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:449) at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:427) at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.handleException(Jets3tNativeFileSystemStore.java:411) at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrieveMetadata(Jets3tNativeFileSystemStore.java:181) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:187) at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102) at org.apache.hadoop.fs.s3native.$Proxy12.retrieveMetadata(Unknown Source) at org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:467) at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337) at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289) at org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968) at org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937) ...
如果我使用 hadoop-common 2.4.x,我会得到一个 NullPointerException
:
java.lang.NullPointerException at org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:433) at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337) at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289) at org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968) at org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937) ...
仅作记录,如果不设置任何 aws 凭证,我会得到:
AWS Access Key ID and Secret Access Key must be specified as the username or password (respectively) of a s3n URL, or by setting the fs.s3n.awsAccessKeyId or fs.s3n.awsSecretAccessKey properties (respectively).
最后的笔记
- 如果我尝试使用来自实例的 AWS CLI 命令从完全相同的 URI(但 s3 代替 s3n)下载文件,我完全没有问题。
- 如果我尝试下载 Hadoop 发行版(例如来自 https://archive.apache.org/dist/hadoop/core/hadoop-2.4.1/ 的 2.4.1),将其解压缩,从实例元数据 URL 检索临时 AWS 凭证并尝试运行
<hadoop-dir>/bin/hadoop fs -cp s3n://<aws-access-key-id>:<aws-secret-access-key>@my-bucket/custom-path/file.tar.gz .
我又一次得到了 NPE:
Fatal internal error java.lang.NullPointerException at org.apache.hadoop.fs.s3native.NativeS3FileSystem.listStatus(NativeS3FileSystem.java:479) at org.apache.hadoop.fs.shell.PathData.getDirectoryContents(PathData.java:268) at org.apache.hadoop.fs.shell.Command.recursePath(Command.java:347) at org.apache.hadoop.fs.shell.Ls.processPathArgument(Ls.java:96) at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:260) at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:244) at org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:190) at org.apache.hadoop.fs.shell.Command.run(Command.java:154) at org.apache.hadoop.fs.FsShell.run(FsShell.java:255) at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70) at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84) at org.apache.hadoop.fs.FsShell.main(FsShell.java:308)
抱歉这篇文章太长了,我只是尽量写得详细一点。感谢您在这里提供的任何最终帮助。
最佳答案
您正在使用 STS/临时 AWS 凭证;这些目前似乎不受 hadoop 中的 s3
或 s3n
FileSystem
实现的支持。
AWS STS/临时凭证不仅包括(访问 key 、 secret key ),还包括 session token 。 hadoop s3
和 s3n
FileSystem
(s) 尚不支持包含 session token (即您的 fs.s3n 配置.awsSessionToken
不受 s3n
FileSystem
支持和忽略。
来自 AmazonS3 - Hadoop Wiki ...
(注意没有提到fs.s3.awsSessionToken
):
Configuring to use s3/ s3n filesystems
Edit your
core-site.xml
file to include your S3 keys<property> <name>fs.s3.awsAccessKeyId</name> <value>ID</value> </property> <property> <name>fs.s3.awsSecretAccessKey</name> <value>SECRET</value> </property>
如果你看一下S3Credentials.java从 github.com 上的 apache/hadoop,您会注意到 session token 的概念在 S3 凭证的表示中完全缺失。
提交了一个补丁来解决这个限制(详细here);但是,它尚未集成。
如果您使用的是 AWS IAM 实例角色,您可能想要探索使用 Hadoop 2.6.0 中添加的新
s3a
FileSystem
。它声称支持基于 IAM 角色的身份验证(即您根本不必明确指定 key )。
Hadoop JIRA 票证描述了如何配置 s3a
FileSystem
:
来自 https://issues.apache.org/jira/browse/HADOOP-10400 :
fs.s3a.access.key
- Your AWS access key ID (omit for role authentication)
fs.s3a.secret.key
- Your AWS secret key (omit for role authentication)
关于java - org.apache.hadoop.security.AccessControlException : Permission denied when trying to access S3 bucket through s3n URI using Hadoop Java APIs on EC2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28024570/