29 Sept 2018

Configuring a Datasource in a Docker Container

In this post I am going to show how to configure a datasource consumed by an ADF application running on Tomcat in a Docker container.


So, there is a Docker container sample-adf with a Tomcat application server preconfigured with ADF libraries and with an ADF application running on top of Tomcat. The ADF application requires a connection to an external database.
The application is implemented with ADF BC and it's application module is referring to a datasource jdbc/appDS.



This datasource is configured inside a container in Tomcat /conf/context.xml file. The JDBC url, username and password are provided by environment variables:

<Resource name="jdbc/appDS" auth="Container"
           type="oracle.jdbc.pool.OracleDataSource"
           factory="oracle.jdbc.pool.OracleDataSourceFactory"
           url="${DB_URL}"
           user="${DB_USERNAME}"
           password="${DB_PWD}"

           ...


These variables are propagated to the application server in Tomcat /bin/setenv.sh file:

CATALINA_OPTS='-DDB_URL=$DB_URL -DDB_USERNAME=$DB_USERNAME -DDB_PWD=DB_PWD ...'

Having these configurations set, we can run a container providing values of the variables:

docker run --name adf -e DB_URL="jdbc:oracle:thin:@myhost:1521:xe" -e DB_USERNAME=system -e DB_PWD=welcome1 sample-adf

If we are about to run a container in a K8s cluster we can provide variable values in a yaml file:

spec:
      containers:
      - image: sample-adf
        env:
        - name: DB_URL
           value: "jdbc:oracle:thin:@myhost:1521:xe"
        - name: DB_USERNAME
          value: "system"
        - name: DB_PWD
          value: "welcome1"


In order to make this yaml file portable we would avoid providing exact values and refer to K8s ConfigMaps and Secrets instead of that

A ConfigMap is a named K8s resource that allows us to decouple configuration artifacts from image content to keep containerized applications portable. This is just a simple set of key-value paires. And obviously those values in each K8s cluster, in each environment are different.

Similar approach is used when it comes to sensitive data like user names and passwords. Only in this case instead of configmaps we use a special resource which is called Secret. The data is encoded and it is only sent to a node if a pod on that node requires it. It is deleted once the pod that depends on it is deleted.

We can create ConfigMaps and Secrets out of key-value files or just by providing the values in a command line:

kubectl create configmap adf-config  
--from-literal=db.url="jdbc:oracle:thin:@myhost:1521:xe"

kubectl create secret generic adf-secret 
--from-literal=db.username="system
--from-literal=db.pwd="welcome1"


Having done that we can specify in the yaml file that values for the environment variables should be fetched from adf-config ConfigMap and adf-secret Secret:

spec:
      containers:
      - image: sample-adf
        env:
        - name: DB_URL
          valueFrom:
               configMapKeyRef:
                   name: adf-config
                   key: db.url
        - name: DB_USERNAME
          valueFrom:
               secretKeyRef:
                   name: adf-secret
                   key: db.username
        - name: DB_PWD
          valueFrom:
               secretKeyRef:
                   name: adf-secret
                   key: db.pwd


That's it!