Spark读取LocalStack 2.x本地S3失败,Boto3可正常调用
Spark无法访问LocalStack 2.x的S3存储桶(Boto3正常)
问题背景
我在编写Spark作业的端到端测试,用LocalStack启动本地S3服务,同时给Boto3和Spark Session配置了LocalStack端点。LocalStack 1.4.0及以下版本一切正常,但升级到2.x(测试到2.1.0)后,Boto3能正常操作存储桶,但Spark始终提示存储桶不存在。测试了Spark 3.2.1到3.4.0多个版本,问题一致。切换回1.4.0则可正常读取CSV并写入Parquet。
相关配置与代码
依赖安装
pip install localstack==2.1.0 boto3==1.28.0 pyspark==3.4.0
LocalStack启动命令
localstack start -d
测试代码
import boto3 from pyspark.sql import SparkSession # 初始化Boto3客户端 s3_client = boto3.client( 's3', endpoint_url='http://localhost:4566', aws_access_key_id='test', aws_secret_access_key='test', region_name='us-east-1' ) # 创建存储桶并上传测试文件 s3_client.create_bucket(Bucket='test-bucket') with open('test.csv', 'w') as f: f.write('id,name\n1,Alice\n2,Bob') s3_client.upload_file('test.csv', 'test-bucket', 'input/test.csv') # 初始化Spark Session spark = SparkSession.builder \ .appName("LocalStackS3Test") \ .config("spark.hadoop.fs.s3a.endpoint", "http://localhost:4566") \ .config("spark.hadoop.fs.s3a.access.key", "test") \ .config("spark.hadoop.fs.s3a.secret.key", "test") \ .config("spark.hadoop.fs.s3a.path.style.access", "true") \ .getOrCreate() # 尝试读取S3上的CSV(此处报错) df = spark.read.csv("s3a://test-bucket/input/test.csv", header=True) df.show() # 写入Parquet df.write.parquet("s3a://test-bucket/output/test.parquet") spark.stop()
报错信息
com.amazonaws.services.s3.model.AmazonS3Exception: The specified bucket does not exist (Service: Amazon S3; Status Code: 404; Error Code: NoSuchBucket; Request ID: ...; S3 Extended Request ID: ...)
解决办法
1. 强制LocalStack使用路径样式访问
LocalStack 2.x默认启用了虚拟主机样式访问,而Spark的S3A客户端对该模式兼容性不佳。启动LocalStack时添加环境变量强制切换为路径样式:
LOCALSTACK_S3_FORCE_PATH_STYLE=1 localstack start -d
如果用Docker Compose启动,在配置中添加环境变量:
environment: - LOCALSTACK_S3_FORCE_PATH_STYLE=1
2. 补全Spark的Hadoop S3配置参数
Spark 3.x对应的Hadoop版本需要额外配置才能适配LocalStack 2.x,给Spark Session补充以下配置:
spark = SparkSession.builder \ .appName("LocalStackS3Test") \ .config("spark.hadoop.fs.s3a.endpoint", "http://localhost:4566") \ .config("spark.hadoop.fs.s3a.access.key", "test") \ .config("spark.hadoop.fs.s3a.secret.key", "test") \ .config("spark.hadoop.fs.s3a.path.style.access", "true") \ .config("spark.hadoop.fs.s3a.connection.ssl.enabled", "false") # 本地服务无需SSL .config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem") # 明确指定S3A实现类 .config("spark.hadoop.fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider") # 强制使用简单凭证提供者 .getOrCreate()
3. 对齐Hadoop AWS依赖版本
Spark自带的Hadoop AWS依赖可能与LocalStack 2.x不兼容,启动Spark时指定匹配的依赖版本(例如Spark 3.4.0对应Hadoop 3.3.4):
spark-submit --packages org.apache.hadoop:hadoop-aws:3.3.4 your_script.py
也可以在代码中直接配置:
spark = SparkSession.builder \ .appName("LocalStackS3Test") \ .config("spark.jars.packages", "org.apache.hadoop:hadoop-aws:3.3.4") \ # 其他配置... .getOrCreate()
4. 检查存储桶名称合规性
LocalStack 2.x对存储桶名称校验更严格,需符合S3官方规范:仅包含小写字母、数字、连字符,无下划线或特殊字符,长度在3-63位之间。
验证步骤
先通过Boto3确认存储桶存在:
print(s3_client.list_buckets())
若能看到目标桶,再运行Spark代码即可正常读写。
内容的提问来源于stack exchange,提问作者botchniaque
相关产品推荐
相关产品推荐

