简体   繁体   English

Docker容器之间的JDBC连接(docker-compose)

[英]JDBC connection between Docker containers (docker-compose)

I try to connect a web application which runs on a tomcat 8 to an oracle database. 我尝试将在tomcat 8上运行的Web应用程序连接到oracle数据库。 Both of them run as Docker containers: 它们都作为Docker容器运行:

docker-compose.yml: 泊坞窗,compose.yml:

version: "3"
services:
  appweb:
   build: ./app
   image: "servlet-search-app:0.1"
   ports:
    - "8888:8080"
   links:
    - appdb
   environment:
    - DATA_SOURCE_NAME="jdbc:oracle:thin:@appdb:1521/XE"

  appdb:
   build: ./db
   image: "servlet-search-db:0.1"
   ports:
    - "49160:22"
    - "1521:1521"
    - "8889:8080"

Dockerfile of my oracle DB image (build: ./db): 我的Oracle DB映像的Dockerfile(内部版本:./db):

FROM wnameless/oracle-xe-11g
ADD createUser.sql /docker-entrypoint-initdb.d/
ENV ORACLE_ALLOW_REMOTE=true

Dockerfile of the tomcat image (build: ./app) tomcat映像的Dockerfile(内部版本:./ app)

FROM tomcat:8.0.20-jre8
COPY servlet.war /usr/local/tomcat/webapps/
COPY ojdbc14-1.0.jar /usr/local/tomcat/lib/

So the app starts up as expected but throws an exception when trying to connect to the database: 因此,应用程序将按预期启动,但是在尝试连接数据库时引发异常:

java.lang.IllegalStateException: java.sql.SQLException: Io exception: Invalid connection string format, a valid format is: "host:port:sid" 
    org.se.lab.ui.ControllerServlet.createConnection(ControllerServlet.java:115)
    org.se.lab.ui.ControllerServlet.handleSearch(ControllerServlet.java:78)
    org.se.lab.ui.ControllerServlet.doPost(ControllerServlet.java:53)
    org.se.lab.ui.ControllerServlet.doGet(ControllerServlet.java:38)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

Now the issue seems obvious, however when I fix the DATA_SOURCE_NAME string to: 现在问题似乎显而易见,但是当我将DATA_SOURCE_NAME字符串修复为:

DATA_SOURCE_NAME="jdbc:oracle:thin:@appdb:1521:XE"

I get the following exception: 我得到以下异常:

java.lang.IllegalStateException: java.sql.SQLException: Listener refused the connection with the following error:
ORA-12505, TNS:listener does not currently know of SID given in connect descriptor
The Connection descriptor used by the client was:
appdb:1521:XE"

    org.se.lab.ui.ControllerServlet.createConnection(ControllerServlet.java:115)
    org.se.lab.ui.ControllerServlet.handleSearch(ControllerServlet.java:78)
    org.se.lab.ui.ControllerServlet.doPost(ControllerServlet.java:53)
    org.se.lab.ui.ControllerServlet.doGet(ControllerServlet.java:38)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

Now I tried to find out which one of them should actually work. 现在,我试图找出其中一个应该真正起作用。 Thus, I started only the DB container: 因此,我仅启动了数据库容器:

docker build -t dbtest .
docker run -it -d --rm -p 1521:1521 --name dbtest dbtest
docker inspect dbtest | grep IPAddress
>> "IPAddress": "172.17.0.4"

Next, I try to connect with sqlplus: 接下来,我尝试使用sqlplus连接:

sqlplus system/oracle@172.17.0.4:1521/XE # works
sqlplus system/oracle@172.17.0.4:1521:XE #ERROR: ORA-12545: Connect failed because target host or object does not exist

So what's the problem? 所以有什么问题? Due to the link in the docker-compose file, the tomcat container can resolve "appdb" to the container's IP. 由于docker-compose文件中的链接,tomcat容器可以将“ appdb”解析为容器的IP。

Here's the code which should establish the connection: 这是应该建立连接的代码:

protected Connection createConnection() {
        String datasource = System.getenv("DATA_SOURCE_NAME");
        try {
            // debug
            InetAddress address = null;
            try {
                address = InetAddress.getByName("appdb");
                System.out.println(address); // resolves in appdb/10.0.0.2
                System.out.println(address.getHostAddress()); // resolves in 10.0.0.2
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }

            Class.forName("oracle.jdbc.driver.OracleDriver");
            return DriverManager.getConnection(datasource, "system", "oracle");
        } catch (SQLException | ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }
    }

Lastly here's the tnsnames.ora file: 最后是tnsnames.ora文件:

 cat $ORACLE_HOME/network/admin/tnsnames.ora
# tnsnames.ora Network Configuration File:

XE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = fcffb044d69d)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  )

EXTPROC_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
    )
    (CONNECT_DATA =
      (SID = PLSExtProc)
      (PRESENTATION = RO)
    )
  )

Thanks! 谢谢!

The oracle default listener resolved the configured host to the wrong IP address: oracle默认侦听器将配置的主机解析为错误的IP地址:

vim $ORACLE_HOME/network/admin/listener.ora: vim $ ORACLE_HOME / network / admin / listener.ora:

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = PLSExtProc)
      (ORACLE_HOME = /u01/app/oracle/product/11.2.0/xe)
      (PROGRAM = extproc)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
      (ADDRESS = (PROTOCOL = TCP)(HOST = f4c4a3638c11)(PORT = 1521))
    )
  )

DEFAULT_SERVICE_LISTENER = (XE)

The HOST value is the Docker container id. HOST值是Docker容器ID。 If we look at /etc/hosts it is set up correctly for the service link in the docker-compose link: 如果我们查看/ etc / hosts,则会在docker-compose链接中正确设置服务链接:

10.0.0.5        f4c4a3638c11

It gets also resolved correctly from the tomcat container 它也可以从tomcat容器中正确解析

ping f4c4a3638c11
PING f4c4a3638c11 (10.0.0.5): 56 data bytes
...

If I try to connect with an IP address of the other interface, which is the docker interface from the host system, the connection from the web application to the database works 如果我尝试使用另一个接口的IP地址(即主机系统的docker接口)进行连接,则从Web应用程序到数据库的连接有效

String datasource = "jdbc:oracle:thin:@172.17.0.4:1521:XE";

So the solution is to configure the listener to listen to the correct IP address 因此,解决方案是将侦听器配置为侦听正确的IP地址

(ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.0.5)(PORT = 1521))

Now this connection string works: 现在,此连接字符串起作用:

jdbc:oracle:thin:@appdb:1521:XE

I will report this behavior to wnameless/oracle-xe-11g as a bug 我会将这个行为作为错误报告给wnameless / oracle-xe-11g

Sorry, this is not a definitive answer. 抱歉,这不是一个明确的答案。 Let's treat it as long comment :) 让我们将其视为长注释:)

Your setup is quite complex for me to recreate however your error message is intriguing: 对于我来说,您的设置非常复杂,但是您的错误消息很有趣:

The Connection descriptor used by the client was:
appdb:1521:XE"
...

It looks like the environment value was chopped to appdb:1521:XE . 似乎环境值已切碎为appdb:1521:XE How about if you try hard-coding: 如果您尝试硬编码怎么办:

String datasource = "jdbc:oracle:thin:@appdb:1521/XE";

If that works, then probably need to somehow escape your docker DATA_SOURCE_NAME environment variable. 如果DATA_SOURCE_NAME ,则可能需要以某种方式转义DATA_SOURCE_NAME环境变量。

I could be completely wrong but I think it is worth a try. 我可能完全错了,但我认为值得尝试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM