使用承担角色从本地pyspark访问S3存储桶

时间:2018-10-22 15:05:26

标签: apache-spark amazon-s3 pyspark

背景:为了使开发人员能够在易于使用的环境中构建和测试代码,我们构建了本地Spark环境,并集成了其他工具。但是,我们也想从本地环境访问S3和Kinesis。当我们使用假定角色(根据我们的安全标准)从本地从Pyspark应用程序访问S3时,它将抛出禁止的错误。

仅供参考-我们处于访问模式下,用于访问AWS账户上的资源。 assume-role access pattern

用于测试 access-s3-from-pyspark.py 的代码:

from pyspark import SparkConf, SparkContext

conf = SparkConf().setAppName("s3a_test").setMaster("local[1]")
sc = SparkContext(conf=conf)

sc.setSystemProperty("com.amazonaws.services.s3.enableV4", "true")

hadoopConf = {}
iterator = sc._jsc.hadoopConfiguration().iterator()
while iterator.hasNext():
    prop = iterator.next()
    hadoopConf[prop.getKey()] = prop.getValue()
for item in sorted(hadoopConf.items()):
    if "fs.s3" in item[0] :
    print(item)

path="s3a://<your bucket>/test-file.txt"

## read the file for testing
lines = sc.textFile(path)

if lines.isEmpty() == False:
    lines.saveAsTextFile("test-file2.text")

属性文件 spark-s3.properties

spark.hadoop.fs.s3a.impl org.apache.hadoop.fs.s3a.S3AFileSystem
spark.hadoop.fs.s3a.endpoint s3.eu-central-1.amazonaws.com
spark.hadoop.fs.s3a.access.key <your access key >
spark.hadoop.fs.s3a.secret.key <your secret key>
spark.hadoop.fs.s3a.assumed.role.sts.endpoint sts.eu-central-1.amazonaws.com
spark.hadoop.fs.s3a.aws.credentials.provider org.apache.hadoop.fs.s3a.TemporaryAWSCredentialsProvider 
spark.hadoop.fs.s3a.aws.credentials.provider org.apache.hadoop.fs.s3a.AssumedRoleCredentialProvider
spark.hadoop.fs.s3a.aws.credentials.provider org.apache.hadoop.fs.s3a.auth.AssumedRoleCredentialProvider
spark.hadoop.fs.s3a.assumed.role.session.name testSession1
spark.haeoop.fs.s3a.assumed.role.session.duration 3600
spark.hadoop.fs.s3a.assumed.role.arn <role arn>
spark.hadoop.fs.s3.canned.acl BucketOwnerFullControl

如何运行代码:

spark-submit --properties-file spark-s3.properties \
        --jars jars/hadoop-aws-2.7.3.jar,jars/aws-java-sdk-1.7.4.jar \
        access-s3-from-pyspark.pyenter code here

上面的代码返回以下错误,请注意,我能够使用承担角色配置文件或api通过CLI和boto3访问S3。

com.amazonaws.services.s3.model.AmazonS3Exception: Status Code: 403, AWS Service: Amazon S3, AWS Request ID: 66FB4D6351898F33, AWS Error Code: null, AWS Error Message: Forbidden, S3 Extended Request ID: J8lZ4qTZ25+a8/R3ZeBTrW5TDHzo98A9iUshbe0/7VcHmiaSXZ5u6fa0TvA3E7ZYvhqXj40tf74=
    at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:798)
    at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:421)
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:232)
    at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3528)
    at com.amazonaws.services.s3.AmazonS3Client.getObjectMetadata(AmazonS3Client.java:976)
    at com.amazonaws.services.s3.AmazonS3Client.getObjectMetadata(AmazonS3Client.java:956)
    at org.apache.hadoop.fs.s3a.S3AFileSystem.getFileStatus(S3AFileSystem.java:892)
    at org.apache.hadoop.fs.s3a.S3AFileSystem.getFileStatus(S3AFileSystem.java:77)
    at org.apache.hadoop.fs.Globber.getFileStatus(Globber.java:57)
    at org.apache.hadoop.fs.Globber.glob(Globber.java:252)
    at org.apache.hadoop.fs.FileSystem.globStatus(FileSystem.java:1676)
    at org.apache.hadoop.mapred.FileInputFormat.singleThreadedListStatus(FileInputFormat.java:259)
    at org.apache.hadoop.mapred.FileInputFormat.listStatus(FileInputFormat.java:229)
    at org.apache.hadoop.mapred.FileInputFormat.getSplits(FileInputFormat.java:315)
    at org.apache.spark.rdd.HadoopRDD.getPartitions(HadoopRDD.scala:200)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:253)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:251)
    at scala.Option.getOrElse(Option.scala:121)
    at org.apache.spark.rdd.RDD.partitions(RDD.scala:251)
    at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:253)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:251)
    at scala.Option.getOrElse(Option.scala:121)
    at org.apache.spark.rdd.RDD.partitions(RDD.scala:251)
    at org.apache.spark.api.java.JavaRDDLike$class.partitions(JavaRDDLike.scala:61)
    at org.apache.spark.api.java.AbstractJavaRDDLike.partitions(JavaRDDLike.scala:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
    at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
    at py4j.Gateway.invoke(Gateway.java:282)
    at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
    at py4j.commands.CallCommand.execute(CallCommand.java:79)
    at py4j.GatewayConnection.run(GatewayConnection.java:238)
    at java.lang.Thread.run(Thread.java:748)

问题:

这是正确的做法吗?

是否还有其他简便的方法可以在本地使用AWS资源进行开发和测试(我还研究了localstack程序包,该程序在大多数情况下都可以工作,但仍不完全可靠)

我为此使用正确的罐子吗?

1 个答案:

答案 0 :(得分:0)

spark.hadoop.fs.s3a.aws.credentials.provider的配置错误。

  • 应该只有一个条目,并且应该在一个条目中列出所有AWS凭证提供者
  • S3A假定角色提供程序(需要完全登录并要求假定角色)仅在最新的Hadoop版本(3.1+)上使用,而不是在2.7.x上,并且可能无法满足您的要求。它主要用于动态创建具有受限权限的登录并验证S3A连接器本身can cope with things

最好让您的组织严格遵守安全性,这只会使生活变得有些复杂。

假设您可以获取帐户ID,会话令牌和会话密钥(以某种方式),

然后对于Hadoop 2.8+,您可以使用此选项来填写spark默认值

spark.hadoop.fs.s3a.access.key AAAIKIAAA spark.hadoop.fs.s3a.secret.key ABCD spark.hadoop.fs.s3a.fs.s3a.session.token REALLYREALLYLONGVALUE spark.hadoop.fs.s3a.aws.credentials.provider org.apache.hadoop.fs.s3a.TemporaryAWSCredentialsProvider

您需要创建这些假定的角色会话秘密,直到会话持续一整天,过去它们的生命是<= 60分钟,过去一直是PITA;假定角色最多不能使用12个小时-您的IT团队将需要延长您要长期使用的任何角色的寿命。

hadoop 2.7.x版本没有该TemporaryAWSCredentialsProvider,因此您必须

  1. 依靠env var凭据提供程序,该提供程序查找AWS_环境变量。默认情况下启用此功能,因此您完全不需要使用spark.hadoop.fs.s3a.aws.credentials.provider
  2. 将所有三个环境变量(AWS_ACCESS_KEYAWS_SECRET_KEYAWS_SESSION_TOKEN(?)设置为您从假定角色API调用中获得的值。
  3. 然后运行您的工作。恐怕您可能需要在所有地方设置这些环境变量。