Foreword
K8s + SpringBoot achieves zero-downtime release: health check + rolling update + graceful shutdown + elastic scaling + Prometheus monitoring + configuration separation (mirror reuse)
Configuration
Health Check
-
Health check type: readiness probe + liveness probe
-
Probe type: exec (enter the container to execute the script), tcpSocket (detection port), httpGet (call interface)
Business level
Project dependencies pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Define access ports, paths and permissions application.yaml
management: server: port: 50000 # Enable independent operation and maintenance port endpoint: # Open the health endpoint health: probes: enabled: true endpoints: web: exposure: base-path: /actuator # Specify the context path and enable the corresponding endpoint include: health
Two interfaces, /actuator/health/readiness
and /actuator/health/liveness
, will be exposed. The access methods are as follows:
http://127.0.0.1:50000/actuator/health/readiness http://127.0.0.1:50000/actuator/health/liveness
Operation and maintenance level
k8s deployment template deployment.yaml
apiVersion: apps/v1 Kind: Deployment spec: template: spec: containers: - name: {APP_NAME} image: {IMAGE_URL} imagePullPolicy: Always ports: - containerPort: {APP_PORT} - name: management-port containerPort: 50000 # Application management port readinessProbe: # Readiness probe httpGet: path: /actuator/health/readiness port: management-port initialDelaySeconds: 30 # Delay loading time periodSeconds: 10 #Retry interval timeoutSeconds: 1 # Timeout time setting successThreshold: 1 # Health threshold failureThreshold: 6 # Unhealthy threshold livenessProbe: # Liveness probe httpGet: path: /actuator/health/liveness port: management-port initialDelaySeconds: 30 # Delay loading time periodSeconds: 10 #Retry interval timeoutSeconds: 1 # Timeout time setting successThreshold: 1 # Health threshold failureThreshold: 6 # Unhealthy threshold
Rolling updates
Rolling update strategy of k8s resource scheduling. To achieve zero-downtime release, health check must be supported.
apiVersion: apps/v1 Kind: Deployment metadata: name: {APP_NAME} labels: app: {APP_NAME} spec: selector: matchLabels: app: {APP_NAME} replicas: {REPLICAS} # Number of Pod replicas strategy: type: RollingUpdate # Rolling update strategy rollingUpdate: maxSurge: 1 # The maximum number of copies that can be exceeded during the upgrade process than the originally set number. maxUnavailable: 1 # The maximum number of PODs that are unable to provide services during the upgrade process
Graceful shutdown
In K8s, before we implement rolling upgrade, we must achieve application-level graceful shutdown. Otherwise, the rolling upgrade will still affect the business. Make the application close the thread, release the connection resources, and then stop the service.
Business level
Project dependencies pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Define access ports, paths and permissions application.yaml
spring: application: name: <xxx> profiles: active: @profileActive@ life cycle: timeout-per-shutdown-phase: 30s # Set the shutdown process timeout to 30s. If it exceeds 30s, it will shut down directly. server: port: 8080 shutdown: graceful # The default is IMMEDIATE, which means immediate shutdown; GRACEFUL means graceful shutdown. management: server: port: 50000 # Enable independent operation and maintenance port endpoint: # Enable shutdown and health endpoints shutdown: enabled: true health: probes: enabled: true endpoints: web: exposure: base-path: /actuator # Specify the context path and enable the corresponding endpoint include: health,shutdown
The /actuator/shutdown
interface will be exposed, and the calling method is as follows:
curl -X POST 127.0.0.1:50000/actuator/shutdown
Operation and maintenance level
Make sure the dockerfile template integrates the curl tool, otherwise the curl command cannot be used
FROM openjdk:8-jdk-alpine #Build parameters ARG JAR_FILE ARG WORK_PATH="/app" ARG EXPOSE_PORT=8080 #Environment variables ENV JAVA_OPTS=""\ JAR_FILE=${JAR_FILE} #Set time zone RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime & amp; & amp; echo 'Asia/Shanghai' >/etc/timezone RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \ & amp; & amp; apk add --no-cache curl #Copy the jar package in the maven directory to docker and name it for_docker.jar COPY target/$JAR_FILE $WORK_PATH/ #Set working directory WORKDIR $WORK_PATH # Specify the port for external interaction EXPOSE $EXPOSE_PORT # Configure the container to make it executable ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_FILE
k8s deployment template deployment.yaml
Note: After verification, the Java project can omit the configuration of the end callback hook.
In addition, if you need to use a callback hook, you need to ensure that the curl tool is included in the image, and you need to note that the application management port (50000) cannot be exposed to the public network.
apiVersion: apps/v1 Kind: Deployment spec: template: spec: containers: - name: {APP_NAME} image: {IMAGE_URL} imagePullPolicy: Always ports: - containerPort: {APP_PORT} - containerPort: 50000 life cycle: preStop: # End callback hook exec: command: ["curl", "-XPOST", "127.0.0.1:50000/actuator/shutdown"]
Elastic scaling
After setting resource limits for the pod, create the HPA
apiVersion: apps/v1 Kind: Deployment metadata: name: {APP_NAME} labels: app: {APP_NAME} spec: template: spec: containers: - name: {APP_NAME} image: {IMAGE_URL} imagePullPolicy: Always resources: # Container resource management limits: # Resource limits (monitor usage) CPU: 0.5 memory: 1Gi requests: # Minimum available resources (flexible scheduling) CPU: 0.15 memory: 300Mi --- kind: HorizontalPodAutoscaler # Elastic scaling controller apiVersion: autoscaling/v2beta2 metadata: name: {APP_NAME} spec: scaleTargetRef: apiVersion: apps/v1 Kind: Deployment name: {APP_NAME} minReplicas: {REPLICAS} # Zoom range maxReplicas: 6 metrics: - type: Resource resource: name: cpu #Specify resource indicators target: type: Utilization averageUtilization: 50
Prometheus integration
Business level
Project dependencies pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>io.micrometer micrometer-registry-prometheus
Define access ports, paths and permissions application.yaml
management: server: port: 50000 # Enable independent operation and maintenance port metrics: tags: application: ${spring.application.name} endpoints: web: exposure: base-path: /actuator # Specify the context path and enable the corresponding endpoint include: metrics, prometheus
The /actuator/metric
and /actuator/prometheus
interfaces will be exposed, and the access methods are as follows:
http://127.0.0.1:50000/actuator/metric http://127.0.0.1:50000/actuator/prometheus
Operation and maintenance level
deployment.yaml
apiVersion: apps/v1 Kind: Deployment spec: template: metadata: annotations: prometheus:io/port: "50000" prometheus.io/path: /actuator/prometheus # Assign value in pipeline prometheus.io/scrape: "true" # pod-based service discovery
Configuration separation
Plan: Mount external configuration files through configmap and specify the activation environment to run
Function: Configuration separation to avoid leakage of sensitive information; image reuse to improve delivery efficiency
Generate configmap from file
# Generate yaml files through dry-run kubectl create cm -n <namespace> <APP_NAME> --from-file=application-test.yaml --dry-run=1 -oyaml > configmap.yaml # renew kubectl apply -f configmap.yaml
Mount configmap and specify the activation environment
apiVersion: apps/v1 Kind: Deployment metadata: name: {APP_NAME} labels: app: {APP_NAME} spec: template: spec: containers: - name: {APP_NAME} image: {IMAGE_URL} imagePullPolicy: Always env: - name: SPRING_PROFILES_ACTIVE # Specify activation environment value: test volumeMounts: # Mount configmap - name: conf mountPath: "/app/config" # Consistent with the working directory in the Dockerfile readOnly: true volumes: - name: conf configMap: name: {APP_NAME}
Summary configuration
Business level
Project dependencies pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>io.micrometer micrometer-registry-prometheus
Define access ports, paths and permissions application.yaml
spring: application: name: project-sample profiles: active: @profileActive@ life cycle: timeout-per-shutdown-phase: 30s # Set the shutdown process timeout to 30s. If it exceeds 30s, it will shut down directly. server: port: 8080 shutdown: graceful # The default is IMMEDIATE, which means immediate shutdown; GRACEFUL means graceful shutdown. management: server: port: 50000 # Enable independent operation and maintenance port metrics: tags: application: ${spring.application.name} endpoint: # Enable shutdown and health endpoints shutdown: enabled: true health: probes: enabled: true endpoints: web: exposure: base-path: /actuator # Specify the context path and enable the corresponding endpoint include: health,shutdown,metrics,prometheus
Operation and maintenance level
Make sure the dockerfile template integrates the curl tool, otherwise the curl command cannot be used
FROM openjdk:8-jdk-alpine #Build parameters ARG JAR_FILE ARG WORK_PATH="/app" ARG EXPOSE_PORT=8080 #Environment variables ENV JAVA_OPTS=""\ JAR_FILE=${JAR_FILE} #Set time zone RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime & amp; & amp; echo 'Asia/Shanghai' >/etc/timezone RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \ & amp; & amp; apk add --no-cache curl #Copy the jar package in the maven directory to docker and name it for_docker.jar COPY target/$JAR_FILE $WORK_PATH/ #Set working directory WORKDIR $WORK_PATH # Specify the port for external interaction EXPOSE $EXPOSE_PORT # Configure the container to make it executable ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_FILE
k8s deployment template deployment.yaml
apiVersion: apps/v1 Kind: Deployment metadata: name: {APP_NAME} labels: app: {APP_NAME} spec: selector: matchLabels: app: {APP_NAME} replicas: {REPLICAS} # Number of Pod replicas strategy: type: RollingUpdate # Rolling update strategy rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: name: {APP_NAME} labels: app: {APP_NAME} annotations: timestamp: {TIMESTAMP} prometheus.io/port: "50000" # cannot be assigned dynamically prometheus.io/path: /actuator/prometheus prometheus.io/scrape: "true" # pod-based service discovery spec: affinity: # Set the scheduling policy and adopt multi-host/multi-availability zone deployment podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: -key: app operator: In values: - {APP_NAME} topologyKey: "kubernetes.io/hostname" # Multiple availability zones are "topology.kubernetes.io/zone" terminationGracePeriodSeconds: 30 # Graceful termination grace period containers: - name: {APP_NAME} image: {IMAGE_URL} imagePullPolicy: Always ports: - containerPort: {APP_PORT} - name: management-port containerPort: 50000 # Application management port readinessProbe: # Readiness probe httpGet: path: /actuator/health/readiness port: management-port initialDelaySeconds: 30 # Delay loading time periodSeconds: 10 #Retry interval timeoutSeconds: 1 # Timeout time setting successThreshold: 1 # Health threshold failureThreshold: 9 # Unhealthy threshold livenessProbe: # Liveness probe httpGet: path: /actuator/health/liveness port: management-port initialDelaySeconds: 30 # Delay loading time periodSeconds: 10 #Retry interval timeoutSeconds: 1 # Timeout time setting successThreshold: 1 # Health threshold failureThreshold: 6 # Unhealthy threshold resources: # Container resource management limits: # Resource limits (monitor usage) CPU: 0.5 memory: 1Gi requests: # Minimum available resources (flexible scheduling) CPU: 0.1 memory: 200Mi env: - name: TZ value: Asia/Shanghai --- kind: HorizontalPodAutoscaler # Elastic scaling controller apiVersion: autoscaling/v2beta2 metadata: name: {APP_NAME} spec: scaleTargetRef: apiVersion: apps/v1 Kind: Deployment name: {APP_NAME} minReplicas: {REPLICAS} # Zoom range maxReplicas: 6 metrics: - type: Resource resource: name: cpu #Specify resource indicators target: type: Utilization averageUtilization: 50