End to End Java Spring Boot CP4Apps workflow

Recently updated to include RedHat OpenShift 4.x

Prerequisites

Add Stacks to appsody

  • From the Cloud Pak for Applications landing page get the Appsody URL, for example:

  • Use the appsody CLI to add add the URL for your stack configuration file:

appsody repo add kabanero https://github.com/kabanero-io/collections/releases/download/0.3.5/kabanero-index.yaml
  • If possible, remove other repos that are existing.

To get the list of available repos, run this command.

appsody repo list

This returns you something like below.

$ appsody repo list

NAME            URL
*incubator      https://github.com/appsody/stacks/releases/latest/download/incubator-index.yaml
experimental    https://github.com/appsody/stacks/releases/latest/download/experimental-index.yaml
kabanero        https://github.com/kabanero-io/kabanero-stack-hub/releases/download/0.6.3/kabanero-stack-hub-index.yaml

To remove the unused repos, run

appsody repo remove <NAME>

For instance, if you want to remove appsodyhub, then it will be appsody repo remove appsodyhub.

  • List the appsody stacks available in the Collection:

appsody list kabanero

It gives you the list of available stacks.

$ appsody list kabanero

REPO    	ID               	VERSION  	TEMPLATES        	DESCRIPTION
kabanero	java-microprofile	0.2.26   	*default         	Eclipse MicroProfile on Open Liberty & OpenJ9 using Maven
kabanero	java-openliberty 	0.2.3    	*default         	Open Liberty & OpenJ9 using Maven
kabanero	java-spring-boot2	0.3.24   	*default, kotlin 	Spring Boot using OpenJ9 and Maven
kabanero	nodejs           	0.3.3    	*simple          	Runtime for Node.js applications
kabanero	nodejs-express   	0.2.10   	scaffold, *simple	Express web framework for Node.js
  • Set the kabanero repo as default.

appsody repo set-default kabanero

If you want to customize the appsody stacks and extend them, refer to Customizing application stacks.

Appsody application

Create a new Application

  • Create a new directory for the project and change directory into it.

mkdir appsody_sample_springboot

cd appsody_sample_springboot/
  • Initialize the project using appsody init by selecting the desired stack ID template simple.

appsody init kabanero/java-spring-boot2
  • The directory contains a minimal set of artifacts

.
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── application
    │   │       ├── LivenessEndpoint.java
    │   │       └── Main.java
    │   └── resources
    │       ├── application.properties
    │       └── public
    │           └── index.html
    └── test
        └── java
            └── application
                └── MainTests.java
Main.java

Spring Application class

LivenessEndpoint.java

An example Liveness Endpoint

MainTests.java

A simple test class

application.properties

Containing some configuration options for Spring

index.html

A static file

pom.xml

The project build file

Build the application

This command will locally build a docker image of your appsody project.

appsody build

Once it builds successfully, you will see something like this.

[Docker] Step 71/71 : LABEL version=0.3.23
[Docker]  ---> Running in 4818208fe3ab
[Docker] Removing intermediate container 4818208fe3ab
[Docker]  ---> c5ec2b8a9377
[Docker] Successfully built c5ec2b8a9377
[Docker] Successfully tagged dev.local/appsody-sample-springboot:latest
Built docker image dev.local/appsody-sample-springboot
Running command: docker create --name appsody-sample-springboot-extract docker.io/kabanero/java-spring-boot2:0.3

It helps you to check that stack is stable and init is done correctly. You do not need to run build directly ever again.

Test the Application

  • Test the application using appsody

appsody test

This step is building a container and running the test command inside of it.

[Container] [INFO] ------------------------------------------------------------------------
[Container] [INFO] BUILD SUCCESS
[Container] [INFO] ------------------------------------------------------------------------
[Container] [INFO] Total time:  01:01 min
[Container] [INFO] Finished at: 2020-04-12T03:09:06Z
[Container] [INFO] ------------------------------------------------------------------------

Run the Application

  • Run the application using appsody

appsody run

This step is building a container and running it, the output has the endpoint for the application.

