July 19, 2017 By Paul Bennett 6 min read

Spring Boot App Running on Bluemix Kubernetes Using Kubernetes Secrets

In this post I’ll demonstrate how to:

  • Run a Java Spring Boot application on Bluemix Kubernetes that accesses a Bluemix MongoDB service

  • Securely configure the MongoDB service credentials using Kubernetes secrets

  • Use the Spring Cloud Kubernetes framework to access the secrets as standard Spring config properties.

This example builds on my previous post where I showed how to access a Bluemix MongoDB service from a Spring data app running locally. In that simple example the MongoDB credentials were either hard coded in the application or specified manually on the command line.

Prerequisites

To run this demo you’ll need to have:

Create a Kubernetes Cluster

Login to your Bluemix account and go to the Catalog. Under Containers select Kubernetes Cluster. On the Create Cluster page enter a cluster name and type. For this example we’ll use the name mycluster and cluster type Lite. Select Create to create the cluster. It may take a few minutes to create and initialize the cluster so be patient.

After your cluster is initialized configure the CLI to run kubectl commands against your cluster as described here.

Now that kubectl is setup we can use the Kubernetes dashboard to view and manage the cluster. First start the Kubernetes proxy by issuing the following command:

<br>
kubectl proxy &<br>

You can now display the dashboard with the url http://localhost:8001/ui. Initially the cluster should be empty as we haven’t deployed any apps to it yet:

Create a MongoDB Service

From the Bluemix Catalog, under Data & Analytics select the Compose for MongoDB service to create a new MongoDB instance. Select Create to create an instance of MongoDB – the default service name and credential name should be fine.

Create a Kubernetes Secret with the MongoDB Service Credentials

The MongoDB service has security credentials that are required by the application to access the service:

  • A connection string which contains a userid and password

  • An SSL certificate which is used to establish trust on the connection to the MongoDB service

The best practice for configuring a Kubernetes application with security credentials is to use Kubernetes Secrets, rather than hard coding them in the application or putting them in environment variables. We’ll create a secret that contains the following four pieces of data:

  • The MongoDB connection string

  • A Java trust store (a .jks file) containing the MongoDB service’s SSL certificate

  • The name of the trust store file

  • The password of the trust store

To get the service’s connection string and SSL certificate go to your Bluemix dashboard and select the MongoDB service – the connection string and certificate will be displayed:

We’ll use the kubectl create secret command to create our secret from files, so we’ll need to create four files – one for each piece of data in the secret.

  • Create a file named spring.data.mongodb.uri and put the MongoDB connection string in it.

  • Create a trust store containing the MongoDB service’s SSL certificate. First copy the certificate and save it in a file (mongo-service.crt in this example) and issue the following command:

    <br>
    keytool -import -alias compose -file /<path-to-mongo-cert>/mongo-service.crt -keystore /<path-to-trust-store>/mongo-trust.jks -storetype pkcs12 -storepass keypass<br>

     

  • Create a file named trust.file.name that contains the name of the trust store – in this example mongo-trust.jks.

  • Create a file named trust.file.password that contains the password of the trust store – keypass.

Now create a Kubernetes secret named mongosecret on your cluster from the four files just created by issueing the following command:

<br>
kubectl create secret generic mongosecret --from-file=./spring.data.mongodb.uri --from-file=./mongo-trust.jks --from-file=./trust.file.password --from-file=./trust.file.name<br>

 

You can use the dashboard to verify that the secret was created:

Create the Application

We’ll use the Spring guide Accessing MongoDB Data with REST application for this example. Follow the instructions in the guide to download and build the complete project.

