本地Spark Streaming连接S3报Forbidden错误求助
我之前本地跑Spark Streaming读S3也踩过这个Forbidden的坑,终端明明能正常访问S3,偏偏Spark就报错,给你几个实际排查的方向,亲测有用:
1. 先确认Spark真的拿到了你的环境变量
终端里设置的环境变量不一定能被Spark进程继承,特别是用IDE启动或者后台运行的时候。你可以在代码里加几行打印,看看Spark能不能读到这些变量:
// Scala示例,Java/Python写法类似 println("AWS_ACCESS_KEY_ID: " + System.getenv("AWS_ACCESS_KEY_ID")) println("AWS_SECRET_ACCESS_KEY: " + System.getenv("AWS_SECRET_ACCESS_KEY")) println("AWS_SESSION_TOKEN: " + System.getenv("AWS_SESSION_TOKEN"))
要是输出是空的,说明Spark没拿到变量,启动的时候得把变量直接挂在spark-submit命令前面:
AWS_ACCESS_KEY_ID=你的密钥ID AWS_SECRET_ACCESS_KEY=你的密钥 AWS_SESSION_TOKEN=你的会话令牌 spark-submit --class 你的主类路径 你的应用jar包
2. 必须保证Hadoop-AWS版本和Spark兼容
这个是最容易踩的坑!Spark依赖的Hadoop版本和你用的hadoop-aws jar版本必须严格匹配,比如Spark 3.x一般对应Hadoop 3.x,版本不兼容的话认证逻辑直接乱掉。你可以在spark-submit里指定正确的jar包,比如Spark 3.3.0搭配Hadoop 3.3.4:
spark-submit --jars https://repo1.maven.org/maven2/org/apache/hadoop/hadoop-aws/3.3.4/hadoop-aws-3.3.4.jar,https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-bundle/1.12.262/aws-java-sdk-bundle-1.12.262.jar --class 你的主类路径 你的应用jar包
或者在Maven/Gradle的依赖里同步版本,别让依赖冲突。
3. 直接把认证参数硬怼到SparkConf里
有时候环境变量的方式就是不生效,那不如直接在代码里把参数配置到SparkConf,跳过环境变量的依赖:
val sparkConf = new SparkConf() .setAppName("S3StreamingDemo") .setMaster("local[*]") .set("spark.hadoop.fs.s3a.access.key", System.getenv("AWS_ACCESS_KEY_ID")) .set("spark.hadoop.fs.s3a.secret.key", System.getenv("AWS_SECRET_ACCESS_KEY")) .set("spark.hadoop.fs.s3a.session.token", System.getenv("AWS_SESSION_TOKEN")) .set("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem")
注意用s3a协议,这是Hadoop官方推荐的最新S3实现,旧的s3/s3n别用了。
4. 检查S3桶的权限和IAM身份
终端能访问不代表Spark用的身份有足够权限!要确保你的AWS凭证对应的IAM用户/角色有这两个关键权限:
s3:ListBucket:Spark Streaming需要扫描桶里的文件,必须有这个权限s3:GetObject:读取文件内容的权限
去IAM控制台看看该用户的权限策略,或者用终端命令再测一遍:
aws s3 ls s3://你的桶名 aws s3 cp s3://你的桶名/某个测试文件 .
要是终端能正常执行,那大概率是Spark这边的配置问题;要是终端也报错,那就是权限没给够。
5. 临时会话令牌别过期了
如果你用的是临时凭证(AWS_SESSION_TOKEN),得确认它有没有过期,或者权限范围对不对。可以用这个命令验证:
aws sts get-caller-identity
要是返回错误,说明凭证失效了,得重新获取新的临时凭证。
小技巧:本地开发的话,把凭证写到
~/.aws/credentials文件里更省心,Hadoop-AWS会自动读这个文件的配置,不用每次设环境变量:[default] aws_access_key_id = 你的密钥ID aws_secret_access_key = 你的密钥 aws_session_token = 你的会话令牌
内容的提问来源于stack exchange,提问作者covfefe