Running development environment...
Running command: docker[pull kabanero/java-spring-boot2:0.3]
Running docker command: docker[run --rm -p 5005:5005 -p 8080:8080 -p 35729:35729 --name appsody-sample-springboot-dev -u 501:20 -e APPSODY_USER=501 -e APPSODY_GROUP=20 -v /Users/<user>@ibm.com/kabanero101/appsody_sample_springboot/.:/project/user-app -v /Users/<user>@ibm.com/.m2/repository:/mvn/repository -v /Users/<user>@ibm.com/.appsody/appsody-controller:/appsody/appsody-controller -t --entrypoint /appsody/appsody-controller kabanero/java-spring-boot2:0.3 --mode=run]
......
......
......
[Container] 2019-09-12 17:49:22.173  INFO 185 --- [  restartedMain] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 4 endpoint(s) beneath base path '/actuator'
[Container] 2019-09-12 17:49:22.377  INFO 185 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
[Container] 2019-09-12 17:49:22.386  INFO 185 --- [  restartedMain] application.Main                         : Started Main in 7.984 seconds (JVM running for 9.679)
[Container] 2019-09-12 17:58:42.777  INFO 185 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
[Container] 2019-09-12 17:58:42.777  INFO 185 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
[Container] 2019-09-12 17:58:42.805  INFO 185 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 27 ms
[Container] 2019-09-12 17:58:43.044  INFO 185 --- [nio-8080-exec-1] i.j.internal.reporters.LoggingReporter   : Span reported: 445d02b19cea491:445d02b19cea491:0:1 - GET

For more details, refer Spring® Boot 2 Stack.

Stop the Application

  • To stop the application container, run this command.

appsody stop
  • Alternatively, you can also press Ctrl+C.

Update the Application

  • Lets add a new endpoint API to our Application

    Add the following file src/main/java/application/ExampleEndpoint.java and populate with the following code:

    package application;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class ExampleEndpoint {
    
        @RequestMapping("/starter/hello")
        public String example() {
            return "Hello World";
        }
    }
sb lab1 code change
sb lab1 hello endpoint
  • Make a change to the application and refresh the browser for example Hello World Demo

sb lab1 hello refresh

Debug the Application

  • Open your editor. We are using VS Code. Add the project to your workspace, or use the command code .

sb lab1 open project vscode
  • Open a new terminal window inside VS Code. Use View→Terminal.

sb lab1 open terminal
  • To debug the application including reloading the application on code changes run the below command.

appsody debug

The output indicates the debug environment is being used.

Running debug environment
Running command: docker[pull kabanero/java-spring-boot2:0.3]
Running docker command: docker[run --rm -p 35729:35729 -p 5005:5005 -p 8080:8080 --name appsody-sample-springboot-dev -u 501:20 -e APPSODY_USER=501 -e APPSODY_GROUP=20 -v /Users/<user>@ibm.com/kabanero101/appsody_sample_springboot/.:/project/user-app -v /Users/<user>@ibm.com/.m2/repository:/mvn/repository -v /Users/<user>@ibm.com/.appsody/appsody-controller:/appsody/appsody-controller -t --entrypoint /appsody/appsody-controller kabanero/java-spring-boot2:0.3 --mode=debug]
.......
.......
.......
[Container] [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ application ---
[Container] [INFO] Changes detected - recompiling the module!
[Container] [INFO] Compiling 1 source file to /project/user-app/target/test-classes
[Container] [INFO]
[Container] [INFO] <<< spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) < test-compile @ application <<<
[Container] [INFO]
[Container] [INFO]
[Container] [INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) @ application ---
[Container] [INFO] Attaching agents: []
[Container] Listening for transport dt_socket at address: 5005
  • You can attach to the debugger using VSCode

  • To access the debug view use View→Debug or click Debug icon on left menu

sb lab1 vscode debug
  • Add a breakpoint to the application, click to the left of the line number

sb lab1 vscode breakpoint
  • Click on the debug task Appsody: Attach java debugger

sb lab1 vscode attach
sb lab1 vscode attach break

Appsody tasks on VS Code

  • To access the build tasks on VS code, go to

Terminal > Run Build Task...
sb lab1 build task menu
  • You will see a list of available tasks.

sb lab1 build task list
  • Click on Appsody: run and this will run the application.

sb lab1 build task run
sb lab1 build task run app

Codewind on VS Code

Codewind simplifies and enhances development in containers by extending industry standard IDEs with features to write, debug, and deploy cloud-native applications. It helps you to get started quickly with templates or samples, or you can also pull in your applications and let Codewind get them cloud ready.

Codewind supports VS Code, Eclipse Che, and Eclipse. In this lab, we are using VS Code as our IDE.

Getting the Codewind extension

  • Go to the extensions view and search for codewind from the VS code market place.

sb lab1 vscode codewind extension

