Search code examples
javaspringdocker-compose

Dockerized Spring Application with environment variables


I am currently trying to run a Java Spring application in a Docker container. This has worked without any problems so far. But now I want that in the application.properties there are no fixed values but only placeholders, which are passed as environment variables to the container.

According to the Spring Docs this should be possible but when I try this I always get the error that:

no database connection can be established to the placeholder ${DB_CONFIG} (Caused by: java.lang.RuntimeException: Driver com.mysql.cj.jdbc.Driver claims to not accept jdbcUrl, ${DB_CONFIG}).

I already tried to pass the application.properties externally, instead of an env file pass the variables directly as Docker environment variable and instead of a custom variable (DB_CONFIG) for the JDBC url pass the Spring variable (SPRING_DATASOURCE_URL). Neither variant worked for me.

For your information: I don't maintain the source code, I just get it from a CI/CD, copy the changed application.properties to its place and compile the project.


docker-compose file:

version: '3'

services:
    arrowhead-serviceregistry:
        container_name: arrowhead-serviceregistry
        image: 'openjdk:11-jre-slim-buster'
        restart: always
        env_file: '.env'
        ports:
            - 8443:8443/tcp
        volumes:
            - ./arrowhead-serviceregistry-4.3.0.jar:/service.jar
        depends_on:
            - mysql
        command:
            -java -noverify -XX:TieredStopAtLevel=1 -jar /service.jar

application.properties file:

############################################
###       APPLICATION PARAMETERS         ###
############################################

# Database connection (mandatory)
# Change the server timezone if neccessary
spring.datasource.url=${DB_CONFIG}
spring.datasource.username=${SERVICEREGISTRY_DB_USERNAME}
spring.datasource.password=${SERVICEREGISTRY_DB_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# use true only for debugging
spring.jpa.show-sql=false  
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=none

# Service Registry web-server parameters
server.address=0.0.0.0
server.port=${SERVICEREGISTRY_PORT}
domain.name=${SERVICEREGISTRY_ADDRESS}
domain.port=${SERVICEREGISTRY_PORT}

############################################
###       CUSTOM PARAMETERS              ###
############################################

# Name of the core system
core_system_name=SERVICE_REGISTRY 

# Show all request/response in debug log
log_all_request_and_response=${SERVICEREGISTRY_LOG_REQUESTS}

# Service Registry has an optional feature to ping service providers in a fixed time interval,
# and remove service offerings where the service provider was not available
# use this feature (true/false)
ping_scheduled=${SERVICEREGISTRY_PING_ENABLE}
# how much time the Service Registry should wait for the ping response (in milliseconds)
ping_timeout=${SERVICEREGISTRY_PING_TIMEOUT}
# how frequently should the ping happen, in minutes
ping_interval=${SERVICEREGISTRY_PING_INTERVAL}

# Service Registry has an optional feature to automatically remove service offerings, where the endOfValidity
# timestamp field is in the past, meaning the offering expired
# use this feature (true/false)
ttl_scheduled=${SERVICEREGISTRY_TTL_ENABLE}
# how frequently the database should be checked for expired services, in minutes
ttl_interval=${SERVICEREGISTRY_TTL_INTERVAL}

# Interface names has to follow this format <PROTOCOL>-<SECURITY>-<FORMAT>, where security can be SECURE or INSECURE and protocol and format must be a sequence of letters, numbers and underscore.
# A regexp checker will verify that. If this setting is set to true then the PROTOCOL and FORMAT must come from a predefined set.
use_strict_service_intf_name_verifier=${SERVICEREGISTRY_STRICT_INTERFACE_NAMES}

############################################
###           SECURE MODE                ###
############################################

# configure secure mode

# Set this to false to disable https mode
server.ssl.enabled=${SSL_ENABLED}

server.ssl.key-store-type=${SERVICEREGISTRY_SSL_KEYSTORE_TYPE}
server.ssl.key-store=${SERVICEREGISTRY_SSL_KEYSTORE}
server.ssl.key-store-password=${SERVICEREGISTRY_SSL_KEYSTORE_PASSWORD}
server.ssl.key-alias=${SERVICEREGISTRY_SSL_KEY_ALIAS}
server.ssl.key-password=${SERVICEREGISTRY_SSL_KEY_PASSWORD}
server.ssl.client-auth=${SERVICEREGISTRY_SSL_CLIENT_AUTH}
server.ssl.trust-store-type=${SERVICEREGISTRY_SSL_TRUSTSTORE_TYPE}
server.ssl.trust-store=${SERVICEREGISTRY_SSL_TRUSTSTORE_PATH}
server.ssl.trust-store-password=${SERVICEREGISTRY_SSL_TRUSTSTORE_PASSWORD}

#If true, http client does not check whether the hostname is match one of the server's SAN in its certificate
#Just for testing, DO NOT USE this feature in production environment 
disable.hostname.verifier=${SERVICEREGISTRY_DISABLE_HOSTNAME_VERIFIER}

Environment file:

# Basic settings
DB_CONFIG=jdbc:mysql://127.0.0.1:3306/arrowhead?serverTimezone=Europe/Berlin
SSL_ENABLED=false


# Service Registry settings
SERVICEREGISTRY_ADDRESS=127.0.0.1
SERVICEREGISTRY_PORT=8443
SERVICEREGISTRY_DB_USERNAME=myUser
SERVICEREGISTRY_DB_PASSWORD=xxx
SERVICEREGISTRY_LOG_REQUESTS=false
SERVICEREGISTRY_PING_ENABLE=false
SERVICEREGISTRY_PING_TIMEOUT=5000
SERVICEREGISTRY_PING_INTERVAL=60
SERVICEREGISTRY_TTL_ENABLE=false
SERVICEREGISTRY_TTL_INTERVAL=10
SERVICEREGISTRY_STRICT_INTERFACE_NAMES=false
SERVICEREGISTRY_DISABLE_HOSTNAME_VERIFIER=false
SERVICEREGISTRY_SSL_KEYSTORE_TYPE=PKCS12
SERVICEREGISTRY_SSL_KEYSTORE=classpath:certificates/service_registry.p12
SERVICEREGISTRY_SSL_KEYSTORE_PASSWORD=123456
SERVICEREGISTRY_SSL_KEY_ALIAS=service_registry
SERVICEREGISTRY_SSL_KEY_PASSWORD=123456
SERVICEREGISTRY_SSL_CLIENT_AUTH=need
SERVICEREGISTRY_SSL_TRUSTSTORE_TYPE=PKCS12
SERVICEREGISTRY_SSL_TRUSTSTORE_PATH=classpath:certificates/truststore.p12
SERVICEREGISTRY_SSL_TRUSTSTORE_PASSWORD=123456

Solution

  • While searching for a solution to my problem, I came across the Spring Cloud Config Server (also just a Spring application that is self-hostable). This provides an interface between the configuration file storage location and the Spring application. The application is set up so that instead of a static application.(properties|yml) file, a bootstrap.yml is stored with the URL to the config server. When the application starts up, it then connects to the Config Server and receives it via a REST interface. The configuration files can be stored in Git/a DB or as a file.

    Spring Docs - Cloud Config Server
    Spring - Instruction for Implementation
    Baeldung - Tutorial
    Baeldung - Tutorial Bootstrapping