使用SSL证书连接Glue PySpark与Oracle数据库的问题
问题场景
通过Spark JDBC读写Oracle数据库,使用以下代码:
empDF = spark.read \ .format("jdbc") \ .option("url", url) \ .option("driver", "oracle.jdbc.driver.OracleDriver") \ .option("ssl", True) \ .option("sslmode", "require" ) \ .option("dbtable", query) \ .option("user", "******") \ .option("password", "******") \ .load()
已将Oracle钱包和tnsnames.ora文件放置在/tmp目录,URL配置为:
url = "jdbc:oracle:thin:@apm_url?TNS_ADMIN=/tmp"
但连接失败,报错:
An error occurred while calling o104.load. IO Error: IO Error PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target, connect lapse 30 ms., Authentication lapse 0 ms.
解决步骤
确认钱包文件完整性
检查/tmp目录下的钱包文件(cwallet.sso、ewallet.p12等)是否完整未损坏,需从Oracle数据库管理员处获取正确的SSL钱包包。调整TNS_ADMIN参数传递方式
Oracle JDBC Thin驱动对TNS_ADMIN的参数位置有要求,建议改用以下两种写法之一:# 写法1:将TNS_ADMIN作为独立JDBC option传递 empDF = spark.read \ .format("jdbc") \ .option("url", "jdbc:oracle:thin:@apm_url") \ .option("driver", "oracle.jdbc.driver.OracleDriver") \ .option("TNS_ADMIN", "/tmp") \ .option("ssl", True) \ .option("sslmode", "require" ) \ .option("dbtable", query) \ .option("user", "******") \ .option("password", "******") \ .load() # 写法2:确保tnsnames.ora中存在apm_url对应的条目,直接使用别名 url = "jdbc:oracle:thin:@apm_url"将Oracle根证书导入JVM信任库
PKIX路径错误核心原因是JVM不识别Oracle的SSL证书,需完成证书导入:- 从钱包导出证书:
keytool -importkeystore -srckeystore /tmp/ewallet.p12 -srcstoretype PKCS12 -destkeystore oracle_certs.jks -deststoretype JKS - 导入到JVM默认信任库(需管理员权限,路径通常为
$JAVA_HOME/jre/lib/security/cacerts):keytool -import -alias oracle_root -file oracle_root.cer -keystore $JAVA_HOME/jre/lib/security/cacerts - 或在Spark启动时指定自定义信任库:
spark-submit --driver-java-options "-Djavax.net.ssl.trustStore=/path/to/oracle_certs.jks -Djavax.net.ssl.trustStorePassword=your_password" your_spark_job.py
- 从钱包导出证书:
检查驱动版本兼容性
确保Oracle JDBC驱动版本与数据库版本匹配,建议使用ojdbc8或ojdbc11等最新版本,旧版本对SSL配置支持可能存在缺陷。验证TNS配置正确性
检查/tmp/tnsnames.ora中的条目格式是否正确,示例:apm_url = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCPS)(HOST = your_oracle_host)(PORT = your_ssl_port)) ) (CONNECT_DATA = (SERVICE_NAME = your_service_name) ) (SECURITY = (SSL_SERVER_CERT_DN = "CN=your_cert_dn") ) )
内容的提问来源于stack exchange,提问作者Sneha Nadar