You will find Codewind then click install to get it. Also, if you want to use Codewind for Node.js performance analysis, you need to install Codewind Node.js Profiler.

  • Once you get them installed, let us now open the Codewind in the IDE.

View > Open View...
sb lab1 vscode view
  • It gives you you a list of options. Select Codewind.

sb lab1 vscode code explorer
  • This opens the Codewind.

sb lab1 vscode codewind explorer

If you want to configure codewind with appsody, checkout Configure CodeWind with Appsody CLI.

Adding the application

  • You can create a new project or add an existing project to Codewind. Since, we already created one using appsody earlier, let us add the existing project.

  • Right click on Projects under Codewind. Select Add Existing Project in the menu.

sb lab1 codewind add existing project
  • From the codewind workspace, select the project you created earlier.

sb lab1 add existing prj from workspace
  • The codewind extension asks you for confirmation as follows. Click Yes.

sb lab1 appsody extension
  • The project will be added.

sb lab1 appsody project
  • Once it is successfully build, it starts running.

sb lab1 appsody project running

Project Options

  • Go to the application and right click on it to access the various options available.

sb lab1 code wind project options
  • Click Open App to access the application.

sb lab1 codewind open app
- Codewind exposes your applications on different external ports. This will allow you to run multiple projects of same type.
  • To get the overview of your project, click on Open Project Overview.

sb lab1 codewind project overview
  • You can access the container shell directly from the IDE by using Open Container Shell.

sb lab1 codewind container shell
  • To access the logs of the application, click on Show all logs.

sb lab1 codewind project logs
  • You can also hide the logs if you want to by using Hide all logs option.

  • If you have multiple applications and want to manage the logs for them, you can use Manage logs.

  • You can also run the application by using Restart in Run Mode.

sb lab1 codewind project restart in run mode

Once it is restarted, you can access the application by clicking on the button as shown below.

sb lab1 restart in run mode app
  • Similarly, you can also do debugging by using Restart in Debug Mode.

Application Performance, Monitor, Profiling with Codewind

  • You can launch the app monitor by selecting Open Appplication Monitor

sb lab1 code wind project options
  • Click Open Application Monitor to access the application monitor.

Enabling Javametrics

For Springboot project, you need to set up the Javametrics before accessing the application monitor.

- For editing these files, you can use the option Add Folder to Workspace and open it in your VS code editor.
  • To the sample project you created earlier, add the below annotation.

@ComponentScan(basePackages = {"com.ibm.javametrics.spring", "<your_package_name>"})

For the sample application, your main class will be as follows.

package application;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.ibm.javametrics.spring", "application"})
public class Main {

	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}

}
  • Add the below dependencies to your pom.xml.

<dependency>
    <groupId>com.ibm.runtimetools</groupId>
    <artifactId>javametrics-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>com.ibm.runtimetools</groupId>
    <artifactId>javametrics-agent</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.0.4</version>
</dependency>
  • Now, restart your application using the option Restart in Run Mode.

sb lab1 code wind perf restart app
  • Once the application is restarted, you can access the application monitor and performance dashboard.

  • Click on the Open Application Monitor to access the java metrics dashboard.

sb lab1 code wind perf app monitor
sb lab1 code wind perf app monitor summary
  • To open the performance dashboard, click on Open Performance Dashboard.

    • Access the dashboard and then run the test.

sb lab1 code wind perf dashboard
  • Create a load test as follows. Then refresh your application to create load.

sb lab1 code wind perf load test create
  • Run the test.

sb lab1 code wind perf load test run
  • You will see something like below after the first test.

sb lab1 code wind perf load test one
  • Run couple of tests and you should be able to see the results.

sb lab1 code wind perf load test two
sb lab1 code wind perf load test four
  • If you scroll down, you can also see the test history details.

sb lab1 code wind perf load test history
- In codewind, for now the Application Monitor and Performance Dashboard shows up on first run. It does not load the metrics if you reload the dashboard.

Deploy the appsody application on Openshift for team development

Set up team project namespace

  • Create a new project for your team if it does not exist. Or if you have an existing project, skip this step.

oc new-project <yournamespace>

Once you create it, you will see something like below.

$ oc new-project kabanero-samples
Now using project "kabanero-samples" on server "https://c100-e.us-east.containers.cloud.ibm.com:31718".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app centos/ruby-25-centos7~https://github.com/sclorg/ruby-ex.git

to build a new example application in Ruby.
  • Switch to the target project using the below command.

oc project <yournamespace>

It gives you the below message if you are already in that space.

