Simplify Spring Boot Deployments on Kubernetes with the Spring Boot Operator
This guide walks you through packaging a Spring Boot application into a Docker image with Jib, installing the Spring Boot Operator on a Kubernetes cluster, deploying a demo app, customizing deployments, handling graceful shutdown, and using node affinity for advanced scheduling.
Hello, I'm DD.
In 2022, are you using Kubernetes? How do you deploy a Spring Boot application to Kubernetes? Below is a detailed guide.
Source: https://qingmu.io/2020/04/08/Spring-Boot-Operator-User-Guide/, author: Qingmu
Deploying Spring Boot applications on Kubernetes can be cumbersome, but the Spring Boot Operator provides a cleaner, simpler experience.
The Spring Boot Operator is built on Kubernetes Custom Resource Definitions (CRDs) to extend the API.
1. Package Docker Image
Before deployment, you need to package your Spring Boot application into a standard Docker image.
Many Maven/Gradle plugins exist for this; here we introduce the new Google open‑source plugin Jib, which is convenient to use.
Note: Images built with Jib set the Java process PID to 1. The Operator sets the Kubernetes ShareProcessNamespace parameter to true (available in v1.10+) to solve this.
We will generate a standard Spring Boot project operator-demo via https://start.spring.io and build the image with Jib.
mvn com.google.cloud.tools:jib-maven-plugin:build \
-Djib.to.auth.username=${{ secrets.MY_USERNAME }} \
-Djib.to.auth.password=${{ secrets.MY_PASSWORD }} \
-Djib.container.jvmFlags=--add-opens,java.base/sun.nio.ch=ALL-UNNAMED \
-Djib.from.image=freemanliu/oprenjre:11.0.5 \
-Dimage=registry.cn-shanghai.aliyuncs.com/qingmuio/operator-demo/operator-demo:v1.0.0After running the command you obtain a Docker image that is pushed to a remote registry.
2. Operator Quick Experience
After building the image, we install the Operator into the Kubernetes cluster.
2.1 Quick Install
This quick install is only for demo purposes.
kubectl apply -f https://raw.githubusercontent.com/goudai/spring-boot-operator/master/manifests/deployment.yamlSuccessful apply prints:
namespace/spring-boot-operator-system created
customresourcedefinition.apiextensions.k8s.io/springbootapplications.springboot.qingmu.io created
role.rbac.authorization.k8s.io/spring-boot-operator-leader-election-role created
clusterrole.rbac.authorization.k8s.io/spring-boot-operator-manager-role created
clusterrole.rbac.authorization.k8s.io/spring-boot-operator-proxy-role created
clusterrole.rbac.authorization.k8s.io/spring-boot-operator-metrics-reader created
rolebinding.rbac.authorization.k8s.io/spring-boot-operator-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/spring-boot-operator-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/spring-boot-operator-proxy-rolebinding created
service/spring-boot-operator-controller-manager-metrics-service created
deployment.apps/spring-boot-operator-controller-manager createdCheck installation status: kubectl get po -n spring-boot-operator-system Successful output:
NAME READY STATUS RESTARTS AGE
spring-boot-operator-controller-manager-7f498596bb-wcwtn 2/2 Running 0 2m15sIf you are learning Spring Boot, check out this free tutorial: http://blog.didispace.com/spring-boot-learning-2x/
2.2 Deploy OperatorDemo Application
After the Operator is installed, we deploy the first application.
First, create a CRD YAML for the Spring Boot Application:
# Demo.yaml
apiVersion: springboot.qingmu.io/v1alpha1
kind: SpringBootApplication
metadata:
name: operator-demo
spec:
springBoot:
version: v1.0.0
# image: registry.cn-shanghai.aliyuncs.com/qingmuio/operator-demo/operator-demo:v1.0.0Notice the image field is omitted; the Operator can publish using only name and version.
Apply the CRD: kubectl apply -f Demo.yaml Console output:
springbootapplication.springboot.qingmu.io/operator-demo created2.3 Validation
Creation succeeded. Verify the pods:
View pod
# kubectl get po | grep operator-demo
operator-demo-7574f4789c-mg58m 1/1 Running 0 76s
operator-demo-7574f4789c-ssr8v 1/1 Running 0 76s
operator-demo-7574f4789c-sznww 1/1 Running 0Check that the PID is not 1 (ShareProcessNamespace is effective):
kubectl exec -it operator-demo-7574f4789c-mg58m bash
root@...# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 02:06 ? 00:00:00 /pause
root 6 0 26 02:06 ? 00:00:09 java --add-opens java.base/sun.nio.ch=ALL-UNNAMED -cp /app/resources:/app/classes:/app/libs/* io.qingmu.operator.operatordemo.Oper...View svc
# kubectl get svc | grep operator-demo
operator-demo ClusterIP 10.101.128.6 <none> 8080/TCP 2m52sAccess the service:
# curl -i http://10.101.128.6:8080
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 9
Date: Wed, 08 Apr 2020 08:45:46 GMT
hello !!!Scale replicas to 1 by editing Demo.yaml and adding the replicas field:
# Demo.yaml
apiVersion: springboot.qingmu.io/v1alpha1
kind: SpringBootApplication
metadata:
name: operator-demo
spec:
springBoot:
version: v1.0.0
replicas: 1Apply the change:
# kubectl apply -f Demo.yaml
springbootapplication.springboot.qingmu.io/operator-demo configuredVerify the pod count:
# kubectl get po | grep operator-demo
operator-demo-7574f4789c-sznww 1/1 Running 0 8m29s2.4 Clean Up operator-demo
Delete the demo pod:
# kubectl delete -f Demo.yaml
springbootapplication.springboot.qingmu.io "operator-demo" deletedConfirm the pod is gone:
# kubectl get po | grep operator-demo3. Deploy Your Own Application
Deploying an application from a private registry requires creating a secret (skip if already created).
kubectl create secret docker-registry aliyun-registry-secret \
--docker-server=registry-vpc.cn-hangzhou.aliyuncs.com \
--docker-username=*** \
--docker-password=*** \
--docker-email=***Your application's CRD YAML (replace placeholders with actual values):
apiVersion: springboot.qingmu.io/v1alpha1
kind: SpringBootApplication
metadata:
name: <your-app-name>
spec:
springBoot:
version: v1.0.0
replicas: 1
image: <your-image-address>
imagePullSecrets:
- aliyun-registry-secretA complete Spring Boot Application YAML (most fields can use defaults):
apiVersion: springboot.qingmu.io/v1alpha1
kind: SpringBootApplication
metadata:
name: operator-demo
namespace: default
spec:
springBoot:
image: registry.cn-shanghai.aliyuncs.com/qingmuio/operator-demo:v1.0.0
clusterIp: ""
version: v1.0.0
replicas: 1
resource:
cpu:
request: 50m
limit: ""
memory:
request: 1Gi
limit: 1Gi
path:
liveness: /actuator/health
readiness: /actuator/health
hostLog: /var/applog
shutdown: /spring/shutdown
imagePullSecrets:
- aliyun-docker-registry-secret
env:
- name: EUREKA_SERVERS
value: http://eureka1:8761/eureka/,http://eureka2:8761/eureka/,http://eureka3:8761/eureka/
nodeAffinity:
key: "failure-domain.beta.kubernetes.io/zone"
operator: "In"
values:
- "cn-i"
- "cn-h"
- "cn-g"3.1 Graceful Shutdown Path
Graceful shutdown is disabled by default and does not support GET requests. Enable it in application.yml:
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: trueBridge a GET method with a controller:
@RestController
public class ShutdownController {
@Autowired
private ShutdownEndpoint shutdownEndpoint;
@GetMapping("/spring/shutdown")
public Map<String, String> shutdown(HttpServletRequest request) {
return shutdownEndpoint.shutdown();
}
}3.2 Node Affinity Usage
Example: a Spring Boot service user-service should be spread across three zones (cn-i, cn-h, cn-g) with two nodes each.
cn-i zone (node-i1, node-i2)
cn-h zone (node-h1, node-h2)
cn-g zone (node-g1, node-g2)Label the nodes:
kubectl label node node-i1 failure-domain.beta.kubernetes.io/zone=cn-i
kubectl label node node-i2 failure-domain.beta.kubernetes.io/zone=cn-i
kubectl label node node-h1 failure-domain.beta.kubernetes.io/zone=cn-h
kubectl label node node-h2 failure-domain.beta.kubernetes.io/zone=cn-h
kubectl label node node-g1 failure-domain.beta.kubernetes.io/zone=cn-g
kubectl label node node-g2 failure-domain.beta.kubernetes.io/zone=cn-gSet node affinity in the CRD:
spec:
springBoot:
nodeAffinity:
key: "failure-domain.beta.kubernetes.io/zone"
operator: "In"
values:
- "cn-i"
- "cn-h"
- "cn-g"4. Operator Custom Installation
After the quick install, you can customize the Operator by injecting environment variables into its Deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-operator-controller-manager
namespace: spring-boot-operator-system
spec:
template:
spec:
containers:
- name: operator
env:
- name: IMAGE_REPOSITORY
value: registry.cn-shanghai.aliyuncs.com/qingmuio
- name: REQUEST_CPU
value: 50m
- name: LIMIT_CPU
value: ""
- name: REQUEST_MEMORY
value: 500Mi
- name: LIMIT_MEMORY
value: 500Mi
- name: READINESS_PATH
value: /actuator/health
- name: LIVENESS_PATH
value: /actuator/health
- name: SHUTDOWN_PATH
value: /spring/shutdown
- name: REPLICAS
value: "3"
- name: HOST_LOG_PATH
value: /var/applog
- name: IMAGE_PULL_SECRETS
value: ""
- name: SPRING_BOOT_DEFAULT_PORT
value: "8080"
- name: NODE_AFFINITY_KEY
value: ""
- name: NODE_AFFINITY_OPERATOR
value: ""
- name: NODE_AFFINITY_VALUES
value: ""
- name: SPRING_BOOT_ENV
value: ""
image: registry.cn-shanghai.aliyuncs.com/qingmuio/spring-boot-operator-controller:latest4.1 Deploy After Custom Installation
A simplified CRD for your application:
apiVersion: springboot.qingmu.io/v1alpha1
kind: SpringBootApplication
metadata:
name: <your-app-name>
spec:
springBoot:
version: v1.0.0Appendix
Environment variable table:
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
