Spring Boot & Kubernetes
JKube
Spring Boot project offers several options out-of-the-box for containerizing an application. All of them, might fit your requirements but they just cover one part of the equation, that is creating a continer, but it isn’t implements the creation of the Kubernetes resources nor the deployment of them.
Eclipse JKube is a collection of plugins and libraries that are used for building container images using Docker, JIB or S2I build strategies. Eclipse JKube generates and deploys Kubernetes/OpenShift manifests at compile time too.
Open pom.xml
file and add the following properties at <properties>
section:
<jkube.build.strategy>jib</jkube.build.strategy>
<jkube.generator.name>quay.io/sunix/sb-hw:${project.version}</jkube.generator.name>
<jkube.createExternalUrls>true</jkube.createExternalUrls>
Substitute quay.io for your container registry and sunix with your username.
|
Then add the jkube
plugin at plugins
section just after the spring-boot-maven-plugin
plugin:
<plugin>
<groupId>org.eclipse.jkube</groupId>
<artifactId>kubernetes-maven-plugin</artifactId>
<version>1.9.1</version>
<configuration>
<resources>
<imagePullPolicy>Always</imagePullPolicy> (1)
</resources>
</configuration>
</plugin>
1 | Configures imagePullPolicy attribute |
Login into container registry running:
docker login quay.io
You can set username/password in the jkube project or in .m2/settings.xml , but if not then docker login session is used. See https://www.eclipse.org/jkube/docs/kubernetes-maven-plugin#authentication.
|
Building & Pushing
To build the Linux container image and push it to container registry run the following Maven goals:
./mvnw package -DskipTests k8s:build k8s:push
[INFO] k8s: Running in Kubernetes mode
[INFO] k8s: Building Docker image in Kubernetes mode
[INFO] k8s: Running generator spring-boot
[INFO] k8s: spring-boot: Using Docker image quay.io/jkube/jkube-java:0.0.15 as base / builder
[INFO] k8s: JIB image build started
JIB> Base image 'quay.io/jkube/jkube-java:0.0.15' does not use a specific image digest - build may not be reproducible
JIB> Containerizing application with the following files:
JIB> Jkube-generated-layer-original:
JIB> /home/sunix/github/sunix/hello-kubernetes/target/hello-kubernetes-0.0.1-SNAPSHOT.jar
JIB> Getting manifest for base image quay.io/jkube/jkube-java:0.0.15...
JIB> Building jkube-generated-layer-original layer...
JIB> Using base image with digest: sha256:50ba176b6e37993a028732b713d6f239597fe124ad7dc81c5abe05b8ab5b2814
JIB> Container program arguments set to [/usr/local/s2i/run] (inherited from base image)
JIB> Building image to tar file...
JIB> [======================== ] 80.0% complete > writing to tar file
JIB> [==============================] 100.0% complete
[INFO] k8s: /home/sunix/github/sunix/hello-kubernetes/target/docker/quay.io/sunix/sb-hw/0.0.1-SNAPSHOT/tmp/docker-build.tar successfully built
[INFO]
[INFO] --- kubernetes-maven-plugin:1.8.0:push (default-cli) @ hello-kubernetes ---
[INFO] k8s: Running in Kubernetes mode
[INFO] k8s: Building Docker image in Kubernetes mode
[INFO] k8s: Running generator spring-boot
[INFO] k8s: spring-boot: Using Docker image quay.io/jkube/jkube-java:0.0.15 as base / builder
[INFO] k8s: This push refers to: quay.io/sunix/sb-hw:0.0.1-SNAPSHOT
JIB> Containerizing application with the following files:
JIB> Container program arguments set to [/usr/local/s2i/run] (inherited from base image)
JIB> Building a single manifest
JIB> Checking existence of manifest for sha256:03d2b11cfdd191a2fa22f6c10eba3b14012ecb1f6e7d0e51f97553f90bee61d7...
JIB> Skipping manifest existence check; system property set to false
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:38b71301a1d9df24c98b5a5ee8515404f42c929003ad8b13ab83d2de7de34dec, size: 1742
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:4c502bd732043bc253180c5af6b13feeaaaebf4872ad2703c3c092e4d8bf5cef, size: 123456655
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:a9e23b64ace00a199db21d302292b434e9d3956d79319d958ecc19603d00c946, size: 39622437
JIB> Pushing manifest for 0.0.1-SNAPSHOT...
JIB> [=========================== ] 90.9% complete > launching manifest list pushers
JIB> [==============================] 100.0% complete
JIB> Containerizing application with the following files:
JIB> Container program arguments set to [/usr/local/s2i/run] (inherited from base image)
JIB> Building a single manifest
JIB> Checking existence of manifest for sha256:6e3bdcd41a3c43ad81059f4b09f23b80c5234be57800898703d8e679e14f3d52...
JIB> Skipping manifest existence check; system property set to false
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:38b71301a1d9df24c98b5a5ee8515404f42c929003ad8b13ab83d2de7de34dec, size: 1742
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:4c502bd732043bc253180c5af6b13feeaaaebf4872ad2703c3c092e4d8bf5cef, size: 123456655
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:a9e23b64ace00a199db21d302292b434e9d3956d79319d958ecc19603d00c946, size: 39622437
JIB> Skipping push; BLOB already exists on target registry : digest: sha256:2170a78ac8f83c9847552d78f95b35ccfc9c2d44cf93805448f29096f493f693, size: 25356703
JIB> Pushing manifest for latest...
JIB> [=========================== ] 90.9% complete > launching manifest list pushers
JIB> [==============================] 100.0% complete
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Kubernetes Resources
JKube can generate an opinionated Kubernetes resources file for a Spring Boot application setting the image name generated in the previous step:
./mvnw k8s:resource -Djkube.domain=$(minikube ip).nip.io
Replace the domain with the one of your cluster
./mvnw k8s:resource -Djkube.domain=apps.sandbox-m2.ll9k.p1.openshiftapps.com
[INFO] --- kubernetes-maven-plugin:1.8.0:resource (default-cli) @ hello-kubernetes ---
[INFO] k8s: spring-boot: Using Docker image quay.io/jkube/jkube-java:0.0.15 as base / builder
[INFO] k8s: Using resource templates from /home/sunix/github/sunix/hello-kubernetes/src/main/jkube (1)
[INFO] k8s: jkube-controller: Adding a default Deployment
[INFO] k8s: jkube-service: Adding a default service 'hello-kubernetes' with ports [8080] (2)
[INFO] k8s: jkube-healthcheck-spring-boot: Adding readiness probe on port 8080, path='/actuator/health', scheme='HTTP', with initial delay 10 seconds (3)
[INFO] k8s: jkube-healthcheck-spring-boot: Adding liveness probe on port 8080, path='/actuator/health', scheme='HTTP', with initial delay 180 seconds
[INFO] k8s: jkube-service-discovery: Using first mentioned service port '8080'
[INFO] k8s: jkube-revision-history: Adding revision history limit to 2
[INFO] ------------------------------------------------------------------------
1 | Customizations can be placed in this directory. |
2 | Port is taken from Spring Boot configuration. |
3 | Since health actuator is registered liveness/readiness probes are set. More about this later. |
The generated Kubernetes file is located at arget/classes/META-INF/jkube
directory:
cat target/classes/META-INF/jkube/kubernetes.yml
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
annotations:
jkube.io/scm-url: https://github.com/spring-projects/spring-boot/hello-kubernetes
prometheus.io/scrape: "true"
jkube.io/scm-tag: HEAD
prometheus.io/path: /metrics
prometheus.io/port: "9779"
labels:
expose: "true"
app: hello-kubernetes
provider: jkube
version: 0.0.1-SNAPSHOT
group: org.acme
name: hello-kubernetes
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: hello-kubernetes
provider: jkube
group: org.acme
- apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
jkube.io/scm-tag: HEAD
jkube.io/scm-url: https://github.com/spring-projects/spring-boot/hello-kubernetes
labels:
app: hello-kubernetes
provider: jkube
version: 0.0.1-SNAPSHOT
group: org.acme
name: hello-kubernetes
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app: hello-kubernetes
provider: jkube
group: org.acme
template:
metadata:
annotations:
jkube.io/scm-tag: HEAD
jkube.io/scm-url: https://github.com/spring-projects/spring-boot/hello-kubernetes
labels:
app: hello-kubernetes
provider: jkube
version: 0.0.1-SNAPSHOT
group: org.acme
name: hello-kubernetes
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
image: quay.io/sunix/sb-hw:0.0.1-SNAPSHOT
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
httpGet:
path: /actuator/health
port: 8080
scheme: HTTP
initialDelaySeconds: 180
successThreshold: 1
name: spring-boot
ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: 9779
name: prometheus
protocol: TCP
- containerPort: 8778
name: jolokia
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /actuator/health
port: 8080
scheme: HTTP
initialDelaySeconds: 10
successThreshold: 1
securityContext:
privileged: false
- apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
jkube.io/scm-tag: HEAD
jkube.io/scm-url: https://github.com/spring-projects/spring-boot/hello-kubernetes
labels:
app: hello-kubernetes
provider: jkube
version: 0.0.1-SNAPSHOT
group: org.acme
name: hello-kubernetes
spec:
rules:
- host: hello-kubernetes.apps.sandbox-m2.ll9k.p1.openshiftapps.com
http:
paths:
- backend:
service:
name: hello-kubernetes
port:
number: 8080
path: /
pathType: ImplementationSpecific
Kubernetes Deploy
The last step is to deploy the application to the Kubernetes cluster.
We can deploy application using kubectl
CLI tool:
kubectl apply -f target/classes/META-INF/jkube/kubernetes.yml
Or we use k8s:apply
goal.
This goal takes the resources created with k8s:resource
goal :
./mvnw k8s:apply