$ oc project kabanero-samples
Already on project "kabanero-samples" on server "https://c100-e.us-east.containers.cloud.ibm.com:31718".
  • Check that the current context is your team’s project space.

oc project -q

You will see something like below.

$ oc project -q
kabanero-samples

Add new Target Namespace

  • In order to deploy you app to the new project (kabanero-samples), perform the following:

  • First check that you have kabanero available

$ oc get kabaneros -n kabanero
NAME       AGE   VERSION   READY
kabanero   44h   0.6.1     True
  • Next, we will have to edit the yaml file configuring kabanero with the following command.

  • This will bring up your default editor

$ oc edit kabanero kabanero -n kabanero
  • Finally, navigate to the spec label within the file and add the following targetNamespaces label.

spec:
  targetNamespaces:
    - kabanero-samples

Create application deployment manifest

  • Extract the appsody deployment config file

appsody build

This will generate the file app-deploy.yaml with the following content:

apiVersion: appsody.dev/v1beta1
kind: AppsodyApplication
metadata:
  name: appsody-sample-springboot
spec:
  # Add fields here
  version: 1.0.0
  applicationImage: appsody-sample-springboot
  stack: java-spring-boot2
  service:
    type: NodePort
    port: 8080
    annotations:
      prometheus.io/scrape: 'true'
      prometheus.io/path: '/actuator/prometheus'
  readinessProbe:
    failureThreshold: 12
    httpGet:
      path: /actuator/health
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 2
  livenessProbe:
    failureThreshold: 12
    httpGet:
      path: /actuator/liveness
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 2
  expose: true

By default, the application is deployed in the kabanero namespace. If you want to deploy the application in a different namespace, you can specify it in this yaml file. In this lab, let us use a namespace called kabanero-samples and we can specify it under the metadata as below.

apiVersion: appsody.dev/v1beta1
kind: AppsodyApplication
metadata:
  name: appsody-sample-springboot
  namespace: kabanero-samples
spec:
  # Add fields here
  version: 1.0.0
  applicationImage: appsody-sample-springboot
  stack: java-spring-boot2
  service:
    type: NodePort
    port: 8080
    annotations:
      prometheus.io/scrape: 'true'
      prometheus.io/path: '/actuator/prometheus'
  readinessProbe:
    failureThreshold: 12
    httpGet:
      path: /actuator/health
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 2
  livenessProbe:
    failureThreshold: 12
    httpGet:
      path: /actuator/liveness
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 2
  expose: true

Creating a git repo

  • Setup your git locally with the content of the application.

git init
git add .
git commit -m "initial commit"
  • Create a github repository and push the code to the remote repository.

git remote add origin $GITHUB_REPOSITORY_URL
git push -u origin master

Create an access token

  • Go to Github Settings.

  • Select Developer settings.

  • Click on Personal access tokens.

  • Select Generate new token.

  • Create a Github access token with permission admin:repo_hook

sb lab1 github token
  • Then finally click Generate token to create one.

For more details on how to generate Github personal access token refer Creating a personal access token.

Configure the DevOps Pipeline

Accessing Tekton dashboard

  • Navigate to the menu in the upper left and select instances.

  • Then click the link under the Tekton label to navigate to the Tekton Dashboard.

tekton link 42
sb lab1 tekton dashboard

Create Tekton webhook for git repo

  • Click on Webhooks in the menu.

  • Click on Add Webhook.

  • Enter the information for the Webhook settings.

sb lab1 webhook settings
Name - <Name for webhook>
Repository URL - <Your github repository URL>
Access Token - <For this, you need to create a Github access token with permission `admin:repo_hook` or select one from the list>

For more information on how to configure github credentials, refer to Tekton - Configure GitHub Credentials.

  • Create a new token as follows.

sb lab1 webhook settings access token create
  • You can also use an existing token if it is already created.

sb lab1 webhook settings access token existing

Set up the pipeline

  • Enter the information for the Pipeline settings NOTE: Replace <your_project> with the name of the target namespace in our case kabanero-samples

Namespace - kabanero
Pipeline - java-spring-boot2-build-deploy-pipeline
Service account - kabaner-operator
OCP 3.11 - Docker Registry - docker-registry.default.svc:5000/<your_project>
OCP 4.X - Docker Registry - image-registry.openshift-image-registry.svc:5000/<your_project>
sb lab1 pipeline settings

Here, we are using the default docker registry that comes with the openshift cluster. Sometimes, you may need to configure a third party registry instead of using the default one. In order to do that, check out Tekton - Configure External Docker Registry.

  • Click Create, a new webhook is created.

