2
votes

So I have a Spring boot application that runs on port 8080 on my localhost. It connects to an Azure sqlserver database and pulls or inserts item from that database. Locally everything is working fine. Now I'm trying to dockerize the application. My Dockerfile looks like this:

FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
COPY mssql-jdbc-9.2.1.jre15.jar .
RUN mvn -f /usr/src/app/pom.xml clean package

FROM gcr.io/distroless/java
COPY --from=build /usr/src/app/target/ProfileService-0.0.1-SNAPSHOT.jar /usr/app/ProfileService-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/ProfileService-0.0.1-SNAPSHOT.jar"]

The Dockerfile is located in the root folder, the same folder as the pom.xml and the mssql-jdbc-9.2.1.jre15.jar file. This jar is downloaded from the official Microsoft source. My pom includes multiple dependencies, one of them is the mssql-jdbc dependency.

<dependency>

    <groupId>com.microsoft.sqlserver</groupId>

    <artifactId>mssql-jdbc</artifactId>

    <version>9.2.1.jre15</version>

</dependency>

To connect to the database, I use the following code:

private final String connectionUrl = "jdbc:sqlserver://x.database.windows.net:1433;DatabaseName=profile;user=x@xr;password=x;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;";

With the following method as an example to get data:

public GetProfileReturnModel getProfile(String profile_name) {
    GetProfileReturnModel returnModel = new GetProfileReturnModel();
    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
        try {
            CallableStatement cstmnt = connection.prepareCall("{CALL getProfile(?)}");
            cstmnt.setString(1, profile_name);

            cstmnt.execute();
            ResultSet rs = cstmnt.getResultSet();
            if (rs.next()) {
                returnModel.setUser_id(rs.getInt("user_id"));
                returnModel.setBio(rs.getString("bio"));
                returnModel.setLocation(rs.getString("location"));
                returnModel.setProfile_name(rs.getString("profile_name"));
                returnModel.setPicture(rs.getString("picture"));
                returnModel.setWebsite(rs.getString("website"));
            }
            returnModel.setSuccess(true);
        } catch (SQLException e) {
            returnModel.setSuccess(false);
            returnModel.setErrorMessage(e.toString());
    }
    } catch (SQLException e) {
        returnModel.setErrorMessage(e.getMessage());
        returnModel.setSuccess(false);
    }
    return returnModel;
}

This works fine locally. But when I run my build image in Docker and I call the same request I get the following error:

No suitable driver found for jdbc:sqlserver://x.database.windows.net:1433;DatabaseName=profile;user=x@xr;password=x;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;

There is also this weird behavior that when I run the packaged .jar file using the cmd it runs perfectly, but if I start it by double clicking the jar I get the same error as mentioned above

My complete pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kwetter.profileservice</groupId>
    <artifactId>ProfileService</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>ProfileService</name>
    <description>Demo project for Spring Boot</description>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <distributionManagement>
        <repository>
            <id>ProfileServiceBuild</id>
            <url>https://pkgs.dev.azure.com/382763/Kwetter/_packaging/ProfileServiceBuild/maven/v1</url>
        </repository>
    </distributionManagement>
    <repositories>
        <repository>
            <id>ProfileServiceBuild</id>
            <url>https://pkgs.dev.azure.com/382763/Kwetter/_packaging/ProfileServiceBuild/maven/v1</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.4.3</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.amqp/spring-rabbit -->
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.3.6</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.amqp/spring-amqp -->
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-amqp</artifactId>
            <version>2.3.6</version>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>9.2.1.jre15</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.ws.rs</groupId>
            <artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.3</version>
        </dependency>
    </dependencies>
</project>
3
If you have it in your POM like this you dont need to copy it manually in the image. It will be downloaded from maven central.Robert Niestroj
@RobertNiestroj that's what I thought. I just tried some stuff and that was one of themKevin Tinnemans
Are you using spring-boot-maven-plugin ?Michał Krzywański
@michalk yes I amKevin Tinnemans
@KevinTinnemans I posted a possible solution, I hope it helps.jccampanero

3 Answers

1
votes

The spring-boot-maven-plugin should by default, when configuring a jar packaging, repackage and include all your project dependencies in a big jar when mvn package runs, so probably the SQL Server JDBC Driver related classes should be included as well.

The docker related problem may have to do with the fact that you are using an incompatible Java version for the provided SQL Server driver when running your application.

You are using the SQL Server JDBC Driver for Java 15:

<dependency>
  <groupId>com.microsoft.sqlserver</groupId>
  <artifactId>mssql-jdbc</artifactId>
  <version>9.2.1.jre15</version>
  <scope>compile</scope>
</dependency>

Probably you are using Java 15 in your local environment, and it is running fine.

But you are running the application with gcr.io/distroless/java and that image only supports Java 1.8 and Java 11.

Please, consider use a different version of the SQL Server driver, for example:

<dependency>
  <groupId>com.microsoft.sqlserver</groupId>
  <artifactId>mssql-jdbc</artifactId>
  <version>9.2.1.jre8</version>
</dependency>

And adapt your Dockerfile if necessary:

FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package

FROM gcr.io/distroless/java:8
COPY --from=build /usr/src/app/target/ProfileService-0.0.1-SNAPSHOT.jar /usr/app/ProfileService-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/ProfileService-0.0.1-SNAPSHOT.jar"]

I removed line COPY mssql-jdbc-9.2.1.xxx.jar .: if you are including the dependency in your pom it will not be necessary.

Perhaps the double click problem can also be related with this if you are using a different version of the JVM as the one configured by default in your OS.

0
votes

This could be caused by Azure SQL requiring login through AD Authentication. Your connection string would look like this:

jdbc:sqlserver://x.database.windows.net:1433;DatabaseName=profile;user=x@xr;password=x;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword;
0
votes

It seems you are including the jdbc driver in your build image instead of your runtime image

You should copy it to your runtime images the following way : (please check the appropriate destination folder, but I assumed /usr/app/)

FROM gcr.io/distroless/java
COPY --from=build /usr/src/app/target/ProfileService-0.0.1-SNAPSHOT.jar /usr/app/ProfileService-0.0.1-SNAPSHOT.jar
COPY mssql-jdbc-9.2.1.jre15.jar /usr/app/
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/ProfileService-0.0.1-SNAPSHOT.jar"]