Cloud Native 19 min read

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.

Programmer DD
Programmer DD
Programmer DD
Simplify Spring Boot Deployments on Kubernetes with the Spring Boot Operator

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.0

After 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.yaml

Successful 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 created

Check 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          2m15s
If 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.0

Notice 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 created

2.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   0

Check 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   2m52s

Access 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: 1

Apply the change:

# kubectl apply -f Demo.yaml
springbootapplication.springboot.qingmu.io/operator-demo configured

Verify the pod count:

# kubectl get po | grep operator-demo
operator-demo-7574f4789c-sznww   1/1   Running   0   8m29s

2.4 Clean Up operator-demo

Delete the demo pod:

# kubectl delete -f Demo.yaml
springbootapplication.springboot.qingmu.io "operator-demo" deleted

Confirm the pod is gone:

# kubectl get po | grep operator-demo

3. 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-secret

A 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: true

Bridge 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-g

Set 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:latest

4.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.0

Appendix

Environment variable table:

Environment variables
Environment variables
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerDeploymentKubernetesOperatorSpring BootJibNode Affinity
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.