我们正在尝试使用我们端配置的SSO钱包和Apache Spark连接到作为AmazonRDS运行的远程Oracle数据库。我们能够使用spark-shell
实用程序加载数据,如下所述
启动 Spark shell,将 jdbc 和 oraclepki jar 添加到类路径中
spark-shell --driver-class-path /path/to/ojdbc8.jar:/path/to/oraclepki.jar
这是使用的 JDBC 网址:
val JDBCURL="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(HOST=www.example.aws.server.com)(PORT=1527))(CONNECT_DATA=(SID=XXX))(SECURITY = (SSL_SERVER_CERT_DN ="C=US,ST=xxx,L=ZZZ,O=Amazon.com,OU=RDS,CN=www.xxx.aws.zzz.com")))"
下面是用于加载数据的 Spark jdbc 调用
spark.read.format("jdbc").option("url",JDBCURL)
.option("user","USER")
.option("oracle.net.tns_admin","/path/to/tnsnames.ora")
.option("oracle.net.wallet_location","(SOURCE=(METHOD=file)(METHOD_DATA=(DIRECTORY=/path/to/ssl_wallet/)))")
.option("password", "password")
.option("javax.net.ssl.trustStore","/path/to/cwallet.sso")
.option("javax.net.ssl.trustStoreType","SSO")
.option("dbtable",QUERY)
.option("driver", "oracle.jdbc.driver.OracleDriver").load
但是当我们尝试使用spark-submit
命令运行它时,我们收到以下错误:
Exception in thread "main" java.sql.SQLRecoverableException: IO Error: The Network Adapter could not establish the connection
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:774)
at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:688)
...
...
...
Caused by: oracle.net.ns.NetException: The Network Adapter could not establish the connection
at oracle.net.nt.ConnStrategy.execute(ConnStrategy.java:523)
at oracle.net.resolver.AddrResolution.resolveAndExecute(AddrResolution.java:521)
at oracle.net.ns.NSProtocol.establishConnection(NSProtocol.java:660)
at oracle.net.ns.NSProtocol.connect(NSProtocol.java:286)
at oracle.jdbc.driver.T4CConnection.connect(T4CConnection.java:1438)
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:518)
... 28 more
Caused by: oracle.net.ns.NetException: Unable to initialize ssl context.
at oracle.net.nt.CustomSSLSocketFactory.getSSLSocketEngine(CustomSSLSocketFactory.java:597)
at oracle.net.nt.TcpsNTAdapter.connect(TcpsNTAdapter.java:143)
at oracle.net.nt.ConnOption.connect(ConnOption.java:161)
at oracle.net.nt.ConnStrategy.execute(ConnStrategy.java:470)
... 33 more
Caused by: oracle.net.ns.NetException: Unable to initialize the key store.
at oracle.net.nt.CustomSSLSocketFactory.getKeyManagerArray(CustomSSLSocketFactory.java:642)
at oracle.net.nt.CustomSSLSocketFactory.getSSLSocketEngine(CustomSSLSocketFactory.java:580)
... 36 more
Caused by: java.security.KeyStoreException: SSO not found
at java.security.KeyStore.getInstance(KeyStore.java:851)
at oracle.net.nt.CustomSSLSocketFactory.getKeyManagerArray(CustomSSLSocketFactory.java:628)
... 37 more
Caused by: java.security.NoSuchAlgorithmException: SSO KeyStore not available
at sun.security.jca.GetInstance.getInstance(GetInstance.java:159)
at java.security.Security.getImpl(Security.java:695)
at java.security.KeyStore.getInstance(KeyStore.java:848)
我对火花很陌生,可能在这里做错了什么。这就是我尝试配置配置的方式
val conf = new SparkConf().setAppName(JOB_NAME)
conf.set("javax.net.ssl.trustStore", "/path/to/cwallet.sso");
conf.set("javax.net.ssl.trustStoreType", "SSO")
conf.set("oracle.net.tns_admin", "/path/to/tnsnames.ora")
conf.set("oracle.net.wallet_location", "(SOURCE=(METHOD=file)(METHOD_DATA=(DIRECTORY=/path/to/ssl_wallet/dir/)))")
conf.set("user", "user")
conf.set("password", "pass")
下面是使用的spark-submit
命令
spark-submit --class fully.qualified.path.to.main
--jars /path/to/ojdbc8.jar,/path/to/oraclepki.jar,/path/to/osdt_cert.jar,/path/to/osdt_core.jar
--deploy-mode client --files /path/to/hive-site.xml --master yarn
--driver-memory 12G
--conf "spark.executor.extraJavaOptions=-Djavax.net.ssl.trustStore=/path/to/cwallet.sso -Djavax.net.ssl.trustStoreType=SSO"
--executor-cores 4 --executor-memory 12G
--num-executors 20 /path/to/application.jar /path/to/application_custom_config.conf
还尝试添加
--conf 'spark.executor.extraJavaOptions=-Djavax.net.ssl.trustStore=/path/to/cwallet.sso -Djavax.net.ssl.trustStoreType=SSO'
和
--files /path/to/cwallet.sso,/path/to/tnsnames.ora
到spark-submit
命令,但没有任何运气。 我到底在这里做错了什么?还尝试了本文中提到的解决方案,但出现相同的错误。我需要确保trustStore should be accessible on each executor node
吗?如果是这种情况,那么为什么spark-shell
命令工作正常?这是否意味着 spark-cli 不包含任何工作节点来执行命令?
请指教
更新:
看起来您使用的是 12.1.0.2 中的 JDBC 驱动程序。请升级到 18.3,您可以从 oracle.com/technetwork/database/application-development/jdbc/下载...为了更容易使用钱包,进行了一些更改。- @Jean·德·拉瓦雷内
在遵循@Jean建议的更改后,de Lavarene摆脱了最初的错误,但以下是我现在得到的
org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 4 times, most recent failure: Lost task 0.3 in stage 0.0 (TID 3, example.server.net, executor 2): java.sql.SQLException: PKI classes not found. To use 'connect /' functionality, oraclepki.jar must be in the classpath: java.lang.NoClassDefFoundError: oracle/security/pki/OracleWallet
at oracle.jdbc.driver.PhysicalConnection.getSecretStoreCredentials(PhysicalConnection.java:3058)
at oracle.jdbc.driver.PhysicalConnection.parseUrl(PhysicalConnection.java:2823)
当我在 Spark 本地模式下运行它时:--master local[*]
它工作正常,但在yarn
模式下失败。
我已经在使用带有逗号分隔的jar列表的--jars
命令。我发现的是:
1)--jars
期望路径是本地路径,然后将它们复制到 HDFS 路径
2) 在开头使用file:///
不起作用
3) 如果我不指定--jars
参数,程序将要求缺少 JDBC 驱动程序类。一旦我使用 --jars 指定ojdbc8.jar
,错误就会消失并开始给出oraclepki.jar
未找到错误。我不知道为什么会发生这种情况。
4)还尝试使用:
作为分隔符,同时指定多个罐子,但没有任何运气
更新 2
我能够oraclepki.jar
通过使用
--driver-class-path /path/to/oraclepki.jar:/path/to/osdt_cert.jar:/path/to/others.jar
但是一旦我们进入--master yarn
模式,就会显示以下异常
Caused by: oracle.net.ns.NetException: Unable to initialize the key store.
at oracle.net.nt.CustomSSLSocketFactory.getKeyManagerArray(CustomSSLSocketFactory.java:617)
at oracle.net.nt.CustomSSLSocketFactory.createSSLContext(CustomSSLSocketFactory.java:322)
... 32 more
Caused by: java.io.FileNotFoundException: /path/to/cwallet.sso (No such file or directory)
据我了解,当它从工作节点启动作业时,cwallet.sso
文件路径在这些节点上不可用。我们尝试为钱包指定HDFS路径,但该实用程序希望在创建钱包时提供本地路径。
那么我们需要手动将钱包文件复制到所有工作节点吗?或者有没有更好的选择来实现这一目标?
请指教
这就是我们能够解决它的方式。这里要记住的一件重要事情是,SSO
文件必须存在于将运行 Spark 的所有节点上(Spark 的执行器节点)
val SOURCE_DF = spark.read.format("jdbc")
.option("url", "jdbc:oracle:thin:@...full string here")
.option("oracle.net.wallet_location", "(SOURCE=(METHOD=file)(METHOD_DATA=(DIRECTORY=/path/to/sso/dir)))")
...
...
如果您需要传递其他详细信息,您可以添加更多.options
参数
.option("oracle.net.tns_admin", "oracle/tns/file/path"))
.option("javax.net.ssl.trustStoreType", "sso")
Caused by: oracle.net.ns.NetException: Unable to initialize the key store.
Caused by: java.security.KeyStoreException: SSO not found
Caused by: java.security.NoSuchAlgorithmException: SSO KeyStore not available
观察和说明:
此错误表示
KeyStore
,而不是TrustStore
。但这可能只是误导性的术语,然而,java 和 oracle 类的错误消息使用相同的术语。根 CA 证书
TrustStore
,但如果使用的是客户端密钥,则需要以下KeyStore
属性:.option("javax.net.ssl.keyStore","/path/to/cwallet.sso") .option("javax.net.ssl.keyStoreType","SSO") .option("oracle.net.authentication_services","(TCPS)")
Oracle JDBC SSL手册本身就造成了术语混淆,因为它甚至从未解决
TrustStore
:如果 oraclepki.jar 文件位于 CLASSPATH 上,则驱动程序可以通过以下方式自动加载 Oracle PKI 提供程序:
java –cp oraclepki.jar:ojdbc8.jar –D javax.net.ssl.keyStore=/path/to/wallet/cwallet.sso MyApp
同样,对于
oracle.net.wallet_location
连接属性的指定值,驱动程序可以通过以下方式自动加载 Oracle PKI 提供程序:java –cp .:oraclepki.jar:ojdbc8.jar –D oracle.net.wallet_location=file:/path/to/wallet/cwallet.sso MyApp
基于上述替代方案,如果您只指定钱包属性,显然可以省略所有
javax.net.ssl
行?.option("oracle.net.wallet_location", "(SOURCE=(METHOD=file)(METHOD_DATA=(DIRECTORY=/path/to/ssl_wallet)))")
正如您所发现的,使用 Oracle 12.1 客户端连接到 Oracle 18+ 数据库是 SSL 的问题。驱动程序不兼容问题显示为模糊错误(即使这个旧驱动程序应该是"向前兼容的"。
Oracle 12c 驱动程序不支持连接字符串 (Easy Connect Plus) 中的所有增强功能,但在 19c JDBC 驱动程序中添加了一些连接属性(如
wallet_location
)。因此,像这样使用完全指定的自包含 ezconnect 连接 URL 可能会使事情变得更容易。另一种选择是使用Java JKS TrustStore而不是Oracle Wallets。
如果仍然看到问题,请使用
javax.net.debug=all
捕获跟踪详细信息。在 pyspark 中测试之前,请务必测试您与
tnsping
和sqlplus
的连接。