云原生Java应用部署:将Spring Boot应用迁移至Kubernetes完整指南

目录

    • 前言
    • 摘要
    • [1. 引言](#1. 引言)
      • [1.1 为什么需要云原生迁移](#1.1 为什么需要云原生迁移)
      • [1.2 本文目标读者](#1.2 本文目标读者)
      • [1.3 技术栈概览](#1.3 技术栈概览)
    • [2. 云原生架构概述](#2. 云原生架构概述)
      • [2.1 什么是云原生](#2.1 什么是云原生)
      • [2.2 云原生的核心优势](#2.2 云原生的核心优势)
      • [2.3 云原生成熟度模型](#2.3 云原生成熟度模型)
    • [3. Spring Boot框架介绍](#3. Spring Boot框架介绍)
      • [3.1 Spring Boot核心特性](#3.1 Spring Boot核心特性)
      • [3.2 Spring Boot 3.x新特性](#3.2 Spring Boot 3.x新特性)
      • [3.3 Spring Boot与云原生的契合点](#3.3 Spring Boot与云原生的契合点)
    • [4. Kubernetes核心概念详解](#4. Kubernetes核心概念详解)
      • [4.1 Kubernetes架构概](#4.1 Kubernetes架构概)
      • [4.2 核心资源对象](#4.2 核心资源对象)
      • [4.3 Kubernetes网络模型](#4.3 Kubernetes网络模型)
    • [5. Spring Boot应用容器化](#5. Spring Boot应用容器化)
      • [5.1 创建Spring Boot项目](#5.1 创建Spring Boot项目)
      • [5.2 编写应用主类和控制器](#5.2 编写应用主类和控制器)
      • [5.3 编写Dockerfile](#5.3 编写Dockerfile)
      • [5.4 构建和测试容器镜像](#5.4 构建和测试容器镜像)
    • [6. Kubernetes部署配置](#6. Kubernetes部署配置)
      • [6.1 创建命名空间](#6.1 创建命名空间)
      • [6.2 创建ConfigMap](#6.2 创建ConfigMap)
      • [6.3 创建Secret](#6.3 创建Secret)
      • [6.4 创建Deployment](#6.4 创建Deployment)
      • [6.5 创建Service](#6.5 创建Service)
      • [6.6 创建Ingress](#6.6 创建Ingress)
    • [7. 部署与验证](#7. 部署与验证)
      • [7.1 应用部署流程](#7.1 应用部署流程)
      • [7.2 执行部署命令](#7.2 执行部署命令)
      • [7.3 验证部署结果](#7.3 验证部署结果)
    • [8. 高级配置与优化](#8. 高级配置与优化)
      • [8.1 水平自动扩缩容(HPA)](#8.1 水平自动扩缩容(HPA))
      • [8.2 配置管理最佳实践](#8.2 配置管理最佳实践)
      • [8.3 日志与监控集成](#8.3 日志与监控集成)
    • [9. CI/CD流水线集成](#9. CI/CD流水线集成)
      • [9.1 GitOps工作流](#9.1 GitOps工作流)
      • [9.2 GitHub Actions示例](#9.2 GitHub Actions示例)
    • [10. 常见问题与解决方案](#10. 常见问题与解决方案)
      • [10.1 应用启动失败](#10.1 应用启动失败)
      • [10.2 服务无法访问](#10.2 服务无法访问)
      • [10.3 配置更新不生效](#10.3 配置更新不生效)
    • [11. 总结](#11. 总结)
    • 参考资料

前言

作为一名有多年Java开发经验的工程师,我深刻体会到传统部署模式的痛点:环境不一致导致的"在我机器上能运行"问题、手动部署的繁琐流程、扩容困难等。自从将Spring Boot应用迁移到Kubernetes后,这些问题都得到了系统性的解决。

本文是我将多个Spring Boot项目迁移至Kubernetes的经验总结,涵盖了从容器化到生产部署的完整流程。无论你是刚开始接触云原生,还是已经在使用Kubernetes,都能从本文中获得有价值的实践指导。

摘要

随着云原生技术的快速发展,越来越多的企业开始将传统应用迁移至Kubernetes平台。本文详细介绍了如何将Spring Boot应用从传统部署方式迁移至Kubernetes云原生环境的完整过程。内容涵盖云原生概念解析、Spring Boot应用容器化、Kubernetes核心资源对象配置、服务发现与负载均衡、配置管理、持久化存储、健康检查机制以及CI/CD流水线集成等关键技术点。通过本文的学习,读者将掌握Spring Boot应用云原生迁移的最佳实践,能够独立完成从开发到生产的全流程部署。

通过本文你将学到:

  • 云原生架构的核心概念与优势
  • Spring Boot应用Docker容器化最佳实践
  • Kubernetes核心资源对象(Deployment、Service、ConfigMap、Secret)的配置与管理
  • 生产环境的高可用部署策略
  • CI/CD流水线与Kubernetes的集成方案

1. 引言

1.1 为什么需要云原生迁移

在传统的应用部署模式中,开发团队往往面临着诸多挑战:环境不一致导致的"在我机器上能运行"问题、资源利用率低下、扩缩容困难、部署流程繁琐等。随着业务规模的快速增长,这些问题变得愈发突出,严重影响了开发效率和系统稳定性。

云原生(Cloud Native)作为一种现代化的应用架构理念,为解决上述问题提供了系统性的解决方案。通过容器化、微服务、DevOps等技术手段,云原生架构能够显著提升应用的弹性、可扩展性和运维效率。根据CNCF(云原生计算基金会)的调查报告,超过93%的组织正在使用或评估Kubernetes,云原生已经成为企业数字化转型的必然选择。

1.2 本文目标读者

本文适合以下读者群体:

读者类型 前置知识要求 学习目标
Java后端开发工程师 熟悉Spring Boot开发 掌握云原生部署技能
DevOps工程师 了解Docker基础 深入Kubernetes实践
架构师 具备分布式系统经验 设计云原生架构方案
运维工程师 熟悉Linux系统 掌握K8s运维管理

1.3 技术栈概览

本文涉及的主要技术栈如下:
🔧 运维层
⚙️ 编排层
📦 容器层
🖥️ 开发层
Spring Boot 3.x
Maven/Gradle
JDK 17+
Docker
Dockerfile
Docker Compose
Kubernetes
Helm
kubectl
Prometheus
Grafana
ELK Stack


2. 云原生架构概述

2.1 什么是云原生

云原生(Cloud Native)是一种构建和运行应用程序的方法,它充分利用云计算模型的优势,包括弹性、敏捷性和分布式特性。根据CNCF的定义,云原生技术包括容器、服务网格、微服务、不可变基础设施和声明式API等。

云原生的核心理念可以概括为以下几点:

容器化封装:将应用程序及其依赖项打包到容器中,实现环境的一致性和可移植性。容器镜像成为应用交付的标准单元,消除了"在我的机器上能运行"的问题。

动态编排:使用Kubernetes等容器编排平台管理容器的生命周期,实现自动化部署、扩缩容和故障恢复。编排系统能够根据负载情况动态调整资源分配。

微服务架构:将单体应用拆分为多个小型、独立的服务,每个服务专注于单一业务功能。微服务之间通过轻量级通信机制(如REST API、gRPC)进行交互。

DevOps实践:建立开发与运维之间的协作机制,实现持续集成、持续交付(CI/CD)。通过自动化流水线,代码变更能够快速、安全地部署到生产环境。

2.2 云原生的核心优势

相比传统部署模式,云原生架构具有以下显著优势:

特性 传统部署 云原生部署 优势说明
弹性伸缩 手动扩容,耗时数小时 自动扩缩容,秒级响应 应对流量波动更灵活
故障恢复 人工介入,恢复时间长 自动重启/迁移,秒级恢复 提升系统可用性
资源利用 固定资源分配,浪费严重 动态调度,按需分配 降低基础设施成本
部署效率 手动部署,易出错 自动化流水线,标准化 缩短发布周期
环境一致性 环境差异大,问题频发 容器化封装,环境统一 减少环境相关问题

2.3 云原生成熟度模型

企业在进行云原生转型时,通常会经历以下阶段:
阶段0

传统部署
阶段1

容器化
阶段2

编排管理
阶段3

DevOps集成
阶段4

云原生成熟

阶段0 - 传统部署:应用直接部署在物理服务器或虚拟机上,缺乏容器化和自动化能力。

阶段1 - 容器化:应用完成容器化封装,使用Docker进行本地开发和测试,但尚未使用编排系统。

阶段2 - 编排管理:引入Kubernetes进行容器编排,实现应用的自动化部署和管理。

阶段3 - DevOps集成:建立CI/CD流水线,实现代码到生产的自动化交付,开始采用GitOps实践。

阶段4 - 云原生成熟:全面采用云原生技术栈,包括服务网格、可观测性、混沌工程等,实现高度自动化和智能化运维。


3. Spring Boot框架介绍

3.1 Spring Boot核心特性

Spring Boot是Pivotal团队于2014年推出的Java快速开发框架,它基于Spring生态系统,通过"约定优于配置"的理念,大幅简化了Spring应用的初始搭建和开发过程。Spring Boot已经成为Java微服务开发的事实标准。

Spring Boot的核心特性包括:

自动配置(Auto-configuration) :Spring Boot会根据项目依赖自动配置Spring应用,减少大量的XML配置和样板代码。例如,当项目中引入了spring-boot-starter-web依赖,Spring Boot会自动配置嵌入式Tomcat、Spring MVC等组件。

起步依赖(Starter Dependencies) :Spring Boot提供了一系列"起步依赖",将常用的依赖组合打包。开发者只需引入一个starter依赖,就能获得一组协调好版本的依赖集合。例如,spring-boot-starter-data-jpa包含了JPA、Hibernate、Spring Data JPA等依赖。

嵌入式容器(Embedded Containers):Spring Boot内置了Tomcat、Jetty、Undertow等Servlet容器,应用可以打包为独立的可执行JAR文件,无需部署到外部应用服务器。

生产就绪特性(Production-ready Features):Spring Boot Actuator提供了健康检查、指标收集、外部化配置等生产环境必需的功能,开箱即用。

3.2 Spring Boot 3.x新特性

2022年发布的Spring Boot 3.x带来了多项重要更新:

特性 说明 影响
JDK 17基线 最低要求JDK 17 利用Java新特性,提升性能
Jakarta EE 9+ 从javax迁移到jakarta命名空间 与Java EE生态保持一致
原生编译支持 支持GraalVM原生镜像编译 启动时间缩短至毫秒级
可观测性增强 集成Micrometer Observation API 简化分布式追踪集成
虚拟线程支持 支持JDK 21虚拟线程(Project Loom) 大幅提升并发性能

3.3 Spring Boot与云原生的契合点

Spring Boot的设计理念与云原生架构高度契合:

快速启动:Spring Boot应用的启动时间通常在几秒到十几秒,非常适合容器化环境中的快速扩容场景。

无状态设计:Spring Boot鼓励无状态应用设计,将状态存储在外部(如Redis、数据库),这与Kubernetes的Pod生命周期管理完美匹配。

配置外置:Spring Boot支持多种外部化配置方式,包括环境变量、配置文件、配置中心等,便于在Kubernetes中使用ConfigMap和Secret。

健康检查:Spring Boot Actuator提供了开箱即用的健康检查端点,可以轻松集成到Kubernetes的探针机制中。


4. Kubernetes核心概念详解

4.1 Kubernetes架构概

Kubernetes(简称K8s)是Google开源的容器编排平台,用于自动化部署、扩展和管理容器化应用。Kubernetes采用Master-Worker架构,由控制平面(Control Plane)和工作节点(Worker Node)组成。
💻 工作节点 2
💻 工作节点 1
🎛️ 控制平面 (Control Plane)
kube-apiserver

API服务器
etcd

键值存储
kube-scheduler

调度器
kube-controller-manager

控制器管理器
cloud-controller-manager

云控制器管理器
kubelet
kube-proxy
Container Runtime
Pod 1
Pod 2
kubelet
kube-proxy
Container Runtime
Pod 3
Pod 4

控制平面组件

  • kube-apiserver:集群的统一入口,所有请求都通过API Server进行处理。它负责认证、授权、准入控制等功能。

  • etcd:高可用的键值存储,保存集群的所有状态数据。etcd是Kubernetes的数据 backbone,需要做好备份和高可用部署。

  • kube-scheduler:负责Pod的调度,根据资源需求、亲和性规则、污点容忍等策略,将Pod分配到合适的工作节点。

  • kube-controller-manager:运行各种控制器,如Deployment Controller、ReplicaSet Controller、Node Controller等,负责维护集群的期望状态。

工作节点组件

  • kubelet:节点代理,负责与API Server通信,管理本节点上的Pod生命周期,执行健康检查。

  • kube-proxy:维护节点上的网络规则,实现Service的负载均衡和服务发现。

  • Container Runtime:容器运行时,负责运行容器。Kubernetes支持多种运行时,如containerd、CRI-O、Docker(已弃用,通过containerd兼容)。

4.2 核心资源对象

Kubernetes定义了一系列资源对象来描述应用的部署状态:

Pod:Kubernetes中最小的部署单元,包含一个或多个容器。Pod内的容器共享网络命名空间和存储卷,可以通过localhost互相访问。

Deployment:管理无状态应用的控制器,负责维护Pod的副本数量,支持滚动更新和回滚。Deployment通过ReplicaSet间接管理Pod。

Service:定义一组Pod的访问策略,提供稳定的访问入口。Service通过Label Selector找到对应的Pod,支持ClusterIP、NodePort、LoadBalancer等多种类型。

ConfigMap:存储非敏感的配置数据,可以以环境变量或配置文件的形式挂载到Pod中。

Secret:存储敏感信息(如密码、证书、Token),数据经过Base64编码,使用时需要谨慎处理。

PersistentVolume(PV)与PersistentVolumeClaim(PVC):提供持久化存储能力,将存储资源与应用解耦。

4.3 Kubernetes网络模型

Kubernetes采用扁平的网络模型,要求所有Pod都在同一个扁平的IP地址空间中,可以直接互相通信,无需NAT(网络地址转换)。
🌐 Kubernetes 集群网络
Service ClusterIP

10.96.0.1
Pod 1

10.244.1.2
Pod 2

10.244.1.3
Pod 3

10.244.2.2
Ingress Controller
外部流量

Service类型说明

Service类型 访问范围 使用场景 示例
ClusterIP 集群内部 内部服务通信 数据库、缓存服务
NodePort 集群外部(通过节点端口) 开发测试环境 30000-32767端口
LoadBalancer 集群外部(通过云负载均衡器) 生产环境对外服务 Web应用、API服务
ExternalName 映射到外部DNS 访问外部服务 外部数据库

5. Spring Boot应用容器化

5.1 创建Spring Boot项目

首先,我们创建一个标准的Spring Boot项目。可以使用Spring Initializr(https://start.spring.io)或IDE插件快速生成项目骨架。

项目依赖配置(pom.xml)

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.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>demo-application</artifactId>
    <version>1.0.0</version>
    <name>demo-application</name>
    <description>Spring Boot Kubernetes Demo</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>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

上述Maven配置文件定义了一个标准的Spring Boot Web项目。其中spring-boot-starter-web提供了Web开发所需的所有依赖,包括嵌入式Tomcat、Spring MVC、Jackson等。spring-boot-starter-actuator提供了生产就绪特性,如健康检查、指标收集等,这对于Kubernetes环境至关重要。spring-boot-maven-plugin插件用于打包可执行的JAR文件。

5.2 编写应用主类和控制器

应用主类(Application.java)

java 复制代码
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

健康检查控制器(HealthController.java)

java 复制代码
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HealthController {
    
    @Value("${spring.application.name:demo-application}")
    private String applicationName;
    
    @Value("${app.version:1.0.0}")
    private String version;
    
    @GetMapping("/health")
    public Map<String, Object> health() {
        Map<String, Object> response = new HashMap<>();
        response.put("status", "UP");
        response.put("application", applicationName);
        response.put("version", version);
        response.put("timestamp", System.currentTimeMillis());
        return response;
    }
    
    @GetMapping("/hello")
    public String hello() {
        return "Hello from " + applicationName + " v" + version;
    }
}

上述代码定义了两个简单的REST端点。/health端点返回应用的健康状态信息,包括应用名称、版本和时间戳,这个端点可以用于Kubernetes的健康检查。/hello端点返回一个简单的问候消息,用于验证应用是否正常运行。通过@Value注解,我们从配置文件或环境变量中读取应用名称和版本信息,这样可以实现配置的外置化。

5.3 编写Dockerfile

Dockerfile是构建容器镜像的脚本文件,定义了镜像的构建步骤。对于Spring Boot应用,我们需要编写一个优化的Dockerfile。

多阶段构建Dockerfile

dockerfile 复制代码
# ==================== 构建阶段 ====================
FROM eclipse-temurin:17-jdk-alpine AS builder

# 设置工作目录
WORKDIR /build

# 复制Maven配置文件和源代码
COPY pom.xml .
COPY src ./src

# 安装Maven并构建应用
# 使用Alpine的apk包管理器安装Maven
RUN apk add --no-cache maven && \
    mvn clean package -DskipTests

# ==================== 运行阶段 ====================
FROM eclipse-temurin:17-jre-alpine

# 创建非root用户运行应用(安全最佳实践)
RUN addgroup -S spring && adduser -S spring -G spring

# 设置工作目录
WORKDIR /app

# 从构建阶段复制JAR文件
COPY --from=builder /build/target/*.jar app.jar

# 更改文件所有者
RUN chown -R spring:spring /app

# 切换到非root用户
USER spring:spring

# 暴露应用端口
EXPOSE 8080

# 设置JVM参数(针对容器环境优化)
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"

# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

这个Dockerfile采用了多阶段构建(Multi-stage Build)模式,这是Docker镜像构建的最佳实践。第一阶段(builder)使用JDK镜像编译应用,第二阶段使用更小的JRE镜像运行应用,最终镜像体积大幅减小。我们还创建了非root用户来运行应用,这是容器安全的基本要求。JAVA_OPTS环境变量设置了JVM参数,UseContainerSupportMaxRAMPercentage参数让JVM能够正确识别容器的资源限制。

5.4 构建和测试容器镜像

构建镜像命令

bash 复制代码
# 构建镜像
docker build -t demo-application:1.0.0 .

# 查看镜像大小
docker images demo-application:1.0.0

# 本地运行测试
docker run -d --name demo-app \
    -p 8080:8080 \
    -e SPRING_PROFILES_ACTIVE=dev \
    demo-application:1.0.0

# 测试健康检查端点
curl http://localhost:8080/health

# 查看容器日志
docker logs demo-app

# 停止并删除容器
docker stop demo-app && docker rm demo-app

上述命令演示了Docker镜像的完整生命周期管理。首先使用docker build命令构建镜像,-t参数指定镜像名称和标签。然后使用docker run命令启动容器,-d参数让容器在后台运行,-p参数映射端口,-e参数设置环境变量。最后通过curl命令测试应用是否正常运行。


6. Kubernetes部署配置

6.1 创建命名空间

命名空间(Namespace)是Kubernetes中用于资源隔离的逻辑分区。在生产环境中,通常会为不同的环境(dev、test、prod)或不同的团队创建独立的命名空间。

命名空间定义(namespace.yaml)

yaml 复制代码
apiVersion: v1
kind: Namespace
metadata:
  name: demo-application
  labels:
    name: demo-application
    environment: production
    team: backend

这个YAML文件定义了一个名为demo-application的命名空间,并添加了标签用于资源分类和管理。标签(Labels)是Kubernetes中用于组织和选择资源的关键机制。

6.2 创建ConfigMap

ConfigMap用于存储非敏感的配置数据,可以将配置与镜像解耦,实现配置的动态更新。

ConfigMap定义(configmap.yaml)

yaml 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-application-config
  namespace: demo-application
data:
  # 应用配置
  application.yml: |
    server:
      port: 8080
      shutdown: graceful
    
    spring:
      application:
        name: demo-application
      lifecycle:
        timeout-per-shutdown-phase: 30s
    
    management:
      endpoints:
        web:
          exposure:
            include: health,info,metrics,prometheus
      endpoint:
        health:
          show-details: always
          probes:
            enabled: true
    
    logging:
      level:
        root: INFO
        com.example: DEBUG
  
  # 环境变量形式
  SPRING_APPLICATION_NAME: "demo-application"
  APP_VERSION: "1.0.0"
  JAVA_OPTS: "-Xms256m -Xmx512m -XX:+UseContainerSupport"

这个ConfigMap包含了两种配置方式:一种是以配置文件形式(application.yml),另一种是以环境变量形式。配置文件方式适合复杂的配置场景,环境变量方式适合简单的键值对配置。注意management.endpoint.health.probes.enabled: true配置,它启用了Kubernetes存活探针和就绪探针的支持。

6.3 创建Secret

Secret用于存储敏感信息,如数据库密码、API密钥等。Secret中的数据需要使用Base64编码。

Secret定义(secret.yaml)

yaml 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: demo-application-secret
  namespace: demo-application
type: Opaque
data:
  # Base64编码的敏感数据
  # 原始值: db_password_123
  DB_PASSWORD: ZGJfcGFzc3dvcmRfMTIz
  # 原始值: api_key_secret
  API_KEY: YXBpX2tleV9zZWNyZXQ=
---
# 也可以使用stringData字段,Kubernetes会自动编码
apiVersion: v1
kind: Secret
metadata:
  name: demo-application-secret-v2
  namespace: demo-application
type: Opaque
stringData:
  DB_USERNAME: "admin"
  DB_PASSWORD: "secure_password_here"

上述Secret定义展示了两种方式:data字段需要手动Base64编码,stringData字段可以明文写入,Kubernetes会自动编码。在生产环境中,建议使用密钥管理系统(如Vault、AWS Secrets Manager)来管理敏感信息,而不是直接写入Secret文件。

6.4 创建Deployment

Deployment是管理无状态应用的核心控制器,负责维护Pod的副本数量和滚动更新。

Deployment定义(deployment.yaml)

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-application
  namespace: demo-application
  labels:
    app: demo-application
    version: v1
spec:
  # 副本数量
  replicas: 3
  
  # 选择器,用于关联Pod
  selector:
    matchLabels:
      app: demo-application
  
  # 滚动更新策略
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 滚动更新时最多可以超出期望副本数的数量
      maxUnavailable: 0    # 滚动更新时最多不可用的副本数
  
  # Pod模板
  template:
    metadata:
      labels:
        app: demo-application
        version: v1
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/actuator/prometheus"
    spec:
      # 优雅终止时间
      terminationGracePeriodSeconds: 30
      
      # 容器定义
      containers:
        - name: demo-application
          image: demo-application:1.0.0
          imagePullPolicy: IfNotPresent
          
          # 端口配置
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          
          # 环境变量
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: "kubernetes"
            - name: JAVA_OPTS
              valueFrom:
                configMapKeyRef:
                  name: demo-application-config
                  key: JAVA_OPTS
          
          # 从ConfigMap挂载配置文件
          volumeMounts:
            - name: config-volume
              mountPath: /config
              readOnly: true
          
          # 资源限制
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          
          # 存活探针 - 检测容器是否存活
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          
          # 就绪探针 - 检测容器是否准备好接收流量
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 3
          
          # 启动探针 - 检测应用是否启动完成
          startupProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 30  # 最多等待150秒
      
      # 卷定义
      volumes:
        - name: config-volume
          configMap:
            name: demo-application-config
            items:
              - key: application.yml
                path: application.yml

这个Deployment配置非常全面,包含了生产环境部署的所有关键配置。replicas: 3确保应用有3个副本,提供高可用性。滚动更新策略设置了maxSurge: 1maxUnavailable: 0,确保更新过程中服务不中断。资源限制(resources)设置了请求和上限,防止应用占用过多资源。三种探针(liveness、readiness、startup)实现了完善的健康检查机制。ConfigMap以卷的形式挂载到容器中,实现了配置的外置化。

6.5 创建Service

Service为Pod提供稳定的访问入口,实现服务发现和负载均衡。

Service定义(service.yaml)

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: demo-application
  namespace: demo-application
  labels:
    app: demo-application
spec:
  type: ClusterIP
  selector:
    app: demo-application
  ports:
    - name: http
      port: 8080        # Service端口
      targetPort: 8080  # Pod端口
      protocol: TCP
---
# NodePort类型的服务,用于外部访问(开发测试环境)
apiVersion: v1
kind: Service
metadata:
  name: demo-application-nodeport
  namespace: demo-application
spec:
  type: NodePort
  selector:
    app: demo-application
  ports:
    - name: http
      port: 8080
      targetPort: 8080
      nodePort: 30080  # 节点端口(范围:30000-32767)

上述定义创建了两个Service:一个是ClusterIP类型,只能在集群内部访问,适合内部服务通信;另一个是NodePort类型,可以通过任意节点的30080端口从外部访问,适合开发测试环境。在生产环境中,通常会使用Ingress或LoadBalancer类型的Service来暴露服务。

6.6 创建Ingress

Ingress是Kubernetes的API对象,用于管理外部访问集群内服务的HTTP/HTTPS路由。

Ingress定义(ingress.yaml)

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-application-ingress
  namespace: demo-application
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  
  # TLS配置
  tls:
    - hosts:
        - demo.example.com
      secretName: demo-application-tls
  
  # 路由规则
  rules:
    - host: demo.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: demo-application
                port:
                  number: 8080

这个Ingress配置使用Nginx Ingress Controller作为入口控制器,配置了TLS证书(通过cert-manager自动管理)和路由规则。所有访问demo.example.com的请求都会被路由到demo-application服务的8080端口。annotations字段配置了Nginx特定的行为,如SSL重定向、请求体大小限制等。


7. 部署与验证

7.1 应用部署流程

将Spring Boot应用部署到Kubernetes的完整流程如下:


编写应用代码
编写Dockerfile
构建容器镜像
推送镜像到仓库
编写K8s资源文件
应用K8s资源
验证部署状态
部署成功?
配置Ingress/负载均衡
排查问题
监控与运维

7.2 执行部署命令

bash 复制代码
# 1. 创建命名空间
kubectl apply -f namespace.yaml

# 2. 创建ConfigMap和Secret
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml

# 3. 创建Deployment
kubectl apply -f deployment.yaml

# 4. 创建Service
kubectl apply -f service.yaml

# 5. 创建Ingress(如果需要外部访问)
kubectl apply -f ingress.yaml

# 6. 查看部署状态
kubectl get all -n demo-application

# 7. 查看Pod详情
kubectl describe pod -n demo-application -l app=demo-application

# 8. 查看应用日志
kubectl logs -n demo-application -l app=demo-application -f

# 9. 进入容器调试
kubectl exec -it -n demo-application deployment/demo-application -- /bin/sh

7.3 验证部署结果

检查Pod状态

bash 复制代码
# 查看Pod列表
kubectl get pods -n demo-application -o wide

# 预期输出:
# NAME                                READY   STATUS    RESTARTS   AGE
# demo-application-6b8b9c8d4-abc12   1/1     Running   0          2m
# demo-application-6b8b9c8d4-def34   1/1     Running   0          2m
# demo-application-6b8b9c8d4-ghi56   1/1     Running   0          2m

测试应用访问

bash 复制代码
# 端口转发(本地测试)
kubectl port-forward -n demo-application svc/demo-application 8080:8080

# 测试健康检查
curl http://localhost:8080/actuator/health

# 预期输出:
# {
#   "status": "UP",
#   "groups": ["liveness", "readiness"],
#   ...
# }

8. 高级配置与优化

8.1 水平自动扩缩容(HPA)

Kubernetes的Horizontal Pod Autoscaler(HPA)可以根据CPU/内存使用率或自定义指标自动调整Pod副本数量。

HPA定义(hpa.yaml)

yaml 复制代码
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: demo-application-hpa
  namespace: demo-application
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: demo-application
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 100
          periodSeconds: 15
        - type: Pods
          value: 4
          periodSeconds: 15
      selectPolicy: Max

这个HPA配置设置了最小2个副本、最大10个副本的弹性伸缩范围。当CPU使用率超过70%或内存使用率超过80%时,HPA会自动增加Pod数量。behavior字段定义了扩缩容的行为策略:缩容时等待300秒的稳定窗口,每次最多减少10%的Pod;扩容时等待60秒的稳定窗口,每次最多增加100%或4个Pod(取较大值)。

8.2 配置管理最佳实践

在Kubernetes环境中管理Spring Boot应用配置,需要遵循以下最佳实践:

配置类型 推荐方式 说明
环境特定配置 ConfigMap 不同环境使用不同的ConfigMap
敏感信息 Secret + 外部密钥管理 使用Vault等工具管理敏感信息
启动参数 Deployment env JVM参数、Profile等
配置热更新 Spring Cloud Kubernetes 支持ConfigMap变更自动刷新

8.3 日志与监控集成

Prometheus监控集成

Spring Boot Actuator原生支持Prometheus指标暴露,只需添加依赖并配置:

xml 复制代码
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

Prometheus ServiceMonitor配置

yaml 复制代码
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: demo-application
  namespace: demo-application
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: demo-application
  endpoints:
    - port: http
      path: /actuator/prometheus
      interval: 30s

9. CI/CD流水线集成

9.1 GitOps工作流

GitOps是一种现代化的持续交付方法,以Git仓库作为单一事实来源,通过Git提交触发应用部署。
开发者提交代码
Git仓库
CI流水线构建镜像
推送镜像到仓库
更新GitOps仓库
ArgoCD检测变更
同步到Kubernetes
应用更新完成

9.2 GitHub Actions示例

CI/CD流水线配置(.github/workflows/deploy.yaml)

yaml 复制代码
name: Build and Deploy

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
      
      - name: Build with Maven
        run: mvn clean package -DskipTests
      
      - name: Run tests
        run: mvn test
      
      - name: Build Docker image
        run: |
          docker build -t $REGISTRY/$IMAGE_NAME:${{ github.sha }} .
          docker tag $REGISTRY/$IMAGE_NAME:${{ github.sha }} $REGISTRY/$IMAGE_NAME:latest
      
      - name: Login to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Push Docker image
        run: |
          docker push $REGISTRY/$IMAGE_NAME:${{ github.sha }}
          docker push $REGISTRY/$IMAGE_NAME:latest
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Set up kubectl
        uses: azure/setup-kubectl@v3
      
      - name: Configure kubeconfig
        run: |
          mkdir -p ~/.kube
          echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config
      
      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/demo-application \
            demo-application=$REGISTRY/$IMAGE_NAME:${{ github.sha }} \
            -n demo-application
      
      - name: Wait for rollout
        run: |
          kubectl rollout status deployment/demo-application \
            -n demo-application --timeout=300s

这个GitHub Actions工作流实现了完整的CI/CD流程:代码提交后自动构建、测试、打包Docker镜像、推送到镜像仓库,然后更新Kubernetes部署。kubectl set image命令触发滚动更新,kubectl rollout status等待更新完成。


10. 常见问题与解决方案

10.1 应用启动失败

问题现象 :Pod状态为CrashLoopBackOffError

排查步骤

bash 复制代码
# 查看Pod事件
kubectl describe pod <pod-name> -n demo-application

# 查看容器日志
kubectl logs <pod-name> -n demo-application --previous

# 检查资源限制
kubectl get pod <pod-name> -n demo-application -o yaml | grep -A 10 resources

常见原因与解决方案

原因 解决方案
内存不足 增加resources.limits.memory
健康检查失败 调整探针的initialDelaySeconds
配置错误 检查ConfigMap和Secret配置
端口冲突 确认containerPort配置正确

10.2 服务无法访问

问题现象:Service创建成功,但无法访问应用。

排查步骤

bash 复制代码
# 检查Service端点
kubectl get endpoints -n demo-application

# 检查Pod标签
kubectl get pods -n demo-application --show-labels

# 测试Service DNS解析
kubectl run test --rm -it --image=busybox -- nslookup demo-application.demo-application.svc.cluster.local

常见原因:Service的selector与Pod标签不匹配是最常见的原因。确保Deployment中Pod模板的labels与Service的selector.matchLabels完全一致。

10.3 配置更新不生效

问题现象:修改ConfigMap后,应用配置未更新。

解决方案

Spring Boot默认不会自动重新加载配置。有以下几种解决方案:

  1. 重启Pod:修改ConfigMap后手动重启Deployment

    bash 复制代码
    kubectl rollout restart deployment/demo-application -n demo-application
  2. 使用Spring Cloud Kubernetes:添加依赖实现配置热更新

    xml 复制代码
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
    </dependency>
  3. 使用ConfigMap版本控制:在ConfigMap名称中包含版本号,更新时修改Deployment引用


11. 总结

本文详细介绍了将Spring Boot应用迁移至Kubernetes云原生环境的完整过程,从云原生概念、Spring Boot框架特性、Kubernetes核心概念,到容器化实践、部署配置、CI/CD集成,覆盖了云原生迁移的方方面面。

核心要点总结

  1. 容器化是基础:使用多阶段构建Dockerfile,优化镜像体积,确保安全(非root用户运行)。

  2. 配置外置化:使用ConfigMap和Secret管理配置,实现配置与镜像解耦,便于环境迁移。

  3. 健康检查必不可少:配置存活探针、就绪探针和启动探针,确保Kubernetes能够正确管理应用生命周期。

  4. 资源限制要合理:设置合适的resources.requests和resources.limits,防止资源争抢和浪费。

  5. 弹性伸缩提升可用性:配置HPA实现自动扩缩容,应对流量波动。

  6. CI/CD自动化部署:建立GitOps工作流,实现代码到生产的自动化交付。

思考题

  1. 在你的项目中,哪些Spring Boot应用适合迁移到Kubernetes?哪些应用可能不适合?

  2. 如何设计一个支持蓝绿发布和金丝雀发布的Kubernetes部署方案?

  3. 对于有状态应用(如数据库、消息队列),在Kubernetes中部署需要注意哪些问题?


参考资料

相关推荐
曹牧2 小时前
Tomcat 启动内存的设置
java·tomcat
ywf12152 小时前
java进阶1——JVM
java·开发语言·jvm
鱼鳞_2 小时前
Java学习笔记_Day18(数据结构)
java·笔记·学习
会飞的大可2 小时前
Docker Compose 多服务编排实战:从零搭建微服务架构
docker·微服务·架构
mygljx5 小时前
SpringBoot+Mybatis-plus实现分页查询(一看就会)
spring boot·mybatis·状态模式
无巧不成书021810 小时前
30分钟入门Java:从历史到Hello World的小白指南
java·开发语言
zs宝来了12 小时前
Playwright 自动发布 CSDN 的完整实践
java
彭于晏Yan13 小时前
Redisson分布式锁
spring boot·redis·分布式