Deploying a Spring-Boot application running with Java8 on OpenShift2
This post describes how to create and deploy a Spring-Boot application to RedHat OpenShift (version 2) when the application is using Java 8.
Edit 2015-10-04: In this newer post I show how to not install a custom JDK. So you should first read this post and then the linked one for additional information.
Normally deploying a Spring-Boot application on OpenShift is not too much pain and is explained in the Spring-Boot documentation. But some extra work is needed when the application is built and run with Java 8, as at the time of writing, the DIY cartridge of OpenShift only supports Java 7. And, to make things worse, the mvn command which is available in the DIY cartridge is rewritten by RedHat, so it will pick up Java 7 no matter what you set your JAVA_HOME to.
This post will show how to overcome these deficiencies by walking through the necessary steps to create a Spring-Boot based REST service which is deployed on OpenShift. To follow along you need
- Java 8 installed
- an OpenShift account
- setup the
rhc
command line tool as described on OpenShift documentation - know how to create and set up an Spring Boot project (I use a maven project)
My sample is created on Mac OSX by using the terminal and IntelliJ. I will create a REST service named SayService which will just return it’s string input prepended by “you said: “. Not very interesting, but enough for this example.
Create the OpenShift application
As a first step I create the application on OpenShift. To do that, I change into the local directory where I want the app to be created and issue the following rhc
command, assuming you are logged in to OpenShift with rhc
:
rhc app-create sayservice diy
This creates the OpenShift application and clones it’s Git repository to your local sayservice directory. The structure is shown below:
sayservice
├── .git
├── .openshift
│ ├── README.md
│ ├── action_hooks
│ │ ├── README.md
│ │ ├── start
│ │ └── stop
│ ├── cron
│ │ ├── README.cron
│ │ ├── daily
│ │ │ └── .gitignore
│ │ ├── hourly
│ │ │ └── .gitignore
│ │ ├── minutely
│ │ │ └── .gitignore
│ │ ├── monthly
│ │ │ └── .gitignore
│ │ └── weekly
│ │ ├── README
│ │ ├── chrono.dat
│ │ ├── chronograph
│ │ ├── jobs.allow
│ │ └── jobs.deny
│ └── markers
│ └── README.md
├── README.md
├── diy
│ ├── index.html
│ └── testrubyserver.rb
└── misc
└── .gitkeep
The diy subdirectory contains the sample, we ignore that. What we need to adjust later are scripts in the .openshift/action_hooks directory. And of course we need to add our source code for the service.
Create the Spring-Boot REST service
With the help of the Spring Boot Initializr (which I use from within IntelliJ, but the jar created on the Website is quite the same) I create a project that just has the Web/Web component added and where the Java version is set to 1.8. The important thing here is that the project is created in the sayservice directory so that the project files are added to the existing directory. After adding my standard .gitignore
file, the directory contains the following data (not showing the contents of the .openshift directory again and skipping IntelliJ files):
sayservice
├── .git
├── .gitignore
├── .openshift
├── README.md
├── diy
├── misc
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── sothawo
│ │ └── sayservice
│ │ └── SayserviceApplication.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── com
└── sothawo
└── sayservice
└── SayserviceApplicationTests.java
The following listing shows the pom.xml, notice the explicit setting of the java version to 1.8:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sothawo</groupId>
<artifactId>sayservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sayservice</name>
<description>Demo project for Spring Boot REST service on OpenShift</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Add the Service implementation
At the moment we have an application that has not yet a service defined, so we add the following Sayservice
class:
/**
* Copyright (c) 2015 sothawo
*
*
*/
package com.sothawo.sayservice;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Sample Service.
*
* @author P.J. Meisch (pj.meisch@sothawo.com).
*/
@RestController
@RequestMapping("/")
public class Sayservice {
@RequestMapping(value = "/say/{in}", method = RequestMethod.GET)
public String echo(@PathVariable(value = "in") final String in) {
return "you said: " + in;
}
}
After creating and running the application with
mvn package && java -jar target/*.jar
you can access and test it:
curl http://localhost:8080/say/hello
you said: hello
Create an OpenShift build script to install Java8 and build the application
The following script named build must be put in the .openshift/action_hooks directory (it must be executable):
#!/bin/bash
# define some variables for JDK 8
JDK_TGZ=jdk-8u60-linux-i586.tar.gz
JDK_URL=http://download.oracle.com/otn-pub/java/jdk/8u60-b27/$JDK_TGZ
JDK_DIR=jdk1.8.0_60
JDK_LINK=jdk1.8
# download JDK1.8 to the data directory if it does not yet exist, extract it and create a symlink
cd ${OPENSHIFT_DATA_DIR}
if [[ ! -d $JDK_DIR ]]
then
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" $JDK_URL
tar -zxf $JDK_TGZ
rm -fr $JDK_TGZ
rm $JDK_LINK
ln -s $JDK_DIR $JDK_LINK
fi
# export environment vriables
export JAVA_HOME="$OPENSHIFT_DATA_DIR/$JDK_LINK"
export PATH=$JAVA_HOME/bin:$PATH
# call our own mvn script with the right settings
cd $OPENSHIFT_REPO_DIR
./.openshift/mvn package -s .openshift/settings.xml -DskipTests=true
The script downloads the Oracle JDK if it is not yet available and extracts it to the OPENSHIFT_DATA_DIR
directory.
The next thing to adjust is the mvn
script. The one that’s available in the DIY cartridge resets JAVA_HOME
, so I put the following mvn
script in the .openshift directory:
#!/bin/sh
prog=$(basename $0)
export JAVACMD=$JAVA_HOME/bin/java
export M2_HOME=/usr/share/java/apache-maven-3.0.4
exec $M2_HOME/bin/$prog "$@"
As an alternative you might add a download of maven to the build script.
The last needed file for the build is the settings.xml, which I also put into the .openshift directory (Edit 2015-10-04: fixed variable with {} and added /.m2/repository):
<settings>
<localRepository>${OPENSHIFT_DATA_DIR}/.m2/repository</localRepository>
</settings>
Set up start and stop scripts
Replace the files start and stop in the .openshift/action_hooks directory with the following ones (they must be executable):
start
#!/bin/bash
# The logic to start up your application should be put in this
# script. The application will work only if it binds to
# $OPENSHIFT_DIY_IP:8080
JDK_LINK=jdk1.8
export JAVA_HOME="$OPENSHIFT_DATA_DIR/$JDK_LINK"
export PATH=$JAVA_HOME/bin:$PATH
cd $OPENSHIFT_REPO_DIR
nohup java -jar target/*.jar --server.port=${OPENSHIFT_DIY_PORT} --server.address=${OPENSHIFT_DIY_IP} &
stop
#!/bin/bash
source $OPENSHIFT_CARTRIDGE_SDK_BASH
# The logic to stop your application should be put in this script.
PID=$(ps -ef | grep java.*\.jar | grep -v grep | awk '{ print $2 }')
if [ -z "$PID" ]
then
client_result "Application is already stopped"
else
kill $PID
fi
Deploy and run the application
After committing your files to git a final
git push
will upload all your changes to OpenShift, build and start the application. Check it out by calling
curl http://sayservice-yourdomain.rhcloud.com/say/it-works
and getting the answer
you said: it-works
So much for my first post concerning OpenShift.