The project defaults to using a local MongoDB server installed on your computer. To use the Bluemix MongoDB service we’ll need to modify the app to access the MongoDB service using the credentials in the Kubernetes secret we created.

  • Create the file src/main/resources/bootstrap.properties with the following properties to tell Spring to set config properties from Kubernetes secrets located at /etc/mongo/config:

    <br>
    spring.cloud.kubernetes.secrets.enabled=true<br>
    spring.cloud.kubernetes.secrets.paths=/etc/mongo/config<br>

     

  • Modify src/main/java/hello/Application.java to add the following (highlighted) lines to tell Spring the host and port of the Kubernetes cluster service so that the Spring Cloud Kubernetes framework can communicate with the cluster:

    <br>
        public static void main(String[] args) {<br>
            String kubeSvcHost, kubeSvcPort;<br>
            if ((kubeSvcHost = System.getenv("KUBERNETES_SERVICE_HOST")) != null &&<br>
                (kubeSvcPort = System.getenv("KUBERNETES_SERVICE_PORT_HTTPS")) != null) {<br>
                System.setProperty("kubernetes.master", kubeSvcHost + ":" + kubeSvcPort);<br>
            }<br>
            SpringApplication.run(Application.class, args);<br>
        }<br>

     

  • Add the following dependencies to the pom.xml file to add the Spring Cloud Kubernetes jars to the application. Note that the spring-data-rest-core dependency is necessary because spring-cloud-kubernetes-core includes a down level REST, and okhttp is necessary because spring-cloud-kubernetes-core does not include okhttp which is required.

    <br>
            <dependency><br>
                <groupId>io.fabric8</groupId><br>
                <artifactId>spring-cloud-kubernetes-core</artifactId><br>
            </dependency><br>
            <dependency><br>
                <groupId>org.springframework.data</groupId><br>
                <artifactId>spring-data-rest-core</artifactId><br>
                <version>2.6.4.RELEASE</version><br>
            </dependency><br>
            <dependency><br>
                <groupId>com.squareup.okhttp3</groupId><br>
                <artifactId>okhttp</artifactId><br>
                <version>3.8.1</version><br>
            </dependency><br>

     

  • Add the following dependency management bom (Bill Of Materials) to the top level of the pom.xml file to ensure that various dependencies are at the right version:

    <br>
        <dependencyManagement><br>
            <dependencies><br>
                <dependency><br>
                    <groupId>io.fabric8</groupId><br>
                    <artifactId>spring-cloud-kubernetes-bom</artifactId><br>
                    <version>0.1.6</version><br>
                    <type>pom</type><br>
                    <scope>import</scope><br>
                </dependency><br>
            </dependencies><br>
        </dependencyManagement><br>
  • Create the following src/main/java/hello/MyBeanPostProcessor.java class to set the Java trust store and password system properties. The @Value annotations get the property values from the Kubernetes secret and injects them into the variables.Note: System properties must be used because Spring config doesn’t currently support Java trust stores. A BeanPostProcessor is needed in order to set the trust store before MongoDB initialization.

    <br>
    package hello;<p></p>
    <p>import org.springframework.beans.BeansException;<br>
    import org.springframework.beans.factory.annotation.Value;<br>
    import org.springframework.beans.factory.config.BeanPostProcessor;<br>
    import org.springframework.stereotype.Component;</p>
    <p>@Component<br>
    public class MyBeanPostProcessor implements BeanPostProcessor {</p>
    <p>    boolean isMongoInitialized = false;</p>
    <p>    @Value("${spring.cloud.kubernetes.secrets.paths}")<br>
        String trustFilePath;</p>
    <p>    @Value("${trust.file.name}")<br>
        String trustFileName;</p>
    <p>    @Value("${trust.file.password}")<br>
        String trustFilePassword;</p>
    <p>    @Override<br>
        public Object postProcessBeforeInitialization(Object bean, String beanName)<br>
                throws BeansException {<br>
            if (!isMongoInitialized &&<br>
                beanName.endsWith("MongoProperties")) {<br>
                isMongoInitialized = true;<br>
                if (trustFilePath != null && trustFilePath.length() > 0 &&<br>
                    trustFileName != null && trustFileName.length() > 0 &&<br>
                    trustFilePassword != null && trustFilePassword.length() > 0) {<br>
                    System.setProperty("javax.net.ssl.trustStore", trustFilePath + "/" + trustFileName);<br>
                    System.setProperty("javax.net.ssl.trustStorePassword", trustFilePassword);<br>
                }<br>
            }<br>
            return bean;<br>
        }</p>
    <p>    @Override<br>
        public Object postProcessAfterInitialization(Object bean, String beanName)<br>
                throws BeansException {<br>
            return bean;<br>
        }<br>
    }<br>
    </p>

Finally, build the project with the following command:

<br>
mvn clean package -Dmaven.test.skip=true<br>

Build the Docker Image and push it to the Bluemix Registry

To build the Docker image first create the following Dockerfile:

<br>
FROM frolvlad/alpine-oraclejdk8:slim<br>
COPY target/gs-accessing-mongodb-data-rest-0.1.0.jar app.jar<br>
EXPOSE 8080:8080<br>
ENTRYPOINT [ "sh", "-c", "java -jar /app.jar" ]<br>

Then run the following commands where is your Bluemix container registry namespace:

<br>
docker build -t spring-mongo-rest .<br>
docker tag spring-mongo-rest registry.ng.bluemix.net/<namespace>/spring-mongo-rest<br>
docker push registry.ng.bluemix.net/<namespace>/spring-mongo-rest<br>

Deploy the Application to the Bluemix Kubernetes Cluster

Create the following kube-pod.yaml file. The highlighted lines mount mongosecret at location /etc/mongo/config. (This must be the same location specified in the Spring property spring.cloud.kubernetes.secrets.paths in bootstrap.properties.)

<br>
apiVersion: v1<br>
kind: Pod<br>
metadata:<br>
  name: spring-mongo-rest-pod<br>
  labels:<br>
    name: spring-mongo-rest-pod<br>
spec:<br>
  containers:<br>
  - name: spring-mongo-rest-container<br>
    image: registry.ng.bluemix.net/<namespace>/spring-mongo-rest:latest<br>
    ports:<br>
    - containerPort: 8080<br>
    volumeMounts:<br>
    - name: foo<br>
      mountPath: /etc/mongo/config<br>
  volumes:<br>
  - name: foo<br>
    secret:<br>
      secretName: mongosecret<br>
