Scenario
我创建一个名为“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),指定“my-role”作为实例配置文件,一切正常。我通过运行以下命令来验证该实例是否有效地承担了“我的角色”:
-
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/
我从中正确地获得了包含“my-bucket”下第一个子目录的列表。(启动此 AMI 时默认安装并配置 AWS CLI。EC2 实例和 S3 存储桶位于同一 AWS 账户内)
我在这样的实例上运行/安装 Tomcat7 服务器和容器,在其上部署 J2EE 1.7 servlet 没有任何问题。
此类 servlet 应从 S3 存储桶(特别是从s3://my-bucket/custom-path/file.tar.gz
using Hadoop Java API. (请注意,我尝试了 hadoop-common 工件 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
, and mySessionToken
是我之前用实际值设置的 Java 变量。
然后,我使用以下命令有效地获取了一个 FileSystem 实例:
FileSystem fs = path.getFileSystem(conf);
我能够检索与文件系统 (fs.getconf().get(key-name)) 相关的所有配置并验证所有内容均按假设配置。
Problem
我无法下载s3://my-bucket/custom-path/file.tar.gz
using:
...
fs.copyToLocalFile(false, new Path(path.toString()+"custom-path/file.tar.gz"), outputLocalPath);
...
如果我使用 hadoop-common 2.5.x 我得到IOException
:
org.apache.hadoop.security.AccessControlException:权限被拒绝:
s3n://my-bucket/custom-path/file.tar.gz 位于
org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:449)
在
org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:427)
在
org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.handleException(Jets3tNativeFileSystemStore.java:411)
在
org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrieveMetadata(Jets3tNativeFileSystemStore.java:181)
在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) 处
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
在
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
在 java.lang.reflect.Method.invoke(Method.java:606) 处
org.apache.hadoop.io.retry.RetryInvocalHandler.invokeMethod(RetryInvocalHandler.java:187)
在
org.apache.hadoop.io.retry.RetryInvocalHandler.invoke(RetryInvocalHandler.java:102)
在 org.apache.hadoop.fs.s3native.$Proxy12.retrieveMetadata(未知
来源)于
org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:467)
在 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337) 处
org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289) 在
org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968)
在
org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937)
...
如果我使用 hadoop-common 2.4.x,我会得到一个NullPointerException
:
java.lang.NullPointerException
在 org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:433)
在 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337)
在 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289)
在 org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968)
在 org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937)
...
仅供记录,如果不设置任何 aws 凭证,我会得到:
必须将 AWS 访问密钥 ID 和秘密访问密钥指定为
s3n URL 的用户名或密码(分别),或者通过设置
fs.s3n.awsAccessKeyId 或 fs.s3n.awsSecretAccessKey 属性
(分别)。
最后的笔记
- 如果我尝试使用实例中的 AWS CLI 命令从完全相同的 URI(但使用 s3 代替 s3n)下载文件,则完全没有问题。
- 如果我尝试下载 Hadoop 发行版(例如 2.4.1)https://archive.apache.org/dist/hadoop/core/hadoop-2.4.1/ https://archive.apache.org/dist/hadoop/core/hadoop-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:
致命内部错误 java.lang.NullPointerException
在org.apache.hadoop.fs.s3native.NativeS3FileSystem.listStatus(NativeS3FileSystem.java:479)
在 org.apache.hadoop.fs.shell.PathData.getDirectoryContents(PathData.java:268)
在 org.apache.hadoop.fs.shell.Command.recursePath(Command.java:347)
在 org.apache.hadoop.fs.shell.Ls.processPathArgument(Ls.java:96)
在org.apache.hadoop.fs.shell.Command.processArgument(Command.java:260)
在 org.apache.hadoop.fs.shell.Command.processArguments(Command.java:244)
在 org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:190)
在 org.apache.hadoop.fs.shell.Command.run(Command.java:154)
在 org.apache.hadoop.fs.FsShell.run(FsShell.java:255)
在 org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
在 org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84)
在 org.apache.hadoop.fs.FsShell.main(FsShell.java:308)
很抱歉这篇文章很长,我只是尽力尽可能详细。感谢您在这里提供的任何最终帮助。