Spark 和 Amazon S3 未在执行程序中设置凭证

Posted

技术标签:

【中文标题】Spark 和 Amazon S3 未在执行程序中设置凭证【英文标题】:Spark and Amazon S3 not setting credentials in executors 【发布时间】:2017-06-13 09:49:21 【问题描述】:

我正在做一个从 Amazon S3 读写的 Spark 程序。我的问题是,如果我在本地模式下执行(--master local[6])它可以工作,但如果我在集群中执行(在其他机器上)我凭据出错:

org.apache.spark.SparkException: Job aborted due to stage failure: Task 2 in stage 1.0 failed 4 times, most recent failure: Lost task 2.3 in stage 1.0 (TID 33, mmdev02.stratio.com): com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain
at com.amazonaws.auth.AWSCredentialsProviderChain.getCredentials(AWSCredentialsProviderChain.java:117)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3521)
at com.amazonaws.services.s3.AmazonS3Client.headBucket(AmazonS3Client.java:1031)
at com.amazonaws.services.s3.AmazonS3Client.doesBucketExist(AmazonS3Client.java:994)
at org.apache.hadoop.fs.s3a.S3AFileSystem.initialize(S3AFileSystem.java:297)
at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:2596)
at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:91)
at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:2630)
at org.apache.hadoop.fs.FileSystem$Cache.get(FileSystem.java:2612)
at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:370)
at org.apache.hadoop.fs.Path.getFileSystem(Path.java:296)
at org.apache.parquet.hadoop.ParquetFileReader.readFooter(ParquetFileReader.java:384)
at org.apache.parquet.hadoop.ParquetRecordReader.initializeInternalReader(ParquetRecordReader.java:157)
at org.apache.parquet.hadoop.ParquetRecordReader.initialize(ParquetRecordReader.java:140)
at org.apache.spark.rdd.SqlNewHadoopRDD$$anon$1.<init>(SqlNewHadoopRDD.scala:155)
at org.apache.spark.rdd.SqlNewHadoopRDD.compute(SqlNewHadoopRDD.scala:120)
at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:300)
at org.apache.spark.rdd.RDD.iterator(RDD.scala:264)
at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:300)
at org.apache.spark.rdd.RDD.iterator(RDD.scala:264)
at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:300)
at org.apache.spark.rdd.RDD.iterator(RDD.scala:264)
at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:73)
at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:41)
at org.apache.spark.scheduler.Task.run(Task.scala:88)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:214)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

原因:com.amazonaws.AmazonClientException:无法从链中的任何提供商加载 AWS 凭证

我的代码如下:

    val conf = new SparkConf().setAppName("BackupS3")


    val sc = SparkContext.getOrCreate(conf)

sc.hadoopConfiguration.set("fs.s3a.access.key", accessKeyId)
sc.hadoopConfiguration.set("fs.s3a.secret.key", secretKey)
sc.hadoopConfiguration.set("fs.s3a.endpoint", "s3-" + region + ".amazonaws.com")
sc.hadoopConfiguration.set("com.amazonaws.services.s3.enableV4", "true")
sc.hadoopConfiguration.set("fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem")
sc.hadoopConfiguration.set("fs.s3a.buffer.dir", "/var/tmp/spark")
System.setProperty(SDKGlobalConfiguration.ENABLE_S3_SIGV4_SYSTEM_PROPERTY, "true");
System.setProperty("com.amazonaws.services.s3.enableV4", "true")

我可以写入 Amazon S3 但无法读取!我在进行 spark-submit 时还必须发送一些属性,因为我的地区是法兰克福,我必须启用 V4:

--conf spark.executor.extraJavaOptions=-Dcom.amazonaws.services.s3.enableV4=true

我也尝试过以这种方式传递凭据。如果我把它们放在每台机器的 hdfs-site.xml 中,它就可以工作。

我的问题是如何从代码中做到这一点?为什么执行者没有得到我从代码中传递给他们的配置?

我正在使用 Spark 1.5.2、hadoop-aws 2.7.1 和 aws-java-sdk 1.7.4。

谢谢

【问题讨论】:

您是否尝试过在 URI 本身中传递这些参数,如下所示:s3a://:@bucketname/folder 这似乎不起作用,我的密钥有一个'/',即使我用'%2F'替换它也找不到文件。 【参考方案1】: 不要将密钥放在密钥中,这会导致密钥丢失 如果您在 EC2 中运行,您的秘密将自动从 IAM 功能中获取;客户端向一个神奇的网络服务器询问会话秘密。 ...这意味着:可能是 spark 的自动凭证传播受到阻碍。在提交工作之前取消设置您的 AWS_ 环境变量。

【讨论】:

秘密在配置文件中作为 jar 中的资源。我没有设置环境变量,就像你在代码中看到的那样。如果我将密钥放在 hdfs-site.xml 或 spark-defaults.conf 中,它就可以工作。更奇怪的是,我可以写入 S3 并读取小文件(我猜是因为读取它的是驱动程序而不是其他机器......)【参考方案2】:

如果您在代码中明确设置这些属性,则这些值将仅对驱动程序进程可见。执行者将没有机会获取这些凭据。

如果你在实际的配置文件中设置了它们,比如core-site.xml,它们会传播。

您的代码可以在本地模式下运行,因为所有操作都在一个进程中进行。

为什么它适用于小文件而不是大文件的集群 (*):该代码也适用于未分区的文件,其中读取操作在驱动程序中执行,然后将分区广播到执行程序。在分区文件上,执行程序读取单个分区,不会在执行程序上设置凭据,因此它会失败。

最好使用标准机制来传递凭证,或者更好的是,按照 EricJ 的回答建议,在集群中使用 EC2 角色和 IAM 策略。默认情况下,如果您不提供凭证,EMRFS 将通过 EC2 实例元数据服务查找临时凭证。

(*) 我自己还在学习这个,随着我了解更多,我可能需要修改这个答案

【讨论】:

以上是关于Spark 和 Amazon S3 未在执行程序中设置凭证的主要内容,如果未能解决你的问题,请参考以下文章

Amazon S3 HTTPS 未在 Linux 上使用 Chrome 和 wget 加载

在 Amazon s3 中将 Spark RDD 编写为 Gzipped 文件

从 Apache Spark 分段上传到 Amazon S3

Amazon s3a 使用 Spark 返回 400 Bad Request

Spark Streaming 检查点到 amazon s3

使用 PySpark 从 Amazon S3 读取文本文件