Kubernetes 集群部署 Spring Boot 应用最佳实践
【摘要】 Kubernetes 集群部署 Spring Boot 应用最佳实践介绍 (Introduction)Spring Boot 是一个用于快速开发独立、生产级别 Spring 应用的框架。而 Kubernetes (K8s) 是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。将 Spring Boot 应用容器化并部署到 Kubernetes 上,是构建现代微服务和云原生应用的常...
Kubernetes 集群部署 Spring Boot 应用最佳实践
介绍 (Introduction)
Spring Boot 是一个用于快速开发独立、生产级别 Spring 应用的框架。而 Kubernetes (K8s) 是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。将 Spring Boot 应用容器化并部署到 Kubernetes 上,是构建现代微服务和云原生应用的常见模式。
简单的将 Spring Boot JAR 打包进 Docker 镜像并在 K8s 中运行只是第一步。为了充分发挥 Kubernetes 的优势,并确保 Spring Boot 应用在高可用、可伸缩的环境中稳定运行,需要遵循一系列最佳实践。本指南将深入探讨这些最佳实践,并通过一个完整的代码示例来演示。
引言 (Foreword/Motivation)
微服务架构的兴起带来了应用拆分、独立部署和快速迭代的需求。Spring Boot 简化了单个服务的开发,而 Kubernetes 提供了管理这些服务集群的强大能力。它们是天然的搭档。
然而,将一个原本设计在传统服务器或虚拟机上运行的 Spring Boot 应用迁移到动态、分布式的 Kubernetes 环境,会面临新的挑战:
- 配置管理: 如何在不修改镜像的情况下管理不同环境的配置?
- 服务发现与负载均衡: Kubernetes 如何找到并访问应用实例?
- 健康检查与自愈: Kubernetes 如何知道应用是否正常运行并自动重启?
- 资源管理: 如何合理分配 CPU 和内存资源,避免资源争抢或浪费?
- 应用生命周期: 如何确保应用在 Pod 启动和终止时平滑过渡?
- 可观测性: 如何收集日志、指标、追踪数据?
遵循最佳实践,可以确保您的 Spring Boot 应用能够充分利用 Kubernetes 的弹性伸缩、自愈能力和标准化管理,构建出更健壮、更可靠的云原生应用。
技术背景 (Technical Background)
- Kubernetes 架构: 理解 K8s 由 Master(控制平面)和 Node(工作节点)组成。核心组件包括 API Server, Scheduler, Controller Manager。工作负载运行在 Pod 中,Pod 是最小的部署单元,包含一个或多个容器。Deployment 管理 Pod 的副本和更新。Service 提供稳定的网络访问。
- Docker 容器化: 将 Spring Boot 可执行 JAR 和其运行环境(JVM)打包到一个标准的、轻量级的容器镜像中。
- Spring Boot 可执行 JAR: Spring Boot 应用可以被打包成一个包含内嵌 Web 服务器和所有依赖的独立可执行 JAR 文件,可以直接使用
java -jar
命令运行。 - Spring Boot Actuator: 提供了一系列生产就绪的端点(如
/actuator/health
,/actuator/actuator/metrics
),用于监控和管理应用。/actuator/health/liveness
和/actuator/actuator/health/readiness
端点对 Kubernetes 健康检查非常有用。 - Spring Boot 外部化配置: Spring Boot 支持从多种来源加载配置,包括
application.properties
/application.yml
文件、环境变量、命令行参数、ConfigMap/Secret 挂载的文件等,且有优先级顺序。
应用使用场景 (Application Scenarios)
- 将单体 Spring 应用拆分为微服务,并部署到 K8s 集群中。
- 开发新的基于 Spring Boot 的微服务,并利用 K8s 的自动化能力进行部署和管理。
- 构建 API Gateway(例如使用 Spring Cloud Gateway)作为微服务集群的统一入口。
- 部署需要弹性伸缩、高可用和快速迭代的后台服务。
最佳实践核心策略 (Core Best Practice Strategies)
-
优化容器镜像:
- 使用多阶段构建 (Multi-stage Build): 使用一个构建器镜像(如带有 Maven/Gradle 和 JDK 的镜像)来构建 Spring Boot 应用,然后使用一个更小、更精简的基础镜像(如 Alpine Linux + OpenJDK Runtime 或 Google 的 Distroless 镜像)来运行最终的可执行 JAR。这可以显著减小镜像体积,提高安全性和拉取速度。
- 使用 JIB 或 Buildpacks: 避免手动编写 Dockerfile。JIB (由 Google 开发) 或 Spring Boot Buildpacks 可以直接从 Maven/Gradle 项目构建优化的容器镜像,无需 Dockerfile。
- 使用非 Root 用户: 在容器中以非 root 用户运行应用,降低安全风险。
- 减小镜像体积: 清理构建缓存,只包含运行时必需的文件。
-
外部化配置:
- 使用 ConfigMap 和 Secret: 将应用的配置信息(如数据库连接字符串、第三方服务密钥、业务参数)存储在 Kubernetes 的 ConfigMap (非敏感数据) 和 Secret (敏感数据) 中。
- 通过卷挂载配置: 将 ConfigMap 或 Secret 作为卷(Volume)挂载到 Pod 的文件系统中,Spring Boot 可以从挂载的文件(如
application.properties
或application.yml
)中读取配置。 - 通过环境变量注入: 将 ConfigMap 或 Secret 中的数据作为环境变量注入到容器中。Spring Boot 可以方便地从环境变量中加载配置,且环境变量的优先级通常高于配置文件。
-
健康检查与就绪检查:
- 配置 Liveness Probe (存活探针): 让 Kubelet 周期性检查应用是否“活着”。如果探针失败,Kubelet 会重启 Pod。使用 Spring Boot Actuator 的
/actuator/health/liveness
端点作为 HTTP GET 探针。 - 配置 Readiness Probe (就绪探针): 让 Kubelet 知道应用是否“准备好”接收流量。如果探针失败,Kubelet 不会将流量发送到该 Pod,直到探针成功。使用 Spring Boot Actuator 的
/actuator/health/readiness
端点作为 HTTP GET 探针。就绪探针在滚动更新、缩容等场景下非常重要。
- 配置 Liveness Probe (存活探针): 让 Kubelet 周期性检查应用是否“活着”。如果探针失败,Kubelet 会重启 Pod。使用 Spring Boot Actuator 的
-
资源管理:
- 定义 Resource Requests: 为 Pod 设置 CPU 和内存的请求值。这有助于调度器将 Pod 放置在有足够资源的节点上,并为 Pod 分配最低资源保障。
- 定义 Resource Limits: 为 Pod 设置 CPU 和内存的上限值。这可以防止单个 Pod 消耗过多资源影响同一节点上的其他 Pod,并避免因内存耗尽导致 Pod 被 OOMKilled。
-
可伸缩性:
- 使用 Deployment: Deployment 是管理无状态应用副本的最佳选择。通过修改
replicas
字段可以手动伸缩。 - 配置 Horizontal Pod Autoscaler (HPA): 基于 CPU 利用率、内存使用率或自定义指标自动调整 Deployment 的副本数量。
- 使用 Deployment: Deployment 是管理无状态应用副本的最佳选择。通过修改
-
日志:
- 日志输出到标准输出: 配置 Spring Boot 将日志输出到标准输出 (
stdout
) 或标准错误 (stderr
)。Kubernetes 会自动收集这些日志,并通过kubectl logs
命令或集成到日志系统中(如 EFK Stack - Elasticsearch, Fluentd, Kibana 或 Loki, Promtail, Grafana)。
- 日志输出到标准输出: 配置 Spring Boot 将日志输出到标准输出 (
-
优雅停机 (Graceful Shutdown):
- 启用 Spring Boot 优雅停机: 在 Spring Boot 中配置优雅停机(如设置
server.shutdown=graceful
),让应用在接收到终止信号时,完成当前请求并拒绝新的请求,等待一段时间后关闭。 - 配置 K8s
terminationGracePeriodSeconds
: 为 Pod 设置一个足够的时间(默认 30 秒),允许应用在接收到 Kubelet 发送的SIGTERM
信号后,有时间进行优雅停机。
- 启用 Spring Boot 优雅停机: 在 Spring Boot 中配置优雅停机(如设置
原理解释 (Principle Explanation)
- 容器化与隔离: Docker 将应用及其依赖打包,形成独立的环境,避免了“依赖地狱”。K8s 利用容器作为最小部署单元,实现隔离和资源管理。
- 声明式管理: K8s 使用 YAML 文件描述期望的应用状态(Pod 数量、镜像版本、配置、服务暴露方式等),由 K8s 控制平面负责将其转化为实际运行状态。
- 服务发现与抽象: K8s Service 为一组 Pod 提供一个稳定的 IP 地址和 DNS 名称,后端 Pod 变化不会影响前端访问者。
- Probe 工作机制: Kubelet 在每个节点上运行,负责管理该节点上的 Pod。Liveness Probe 和 Readiness Probe 是 Kubelet 周期性执行的检查。根据检查结果,Kubelet 决定是否重启 Pod (Liveness) 或是否将流量发送到 Pod (Readiness)。Spring Boot Actuator 提供了标准的 HTTP 端点供 Kubelet 检查。
- ConfigMap/Secret 与注入: ConfigMap 和 Secret 作为 K8s 的配置存储,通过 Volume 机制或 Downward API/环境变量的方式,将配置数据安全地注入到 Pod 的文件系统或环境变量中,应用可以直接读取。
- HPA 工作机制: HPA 控制器周期性获取 Pod 的资源使用率或自定义指标,与设定的目标值进行比较,如果超过阈值,则向 Deployment 发送指令增加 Pod 副本数;如果低于阈值,则减少副本数。
核心特性 (Core Features - of the combined solution)
- 自动化部署与更新: 使用 Deployment 实现应用的滚动更新和版本回滚。
- 弹性伸缩: 手动或自动根据负载调整副本数量。
- 自我修复: Kubelet 自动检测并重启失败的应用实例。
- 集中配置管理: 安全、灵活地管理应用配置。
- 标准化的健康检查和监控点: 利用 Actuator 端点与 K8s 监控系统集成。
- 资源高效利用: 通过 Requests 和 Limits 合理分配资源。
- 跨环境一致性: 容器镜像保证了在不同 K8s 集群中运行环境的一致性。
- 优雅的应用生命周期管理。
原理流程图以及原理解释 (Principle Flowchart)
(此处无法直接生成图形,用文字描述构建部署流程图)
图示:Spring Boot 应用在 K8s 中的生命周期与部署流程
+---------------------+ +---------------------+ +---------------------+ +---------------------+
| Spring Boot 代码 | ----> | Dockerfile | ----> | Docker Image | ----> | K8s Deployment |
| (Java/Kotlin/Scala) | | (定义构建步骤) | | (可执行 JAR + JRE)| | (定义期望状态) |
+---------------------+ +---------------------+ +---------------------+ +---------------------+
^
| Reference Image
| Define Probes, Resources, Config
|
+---------------------+ +---------------------+ +---------------------+ +---------------------+
| ConfigMap / Secret | ----> | Kubernetes API | <---- | K8s Control Plane| ----> | K8s Node |
| (外部配置) | | (接收/存储资源) | | (Scheduler, DeployCtl)| | (运行 Kubelet) |
+---------------------+ +---------------------+ +---------------------+ +---------------------+
| ^
| Mount/Inject to Pods | Kubelet Pulls Image
v | Kubelet Starts Container
+---------------------+ | Kubelet Mounts Volumes
| K8s Pod | | Kubelet Checks Probes
| (运行容器) | +---------------------+
| - App Container |
| - Mounted Config |
| - Exposed Actuator |
+---------------------+
^
| Selected by Service
| Routed by Ingress
+---------------------+
| K8s Service |
| (网络访问) |
+---------------------+
原理解释:
- 开发者编写 Spring Boot 代码,并创建 Dockerfile 定义如何将其容器化(通常是打包可执行 JAR,然后放入一个轻量级 JRE 镜像)。
- 使用
docker build
命令构建 Docker 镜像。 - 同时,定义 Kubernetes 资源清单(YAML 文件):
ConfigMap
/Secret
: 存储应用的外部配置。Deployment
: 定义应用的 Pod 模板(使用之前构建的镜像)、副本数量、资源请求/限制、健康检查探针(指向 Actuator 端点)、以及如何从 ConfigMap/Secret 挂载配置或注入环境变量。Service
: 定义如何通过稳定的 IP/DNS 访问 Deployment 管理的 Pod 集合。
- 使用
kubectl apply -f ...
命令将这些资源清单提交给 Kubernetes API Server。 - K8s 控制平面接收到这些定义,Scheduler 负责将 Pod 调度到合适的 Node 上。Deployment Controller 负责按照副本数创建 Pod。
- 在选定的 Node 上,Kubelet 负责拉取指定的 Docker 镜像,创建 Pod,并在其中启动应用容器。Kubelet 根据 Deployment 定义,将 ConfigMap/Secret 作为 Volume 挂载到容器文件系统,或将其数据注入为环境变量。
- 应用容器(Spring Boot 应用)启动,从挂载的文件或环境变量中读取配置。
- Kubelet 周期性地执行 Liveness 和 Readiness 探针,检查 Spring Boot 应用的 Actuator 端点,根据返回值判断 Pod 的健康和就绪状态。
- K8s Service 通过 Label Selector 找到属于该 Deployment 的健康 Pod,并将流量转发给这些 Pod。Ingress 则负责将外部流量路由到 Service。
环境准备 (Environment Setup)
- Java Development Kit (JDK): Spring Boot 3+ 需要 JDK 17+,Spring Boot 2+ 需要 JDK 8+。
- Maven 或 Gradle: 构建工具。
- Docker Desktop 或 Docker Engine: 用于构建 Docker 镜像。
- Kubernetes 集群: 正在运行的集群。例如,Minikube, Docker Desktop 内置 K8s, 云服务商提供的托管 K8s (EKS, GKE, AKS, ACK) 或自建集群。
- kubectl 命令行工具: 配置好连接到你的 K8s 集群。
- 文本编辑器/IDE: 用于编写代码和 YAML 文件。
不同场景下详细代码实现 & 代码示例实现 (Detailed Code Examples & Code Sample Implementation)
我们将创建一个简单的 Spring Boot REST 应用并为其编写 Dockerfile 和 K8s Manifests。
1. Spring Boot 应用 (Minimal)
pom.xml
(部分核心依赖):<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.4</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>my-k8s-app</artifactId> <version>0.0.1-SNAPSHOT</version> <name>my-k8s-app</name> <description>Demo Spring Boot app for K8s</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> </configuration> </plugin> </plugins> </build> </project>
src/main/java/com/example/app/MyK8sAppApplication.java
(入口类和控制器):package com.example.app; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class MyK8sAppApplication { // 从配置中注入一个欢迎消息,用于演示外部配置 @Value("${app.greeting:Hello from Default Config}") // 提供默认值 private String greetingMessage; public static void main(String[] args) { // 启用优雅停机 (可选,但推荐最佳实践) // System.setProperty("server.shutdown", "graceful"); // 或在 application.properties/yml 中配置 SpringApplication.run(MyK8sAppApplication.class, args); } @GetMapping("/") public String helloK8s() { // 返回包含外部配置消息的响应 return greetingMessage + " - Running in Kubernetes!"; } // Actuator 端点 /actuator/health/liveness 和 /actuator/health/readiness 会自动暴露 // 用于 K8s 的 Liveness 和 Readiness 探针 }
src/main/resources/application.properties
(默认配置):# application.properties server.port=8080 # 内嵌Web服务器默认端口 # 优雅停机配置 server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=30s # 停机超时时间 # Actuator 端点配置 (确保可以在 K8s 中访问) management.endpoints.web.exposure.include=health,info,prometheus management.endpoint.health.show-details=always management.endpoint.health.livenessstate.enabled=true management.endpoint.health.readinessstate.enabled=true # 默认的问候消息 app.greeting=Hello from Default application.properties
2. Dockerfile (多阶段构建示例)
将以下内容保存为项目根目录下的 Dockerfile
。
# --- 阶段 1: 构建阶段 ---
# 使用带有 JDK 和 Maven 的构建器镜像
FROM maven:3.8.6-openjdk-17 AS builder
# 设置工作目录
WORKDIR /app
# 复制 Maven 项目文件
COPY pom.xml .
COPY src ./src
# 构建 Spring Boot 应用 (生成可执行 JAR)
# -Dmaven.test.skip=true 跳过测试以加快构建 (如果测试耗时)
RUN mvn clean package -Dmaven.test.skip=true
# --- 阶段 2: 运行阶段 ---
# 使用一个更轻量级的 JRE 镜像来运行应用
# openjdk:17-alpine 是一个基于 Alpine Linux 的小型 JRE 镜像
# 也可以考虑更小的 distroless 镜像 (配置稍微复杂)
FROM openjdk:17-alpine
# 作者信息 (可选)
LABEL maintainer="your-email@example.com"
# 创建非 root 用户和用户组
RUN addgroup -S springgrp && adduser -S springusr -G springgrp
# 设置用户
USER springusr
# 设置工作目录
WORKDIR /app
# 从构建阶段复制生成的可执行 JAR 文件
# target/*.jar 匹配构建阶段生成的 JAR 文件
COPY --from=builder /app/target/*.jar app.jar
# 暴露 Spring Boot 应用监听的端口 (通常是 8080)
EXPOSE 8080
# 定义容器启动时执行的命令
# 使用 java -jar 运行 Spring Boot 可执行 JAR
# java -jar /app/app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
# 可以通过环境变量覆盖配置,例如 -e SERVER_PORT=8090
# 也可以通过命令行参数覆盖,例如 java -jar /app/app.jar --server.port=8090
# Spring Boot 支持多种外部化配置方式
3. Kubernetes Manifests
将以下内容保存为不同的 .yaml
文件。
00-namespace.yaml
(Namespace):apiVersion: v1 kind: Namespace metadata: name: my-spring-app # 定义一个独立的命名空间
01-configmap.yaml
(ConfigMap - 示例外部配置):apiVersion: v1 kind: ConfigMap metadata: name: my-spring-config # ConfigMap 名称 namespace: my-spring-app data: # 将 application.properties 的内容作为 ConfigMap 的一个键值对 # Spring Boot 可以配置从这个文件加载配置 application.properties: | # 外部覆盖的问候消息 app.greeting=Hello from Kubernetes ConfigMap! # 也可以在这里配置其他应用参数,如数据库连接,第三方服务地址等 # spring.datasource.url=jdbc:mysql://mysql-service:3306/mydb # api.service.url=http://some-external-api.com
02-deployment.yaml
(Deployment):apiVersion: apps/v1 kind: Deployment metadata: name: my-spring-app-deployment # Deployment 名称 namespace: my-spring-app labels: app: my-spring-app # 用于 Service 选择 Pod 的标签 spec: replicas: 3 # 期望的 Pod 副本数量 selector: matchLabels: app: my-spring-app # 选择带有 app: my-spring-app 标签的 Pod strategy: type: RollingUpdate # 滚动更新策略 # RollingUpdate 策略的更多配置 (可选) # rollingUpdate: # maxUnavailable: 1 # 滚动更新过程中最多有多少个不可用 Pod # maxSurge: 1 # 滚动更新过程中最多有多少个超出期望副本数的 Pod template: metadata: labels: app: my-spring-app # Pod 标签 spec: # 使用非 root 用户运行容器 (Dockerfile 中已设置) # securityContext: # runAsUser: 1000 # 指定运行用户ID # runAsGroup: 1000 # 指定运行用户组ID containers: - name: my-spring-app-container # 容器名称 image: your-docker-registry/my-k8s-app:latest # <<<< 替换为你的 Docker 镜像地址和标签 >>>> ports: - containerPort: 8080 # 应用在容器内部监听的端口 # --- 资源管理 --- resources: requests: # Pod 调度时需要的最低资源请求 cpu: "200m" # 200 milliCPU (0.2 CPU 核心) memory: "256Mi" # 256 MiB 内存 limits: # Pod 可以使用的资源上限 cpu: "500m" # 500 milliCPU (0.5 CPU 核心),避免占用过多CPU memory: "512Mi" # 512 MiB 内存,避免 OOMKilled # --- 健康检查探针 --- livenessProbe: # 存活探针,检查应用是否存活,失败重启 Pod httpGet: path: /actuator/health/liveness # Spring Boot Actuator 的 Liveness 端点 port: 8080 # 容器端口 initialDelaySeconds: 30 # 容器启动后延迟 30 秒开始检查 periodSeconds: 10 # 每隔 10 秒检查一次 timeoutSeconds: 5 # 探针超时时间 failureThreshold: 3 # 连续失败 3 次认为失败 readinessProbe: # 就绪探针,检查应用是否准备好接收流量,失败不发送流量 httpGet: path: /actuator/health/readiness # Spring Boot Actuator 的 Readiness 端点 port: 8080 initialDelaySeconds: 20 # 容器启动后延迟 20 秒开始检查 (通常比 Liveness 早) periodSeconds: 10 # 每隔 10 秒检查一次 timeoutSeconds: 5 failureThreshold: 3 # 连续失败 3 次认为失败 # --- 配置注入 (从 ConfigMap 挂载文件) --- volumeMounts: - name: config-volume # 卷名称 mountPath: /config # 挂载到容器内部的路径 readOnly: true # 只读挂载 # --- 配置注入 (从 ConfigMap 注入环境变量,可选) --- # env: # - name: APP_GREETING # 环境变量名称 # valueFrom: # configMapKeyRef: # name: my-spring-config # 引用 ConfigMap 名称 # key: app.greeting # 引用 ConfigMap 中的 key (Spring Boot 会自动将环境变量转换为配置) # --- 卷定义 --- volumes: - name: config-volume # 卷名称 configMap: name: my-spring-config # 引用 ConfigMap 名称 # items: # 如果 ConfigMap 中有多个 key,可以选择只挂载部分 # - key: application.properties # path: application.properties # 挂载到 /config/application.properties # 优雅停机超时时间,允许 Pod 在接收到 SIGTERM 后有时间优雅退出 terminationGracePeriodSeconds: 60 # 允许 60 秒优雅停机
03-service.yaml
(Service):apiVersion: v1 kind: Service metadata: name: my-spring-app-service # Service 名称 namespace: my-spring-app spec: selector: app: my-spring-app # 选择带有 app: my-spring-app 标签的 Pod ports: - protocol: TCP port: 80 # Service 端口 targetPort: 8080 # Pod 容器端口 (与 Dockerfile 和 Deployment 中的 containerPort 一致) # nodePort: 3xxxx # 如果 Service type 是 NodePort,这里可以指定 Node 端口范围 (30000-32767) type: ClusterIP # Service 类型 (ClusterIP 内部访问, NodePort 节点端口访问, LoadBalancer 负载均衡器访问) # type: LoadBalancer # 如果你的 K8s 环境支持 LoadBalancer
运行结果 (Execution Results)
- 构建 Spring Boot 应用: 在 Spring Boot 项目根目录执行
mvn clean package
或gradle clean build
. 生成target/*.jar
文件。 - 构建 Docker 镜像: 在包含
Dockerfile
和target
目录的根目录执行docker build -t your-docker-registry/my-k8s-app:latest .
并推送到镜像仓库 (docker push ...
)。 - 应用 K8s Manifests:
您会在终端看到资源创建成功的提示。kubectl apply -f 00-namespace.yaml kubectl apply -f 01-configmap.yaml kubectl apply -f 02-deployment.yaml kubectl apply -f 03-service.yaml
- 检查 Pod 状态:
观察 Pod 状态从 Pending -> ContainerCreating -> Running。确认 replica 数量正确(例如 3 个)。kubectl get pods -n my-spring-app
- 检查 Deployment 状态:
确认 Deployment 状态为 Available,Desired, Current, Up-to-date 副本数一致。kubectl get deployments -n my-spring-app
- 检查 Service 状态:
确认 Service 创建成功,并获取其 ClusterIP 或 External IP (如果是 LoadBalancer)。kubectl get svc -n my-spring-app
- 访问应用 API:
- ClusterIP (集群内部访问): 在集群内的其他 Pod 中使用 Service 名称和端口访问,例如
curl my-spring-app-service.my-spring-app.svc.cluster.local
或通过端口转发kubectl port-forward svc/my-spring-app-service 8080:80 -n my-spring-app
然后curl localhost:8080
。 - NodePort: 访问任一 Worker 节点的 IP 和 Service 的 NodePort。
- LoadBalancer: 访问 Service 的 External IP。
- 期望结果: 您应该看到类似
Hello from Kubernetes ConfigMap! - Running in Kubernetes!
的响应。这验证了应用运行、Service 正常、以及 ConfigMap 配置生效。
- ClusterIP (集群内部访问): 在集群内的其他 Pod 中使用 Service 名称和端口访问,例如
测试步骤以及详细代码 (Testing Steps)
除了上述的基本运行验证外,还需要测试关键的最佳实践功能。
- 测试配置更新 (滚动更新):
- 步骤: 修改
01-configmap.yaml
中app.greeting
的值。 - 代码:
# 修改 01-configmap.yaml data: application.properties: | app.greeting=Greeting Updated from K8s Config!
kubectl apply -f 01-configmap.yaml -n my-spring-app # 应用新的 ConfigMap # 触发 Deployment 使用新的 ConfigMap (可以通过删除 Pod 或更新 Deployment 模板,推荐更新模板) kubectl rollout restart deployment my-spring-app-deployment -n my-spring-app # 重启 Pod 以加载新的 ConfigMap # 观察滚动更新过程 kubectl rollout status deployment/my-spring-app-deployment -n my-spring-app
- 验证: 滚动更新完成后,再次访问应用 API,确认返回的消息已经更新。
- 步骤: 修改
- 测试健康检查 (Liveness/Readiness):
- 步骤: 模拟应用内部出错导致健康检查失败。例如,可以在
MyK8sAppApplication
中添加一个状态标志,通过一个特殊请求来设置该标志,使其 Liveness/Readiness 端点返回非 UP 状态。 - 代码 (概念性):
触发// 在 MyK8sAppApplication.java 中 // public static volatile boolean isHealthy = true; // @GetMapping("/makeUnhealthy") // public String makeUnhealthy() { isHealthy = false; return "App is now unhealthy"; } // 然后在 Actuator 的 HealthIndicator 中根据 isHealthy 返回 DownStatus
/makeUnhealthy
请求,然后观察kubectl get pods -n my-spring-app
。 - 验证: Liveness 失败会导致 Pod 被重启。Readiness 失败会导致 Pod 的 READY 状态变为
0/1
或N/N-1
,且 Service 不再向其发送流量。
- 步骤: 模拟应用内部出错导致健康检查失败。例如,可以在
- 测试伸缩 (手动和 HPA):
- 手动伸缩:
kubectl scale deployment my-spring-app-deployment --replicas=5 -n my-spring-app # 扩展到 5 个副本 kubectl get pods -n my-spring-app # 观察 Pod 数量增加 kubectl scale deployment my-spring-app-deployment --replicas=2 -n my-spring-app # 缩减到 2 个副本 kubectl get pods -n my-spring-app # 观察 Pod 数量减少 (会优雅终止 Pod)
- HPA (需要 Metrics Server): 创建 HPA 对象(需要集群安装 Metrics Server)。
# 04-hpa.yaml (示例) apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: my-spring-app-hpa namespace: my-spring-app spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-spring-app-deployment # 关联的 Deployment minReplicas: 2 # 最小副本数 maxReplicas: 10 # 最大副本数 metrics: - type: Resource resource: name: cpu # averageValue: 50m # 平均 CPU 使用量目标 50m target: type: Utilization # 平均使用率 averageUtilization: 50 # 目标平均 CPU 使用率 50% # - type: Resource # 也可以基于内存 # resource: # name: memory # target: # type: AverageValue # averageValue: 200Mi
然后通过工具模拟高负载(如 Locust, JMeter, Apache Bench)使 Pod CPU 利用率超过 50%,观察 Pod 数量是否自动增加。kubectl apply -f 04-hpa.yaml -n my-spring-app kubectl get hpa -n my-spring-app # 检查 HPA 状态
- 验证: 观察 Pod 数量随负载变化。
- 手动伸缩:
- 测试优雅停机:
- 步骤: 确保 Spring Boot 应用已配置
server.shutdown=graceful
,并且 Deployment 的terminationGracePeriodSeconds
足够长。 - 代码: 缩减 Deployment 副本数或直接删除一个 Pod。
观察 Pod 的状态变化(Pending Termination -> Terminating),并在容器日志中检查 Spring Boot 是否打印了优雅停机相关的日志。在 Pod 进入 Terminating 状态后,在# 缩减副本,触发优雅停机 kubectl scale deployment my-spring-app-deployment --replicas=1 -n my-spring-app # 或直接删除 Pod (会收到 SIGTERM 信号) # kubectl delete pod <one-of-your-app-pods> -n my-spring-app
terminationGracePeriodSeconds
超时前,Kubelet 不会强制杀死进程。 - 验证: 确认 Pod 在收到 SIGTERM 后有一段时间才真正停止,并且应用日志显示正在处理 shutdown。
- 步骤: 确保 Spring Boot 应用已配置
部署场景 (Deployment Scenarios)
本指南描述的最佳实践适用于将 Spring Boot 应用部署到几乎所有类型的 Kubernetes 集群中:
- 公有云托管 K8s: AWS EKS, Google GKE, Azure AKS, 阿里云 ACK, 腾讯云 TKE 等。
- 私有云 K8s: 使用 Rancher, OpenShift, VMware Tanzu 等平台搭建的 K8s 集群。
- 自建 K8s 集群: 使用 Kubeadm 等工具搭建的集群。
- 本地 K8s (开发/测试): 使用 Minikube, Docker Desktop 内置 K8s 进行本地开发和测试。
这些最佳实践可以确保您的 Spring Boot 应用在不同的 Kubernetes 环境下都能获得良好的运行时特性。
疑难解答 (Troubleshooting)
- Pod 无法启动:
- 排查: 参照上面运行结果中的检查步骤,使用
kubectl get pods
和kubectl describe pod
查看状态和事件。检查 Docker 镜像是否存在且可拉取。检查资源请求/限制是否合理。
- 排查: 参照上面运行结果中的检查步骤,使用
- Pod 启动后 CrashLoopBackOff:
- 排查: 查看容器的日志 (
kubectl logs <pod-name> -n <namespace>
),这是应用启动失败最常见的原因。可能是应用代码 Bug、配置加载失败、数据库连接问题、依赖项缺失等。
- 排查: 查看容器的日志 (
- Pod 运行中但 Readiness 或 Liveness 失败:
- 排查: 检查 Actuator 健康端点是否正确配置并暴露 (
/actuator/health
相关配置)。检查探针路径和端口是否正确。检查应用内部的健康状态逻辑是否有问题。查看容器日志,Actuator 通常会打印健康检查状态变化。
- 排查: 检查 Actuator 健康端点是否正确配置并暴露 (
- 配置未生效:
- 排查: 检查 ConfigMap 或 Secret 是否已成功创建 (
kubectl get cm/secret -n <namespace>
)。检查 Deployment 的volumeMounts
和volumes
配置是否正确,ConfigMap 是否被成功挂载到容器内部的路径。检查 Spring Boot 应用是否配置为从该路径下的文件读取配置(如spring.config.additional-location=file:/config/
或默认就会扫描/config
)。检查环境变量是否正确注入。检查 Spring Boot 配置的优先级顺序。
- 排查: 检查 ConfigMap 或 Secret 是否已成功创建 (
- OOMKilled (内存不足):
- 排查: 应用尝试使用的内存超过了 Pod 的
limits.memory
。查看容器日志,可能有 OOM 相关的提示。调大limits.memory
。分析应用代码是否存在内存泄漏。优化应用内存使用。
- 排查: 应用尝试使用的内存超过了 Pod 的
- CPU 限制导致性能差:
- 问题: 应用响应延迟高,但 CPU 利用率未达到 100%。
- 排查: 可能是 CPU
limits.cpu
设置过低,导致应用被频繁限流 (throttling)。在kubectl describe pod
的 Events 中可能看到 CPU 相关的警告。适当调大limits.cpu
。分析应用代码,优化 CPU 密集型操作。
- 优雅停机问题:
- 问题: Pod 在终止时没有等待当前请求完成就被杀死。
- 排查: 确保 Spring Boot 应用已正确配置优雅停机。确保 Deployment 的
terminationGracePeriodSeconds
设置得比应用完成未完成请求所需的最长时间长。确保 Service 的 Endpoint Controller 在 Pod 进入 Terminating 状态后有足够时间更新 Endpoints 列表。
未来展望 (Future Outlook)
- Spring Boot Native Images: 利用 GraalVM 将 Spring Boot 应用编译为原生可执行文件,极大地缩短启动时间、降低内存占用,非常适合云原生和 Serverless 场景,进一步提升在 K8s 中的部署效率和资源利用率。
- 更强的 K8s 原生集成: Spring Cloud Kubernetes 等项目将提供与 K8s API 更深度的集成,简化服务发现、配置管理等。
- 服务网格集成: 与 Istio, Linkerd 等服务网格更紧密集成,将流量管理、安全、可观测性等功能下沉到基础设施层。
- 自动化性能优化: 基于 K8s 监控数据,结合 AI/ML 自动调整资源分配、HPA 参数。
技术趋势与挑战 (Technology Trends 和 Challenges)
技术趋势:
- 云原生和容器化普及: K8s 成为主流应用部署平台。
- 微服务和服务网格: 应用架构向更细粒度的服务发展。
- Serverless 架构: 与 K8s 一起成为构建现代应用的重要选择。
- 自动化和智能化运维: 利用工具和 AI 简化复杂的运维任务。
- 高性能和低资源占用: 对应用启动速度和运行时资源消耗提出更高要求。
挑战:
- 分布式系统的复杂性: 管理和调试由大量微服务组成的复杂 K8s 应用。
- 性能调优: 在容器化、多层次抽象的环境中定位和解决性能瓶颈。
- 安全性: 容器镜像安全、运行时安全、网络安全、配置安全等。
- 数据管理: 管理有状态应用和持久化数据在 K8s 中的挑战。
- 成本优化: 合理分配资源、选择合适的实例类型、优化应用,降低云成本。
- 技术栈演进: 快速学习和适应不断发展的云原生技术栈。
总结 (Conclusion)
将 Spring Boot 应用部署到 Kubernetes 集群,是构建可伸缩、有韧性、易于管理的云原生应用的最佳实践。通过遵循本文介绍的核心策略,包括优化容器镜像、外部化配置、合理配置健康检查和资源限制、利用 K8s 的伸缩和自愈能力,并注意优雅停机和日志管理,您可以充分发挥 Spring Boot 和 Kubernetes 的强大组合优势。
虽然实际生产环境的部署和调优可能涉及更复杂的配置和工具,但掌握本指南中提供的基础代码示例和最佳实践,将为您在 Kubernetes 上成功运行和管理 Spring Boot 应用奠定坚实的基础,并帮助您应对未来云原生开发中的挑战。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)