You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 09:30:39