简体   繁体   中英

JDBC connection between Docker containers (docker-compose)

I try to connect a web application which runs on a tomcat 8 to an oracle database. Both of them run as Docker containers:

docker-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):

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

Dockerfile of the tomcat image (build: ./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="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 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.

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:

 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:

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. If we look at /etc/hosts it is set up correctly for the service link in the docker-compose link:

10.0.0.5        f4c4a3638c11

It gets also resolved correctly from the tomcat container

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

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

(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

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 . 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.

I could be completely wrong but I think it is worth a try.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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