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

9 data governance strategies that will unlock the potential of your business data

10 min read - Everything is data—digital messages, emails, customer information, contracts, presentations, sensor data—virtually anything humans interact with can be converted into data, analyzed for insights or transformed into a product. The best way to build a strong foundation for data success is through effective data governance. Access to high-quality data can help organizations start successful products, defend against digital attacks, understand failures and pivot toward success. Emerging technologies and trends, such as machine learning (ML), artificial intelligence (AI), automation and generative AI…

How IBM is shaping AI governance in education with Smarter Balanced

6 min read - The California-based Smarter Balanced Assessment Consortium is a member-led public organization that provides assessment systems to educators working in K-12 and higher education. The organization, which was founded in 2010, partners with state education agencies to develop innovative, standards-aligned test assessment systems. Smarter Balanced supports educators with tools, lessons and resources including formative, interim and summative assessments, which help educators to identify learning opportunities and strengthen student learning. Smarter Balanced is committed to evolution and innovation in an ever-changing educational…

Apache Kafka use cases: Driving innovation across diverse industries

6 min read - Apache Kafka is an open-source, distributed streaming platform that allows developers to build real-time, event-driven applications. With Apache Kafka, developers can build applications that continuously use streaming data records and deliver real-time experiences to users. Whether checking an account balance, streaming Netflix or browsing LinkedIn, today’s users expect near real-time experiences from apps. Apache Kafka’s event-driven architecture was designed to store data and broadcast events in real-time, making it both a message broker and a storage unit that enables real-time…

IBM Newsletters

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