Spring Boot Application in OpenShift / OKD
tl;dr: Now that we have packaged an existing Spring Boot application into a Docker Image, we can deploy it to a Kubernet cluster as well.
In this example the additional features of OpenShift/OKD are used to enable a continuous deployment of the application.
Kubernetes has proven to be one of the best ways to orchestrate dockerized applications.
So the next logical step after packaging a Spring Boot application in a Docker Image is to deploy it in a cluster and enjoy the benefits of Kubernetes.
The following example is based on the RedHat Kubernetes distribution OpenShift1, which is also available as OpenSource version OKD2. To be able to follow the example without an existing OpenShift/OKD installation, you can also use MiniShift3, which is based on a minimal OKD installation in a local virtual machine.
First you should setup and configure the
oc Client tool4 which allows us to create, modify and delete resources in our cluster.
The most important commands for now are:
# Login oc login http://URL-OF-CLUSTER # Create a new Resource from a file oc create -f CONFIG-FILE.yml # Update the Resource from a file oc apply -f CONFIG-FILE.yml # Delete a Resource defined in a file oc delete -f CONFIG-FILE.yml
Kubernetes runs images in
Pods, which in turn are part of a
Deployments. In contrast to other Kubernetes distributions, OpenShift uses
DeploymentConfig5 which provides some additional features, more about this later.
The following yml file describes a
DeploymentConfig which starts two instances (pods) of the
spring-petclinic image and forwards port 8080.
apiVersion: "apps.openshift.io/v1" kind: "DeploymentConfig" metadata: labels: app: "spring-petclinic" name: "spring-petclinic" spec: replicas: 2 selector: app: "spring-petclinic" deploymentconfig: "spring-petclinic" strategy: type: "Rolling" template: metadata: labels: app: "spring-petclinic" deploymentconfig: "spring-petclinic" spec: containers: - name: "spring-petclinic" image: "foobar/spring-petclinic:latest" imagePullPolicy: "Always" ports: - containerPort: 8080 protocol: "TCP" triggers: - type: "ConfigChange"
After creating the
DeploymentConfig in OpenShift, Kubernetes starts the two defined pods and we can also see their logs in the WebUI.
oc create -f petclinic-deployment.yml deploymentconfig.apps.openshift.io/spring-petclinic created
Kubernetes can use Liveness and Readiness Probes6 to check whether a Pod has crashed or is unable to accept requests. The definition takes place in the
containers block of
DeploymentConfig. For both probes there are the following possibilities:
- HTTP Check
- Execution of a command in the Container
- TCP Socket Check
For Spring Boot applications, the Health endpoint7 can be used as a LivenessProbe. This actuator endpoint must be activated in the application and can then be included in the config. A TCP socket check can then be used to see if the Pod is already started and can accept further requests.
spec: containers: - name: spring-petclinic ... livenessProbe: initialDelaySeconds: 60 timeoutSeconds: 2 httpGet: path: /actuator/health port: 8080 scheme: HTTP readinessProbe: initialDelaySeconds: 30 timeoutSeconds: 1 tcpSocket: port: 8080
DeploymentConfigwithout activating the
/actuator/healthendpoint. The pod should now be restarted after a short time automatically.
This is visible in the WebUI in the Deployment list.
oc apply -f petclinic-deployment.yml
Service and Routes
Currently our Pods are only reachable by other Pods which know the hostnames but not from an external address.
First we will now combine all Pods into a
Service8 which provides a load-balancing over all live Pods. Afterwards this Service can be exposed as a
Route9 which will receive a hostname that is reachable from our network.
You can decide, if you want to define all those Resources in separate files or use a single
yml file and divide them using
---. I usually tend to create a central file with all Resources related to a Container.
kind: "Service" apiVersion: "v1" metadata: name: "spring-petclinic-service" spec: ports: - port: 8080 protocol: TCP selector: app: "spring-petclinic" --- kind: Route apiVersion: route.openshift.io/v1 metadata: name: "spring-petclinic-route" spec: to: kind: Service name: spring-petclinic-service
Now again apply the updated config and check the WebUI for the new Route.
If your DNS is configured correctly, you should now be able to reach the
petclinic using the given hostname. Kubernetes will take care of the internal routing and also load-balance over the available Pods.
You might have noticed the declaration of
triggers inside the
DeploymentConfig. Those take care to deploy new Pods if any trigger is executed. Currently only configuration changes will cause a new Deployment but there is another quite important trigger available:
This trigger can be used to make sure, that the Pods always run the latest version of the used image and is therefor quite cool for continuous deployments.
ImageChange monitors an
ImageStream which is basically a reference to a Docker Image. By default OpenShift performs a check every 15 minutes, but this can also be configured cluster-wide11.
kind: ImageStream apiVersion: image.openshift.io/v1 metadata: name: "spring-petclinic-imagestream" spec: tags: - from: kind: DockerImage name: "foobar/spring-petclinic:latest" name: "latest" importPolicy: scheduled: true
spec: containers: - name: spring-petclinic ... image: docker-registry.default.svc:5000/sandbox/spring-petclinic-imagestream triggers: - type: ConfigChange - type: "ImageChange" imageChangeParams: automatic: true from: kind: "ImageStreamTag" name: "spring-petclinic-imagestream:latest" containerNames: - "spring-petclinic"
Dockerize Spring Boot ApplicationsIt’s quite easy to run a Spring Boot Application inside a Docker Container. Here, however, some pitfalls should be considered so that you can draw the maximum benefits from this.
Kubernetes Logging with fluentd and logz.ioBy using logz.io, it is relatively easy to outsource Kubernetes logfiles that do not contain sensitive data to an external service and analyze them there with Kibana.
Setup a Kubernetes Cluster with AnsibleAlthough all large Cloud provider nowadays offer Managed Kubernetes Clusters, I prefer to have access to a local cluster especially during development. In this post, we will setup a Kubernetes Cluster using Ansible and Kubeadm. The cluster will include a single master node and two (or more) worker nodes. Most of the work done here is based on a tutorial by bsder1. Requirements I will use three Ubuntu 18.04 LTS (Bionic Beaver) servers, each with 4GB RAM and 2 CPUs, you should also be fine with 1GB RAM.
Autoscaling GitLab Runner Instances on Google Cloud PlatformMigrating GitLab CI jobs to Google Cloud Platform is possible with little effort due to the good support provided by GitLab and relieves the load off your hardware.
This can be worthwhile even for small projects or private GitLab instances without generating major costs.