sb lab1 webhook

Also, a new Gitub webhook is created on the project repository.

You can verify it by going into your github repository > Settings > Webhooks and you should be able to see the webhook created.

Replace <your_project> with the name of the target namespace in our case kabanero-samples

Here, we are using the default pipeline. If you want to customize the pipeline, refer to Creating and updating tasks and pipelines.

Deploy the Application

The way to deploy the application is to make a change in the application in the git repository to trigger the tekton webhook and start the DevOps pipeline to build and deploy the application.

  • Make a change to the application such as changing the index.html or any other things.

Let us change the title from Hello from Appsody! to Hello from Cloud Paks !!!.

  • Push your changes to the remote git repository.

  • This will trigger the Tekton Pipeline. To see the status of the Pipeline click on PipelineRuns on the menu of the dashboard.

sb lab1 pipeline runs
  • When the application is built and deployed the application will be available via the expose Route.

OCP 3.11 Instructions

  • Go to the OpenShift Console, switch to the project, and select Applications > Routes

You will see a route for your application, click on the url to open your application.

sb lab1 application route

OCP 4.X instructions

  • Open the menu in the upper left of the OpenShift Console and navigate to Topology.

  • Select your Application in the center, and view the route(s) on the side.

  • Note: Please ensure you are in the developer role (should be default if following this guide)

dc springboot route
  • Or you can also get the route from the oc CLI.

oc get route -n <your_project>

For instance,

$ oc get routes -n kabanero-samples
NAME                        HOST/PORT                                                                                                                                PATH      SERVICES                    PORT      TERMINATION   WILDCARD
appsody-sample-springboot   appsody-sample-springboot-kabanero-samples.ocp.example.com            appsody-sample-springboot   8080                    None

You can now acccess the application at <HOST/PORT>, here it is appsody-sample-springboot-kabanero-samples.csantana-ocp3-fa9ee67c9ab6a7791435450358e564cc-0001.us-east.containers.appdomain.cloud.

Serverless autoscaling the Application with Knative

Add the target namespace to ServiceMeshMemberRoll

  • Edit the resource smmr :

oc edit smmr -n knative-serving-ingress
  • Append the target namespace into the SMMR resource spec for example kabanero-samples leave any other namespaces already present:

spec:
  members:
  - knative-serving
  - kabanero-samples
  • Verify the namespace kabanero-samples was added by following this command:

oc get smmr default -n knative-serving-ingress -o jsonpath={.spec.members}

[knative-serving kabanero-samples]

Update the Application for Serverless

  • Edit the file app-deploy.yaml.

  • Add the line createKnativeService: true to the spec object.

apiVersion: appsody.dev/v1beta1
kind: AppsodyApplication
metadata:
  name: appsody-sample-springboot
  namespace: kabanero-samples
spec:
  createKnativeService: true
  • Git push the change, and see tekton pipeline runs again.

  • Show the Knative resource

oc get service.serving.knative.dev/appsody-sample-springboot

NAME                            URL                                                                                                                                            LATESTCREATED                         LATESTREADY                           READY     REASON
appsody-sample-springboot   http://appsody-sample-springboot.kabanero-samples.ocp.example.com  appsody-sample-springboot-mtl4q   appsody-sample-springboot-mtl4q   True
  • Show the Knative route

oc get route.serving.knative.dev/appsody-sample-springboot

NAME                            URL                                                                                                                                            READY     REASON
appsody-sample-springboot   http://appsody-sample-springboot.kabanero-samples.ocp.example.com  True
  • Show the Knative configuration

oc get configuration.serving.knative.dev/appsody-sample-springboot

NAME                            LATESTCREATED                         LATESTREADY                           READY     REASON
appsody-sample-springboot   appsody-sample-springboot-mtl4q   appsody-sample-springboot-mtl4q   True
  • Show the Knative latest ready revision

oc get revision.serving.knative.dev/appsody-sample-springboot-mtl4q

NAME                                  SERVICE NAME                          GENERATION   READY     REASON
appsody-sample-springboot-mtl4q   appsody-sample-springboot-mtl4q   2            True
oc get pods

NAME                                                              READY     STATUS    RESTARTS   AGE
appsody-sample-springboot-mtl4q-deployment-7bf6dbddf6-rr89p   2/2       Running   0          27s
  • Wait 1 minute and you will then see the pods are not longer running

oc get pods

No resources found.