无法使用 SSO 钱包将 Oracle 与 Apache Spark 连接

Posted

技术标签:

【中文标题】无法使用 SSO 钱包将 Oracle 与 Apache Spark 连接【英文标题】:Not able to connect Oracle with Apache Spark using SSO Wallet 【发布时间】:2018-12-04 15:39:18 【问题描述】:

我们正在尝试使用在我们端配置的 SSO 钱包和 Apache Spark 连接到作为 AmazonRDS 运行的远程 Oracle 数据库。我们可以使用下面提到的spark-shell 实用程序加载数据

使用添加到类路径的 jdbc 和 oraclepki jar 启动 spark shell

 spark-shell --driver-class-path /path/to/ojdbc8.jar:/path/to/oraclepki.jar

这是使用的 JDBC url:

 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)

我对 spark 很陌生,可能在这里做错了什么。这就是我尝试配置配置的方式

    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 命令但没有任何运气。 我到底在这里做错了什么?还尝试了this post 中提到的解决方案,但得到了同样的错误。我需要确保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 de Lavarene

在遵循@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 模式下失败。

我已经在使用--jars 命令和逗号分隔的 jar 列表。我发现的是:

1) --jars 期望路径是本地路径,然后将它们复制到 HDFS 路径 2) 一开始使用file:/// 不起作用 3) 如果我没有指定 --jars 参数,程序将要求缺少 JDBC 驱动程序类。一旦我使用 --jars 指定了ojdbc8.jar,错误就会消失并开始给出oraclepki.jar not found 错误。我不知道为什么会这样。 4)还尝试使用:作为分隔符,同时指定多个罐子但没有任何运气

更新 2

我能够使用

解决 oraclepki.jar not found 异常
    --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 路径,但实用程序希望在创建钱包时提供本地路径。

那么我们需要手动将钱包文件复制到所有工作节点吗?或者有没有更好的选择来实现这一点?

请指教

【问题讨论】:

我猜这不相关,但您的示例 trustStoreType 拼写错误 conf.set("javax.net.ssl.trustStoreTyp", "SSO") 看起来您使用的是 12.1.0.2 的 JDBC 驱动程序。请升级到可以从oracle.com/technetwork/database/application-development/jdbc/… 下载的 18.3 @JeandeLavarene 谢谢你的建议,请看我更新的帖子 @user2720864 出现这个新异常“java.lang.NoClassDefFoundError: oracle/security/pki/OracleWallet”是因为您还需要升级依赖的 jar:oraclepki.jar、osdt_core.jar 和 osdt_cert.jar .它们都必须来自同一个版本,即 18.3。 @JeandeLavarene 我已经这样做了。所有这些都来自 18.3。 classnot found 异常已通过添加--driver-class-path 选项解决。现在遇到新的错误集,如 UPDATE 2 标题下所述。 【参考方案1】:

基本上这就是我们能够解决它的方法。这里要记住的一件重要事情是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")

【讨论】:

谢谢!您使用哪个用户来运行作业? opc 还是 spark? @besil 您可以使用任何具有适当权限的用户来运行它,如果上述解决方案适合您,请告诉我。我试图在不将 SSO 复制到所有节点的情况下执行此操作,但徒劳无功。如果你能做到这一点,请分享,这对我来说是非常有用的信息【参考方案2】:
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 manual 本身导致了术语混乱,因为它甚至从未提及 TrustStore

    如果 oraclepki.jar 文件位于 CLASSPATH 上,则驱动程序可以通过以下方式自动加载 Oracle PKI Provider:

    java –cp oraclepki.jar:ojdbc8.jar –D javax.net.ssl.keyStore=/path/to/wallet/cwallet.sso MyApp
    

    同样,对于oracle.net.wallet_location连接属性的指定值,驱动可以通过如下方式自动加载Oracle PKI Provider:

    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 存在问题。驱动程序不兼容问题显示为模糊错误(即使这个旧驱动程序被认为是“forward compatible”。

    Oracle 12c 驱动程序不支持连接字符串 (Easy Connect Plus) 中的所有增强功能,但在 19c JDBC 驱动程序中添加了一些连接属性,例如 wallet_location。因此,使用完全指定的自包含 ezconnect 连接 URL 可能会使事情变得更容易。

    另一种选择是use Java JKS TrustStore,而不是 Oracle Wallets。

    如果您仍然发现问题,请使用 javax.net.debug=all 捕获跟踪详细信息。

    在 pyspark 中进行测试之前,请始终使用 tnspingsqlplus 测试您的连接。

【讨论】:

以上是关于无法使用 SSO 钱包将 Oracle 与 Apache Spark 连接的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat、JDBC 瘦客户端和 Oracle 钱包

谷歌钱包与网站集成

程序员的自我救赎---3.2:SSO及应用案例

向 Oracle 钱包添加证书

如何将常规文件(例如 cwallet.sso )上传到 oracle db 中的 data_pump_dir?

JDBC预言机钱包连接