---<br>
apiVersion: v1<br>
kind: Service<br>
metadata:<br>
  name: spring-mongo-rest-service<br>
  namespace: default<br>
spec:<br>
  type: NodePort<br>
  ports:<br>
  - name: http<br>
    protocol: TCP<br>
    port: 8080<br>
  selector:<br>
    name: spring-mongo-rest-pod<br>

Then run the following command to deploy the app:

<br>
kubectl apply -f kube-pod.yaml<br>

You can verify in the Kubernetes dashboard that the spring-mongo-rest-service and it’s pod started successfully, and get it’s external port number – 30309 in this example:

Invoke the Application

Since this is a NodePort service the IP address of the service will be the IP Address of the node – 169.51.9.135 in this example – which you can get from the Kubernetes dashboard:

The app will be listening on 169.51.9.135:30309 for REST requests that it will forward to the remote Bluemix MongoDB service. You can now update and query the database using CURL commands to 169.51.9.135:30309:

  • To add a person:

    <br>
    $ curl -i -X POST -H "Content-Type:application/json" -d "{  \"firstName\" : \"Frodo\",  \"lastName\" : \"Baggins\" }" http://169.51.9.135:30309/people<p></p>
    <p>HTTP/1.1 201<br>
    X-Application-Context: application<br>
    Location: http://169.51.9.135:30309/people/59652f908ef772325cd7d899<br>
    Content-Type: application/hal+json;charset=UTF-8<br>
    Transfer-Encoding: chunked<br>
    Date: Tue, 11 Jul 2017 20:05:36 GMT<br>
    {<br>
      "firstName" : "Frodo",<br>
      "lastName" : "Baggins",<br>
      "_links" : {<br>
        "self" : {<br>
          "href" : "http://169.51.9.135:30309/people/59652f908ef772325cd7d899"<br>
        },<br>
        "person" : {<br>
          "href" : "http://169.51.9.135:30309/people/59652f908ef772325cd7d899"<br>
        }<br>
      }<br>
    }<br>
    </p>
  • To query all people in the DB:

    <br>
    $ curl http://169.51.9.135:30309/people<br>
    {<br>
      "_embedded" : {<br>
        "people" : [ {<br>
          "firstName" : "Frodo",<br>
          "lastName" : "Baggins",<br>
          "_links" : {<br>
            "self" : {<br>
              "href" : "http://169.51.9.135:30309/people/59652f908ef772325cd7d899"<br>
            },<br>
            "person" : {<br>
              "href" : "http://169.51.9.135:30309/people/59652f908ef772325cd7d899"<br>
            }<br>
          }<br>
        } ]<br>
      },<br>
      "_links" : {<br>
        "self" : {<br>
          "href" : "http://169.51.9.135:30309/people{?page,size,sort}",<br>
          "templated" : true<br>
        },<br>
        "profile" : {<br>
          "href" : "http://169.51.9.135:30309/profile/people"<br>
        },<br>
        "search" : {<br>
          "href" : "http://169.51.9.135:30309/people/search"<br>
        }<br>
      },<br>
      "page" : {<br>
        "size" : 20,<br>
        "totalElements" : 1,<br>
        "totalPages" : 1,<br>
        "number" : 0<br>
      }<br>
    }<br>
Was this article helpful?
YesNo

More from

Advance your enterprise Journey to Hybrid Cloud and AI powered by AIOps on Z

2 min read - Thanks to rising costs, skills shortages and ever-growing security threats, businesses must adapt quickly to shifts in demand patterns brought on by a digital workforce and rapidly changing buyer behavior. That requires putting extra emphasis on the resiliency and performance of your business processes and supporting applications. For larger IT organizations with increasingly hybrid and complex application landscapes that often include IBM Z®, it’s essential to take a comprehensive approach to IT operations. The challenge becomes “How do you effectively sift through terabytes of…

IBM API Connect named a leader in the Forrester Wave: API Management Software, Q3 2024

4 min read - We are excited to announce that Forrester has recognized IBM API Connect® as a Leader in The Forrester Wave™: API Management Software, Q3 20241. Forrester conducted a 24-criteria evaluation of API management software providers to make their assessment and final results. IBM API Connect received the highest score possible in 17 out of the 24 criteria. [button link="https://www.ibm.com/account/reg/us-en/signup?formid=urx-52934"]Download a complimentary copy of the report here[/button] IBM: What to look for when shopping for API Management Software Transformation and integration initiatives…

Success and recognition of IBM offerings in G2 Summer Reports  

2 min read - IBM offerings were featured in over 1,365 unique G2 reports, earning over 230 Leader badges across various categories.   This recognition is important to showcase our leading products and also to provide the unbiased validation our buyers seek. According to the 2024 G2 Software Buyer Behavior Report, “When researching software, buyers are most likely to trust information from people with similar roles and challenges, and they value transparency above other factors.”  With over 90 million visitors each year and hosting more than 2.6…

IBM Newsletters

Get our newsletters and topic updates that deliver the latest thought leadership and insights on emerging trends.
Subscribe now More newsletters