Web09-Spring Boot部署:使用Docker与Kubernetes

Spring Boot部署:使用Docker与Kubernetes

在现代应用开发中,Spring Boot凭借其“约定优于配置”的理念和快速开发特性,成为Java开发者的首选框架。但随着微服务架构的普及,Spring Boot应用的部署面临着环境一致性、** scalability**、运维复杂度等多重挑战。Docker的出现解决了“开发环境能跑,生产环境跑不了”的痛点,而Kubernetes则进一步提供了容器编排、自动扩缩容、服务发现等核心能力,让Spring Boot应用的部署和运维进入自动化时代。

本文将从实战角度出发,详细讲解如何用Docker容器化Spring Boot应用,以及如何通过Kubernetes实现规模化部署和管理。全文包含大量代码示例、配置文件和操作命令,覆盖从基础镜像构建到生产级部署的完整流程,适合Spring Boot开发者、运维工程师和DevOps实践者参考。

一、Spring Boot部署的挑战与解决方案

在传统部署模式中,Spring Boot应用的交付和运维面临诸多问题,这些问题在微服务架构下被进一步放大。

1. 传统部署的痛点

  • 环境不一致:开发、测试、生产环境的JDK版本、依赖库、配置参数差异,导致“在我电脑上能跑”的经典问题。某团队曾因测试环境使用JDK 11而生产环境为JDK 8,导致Spring Boot 2.6+应用启动失败(依赖JDK 11特性)。
  • 部署流程繁琐:手动上传JAR包、配置环境变量、启停服务,步骤多易出错。尤其在多实例部署时,一致性难以保证。
  • 资源利用率低:每个应用独占虚拟机资源,空闲时资源浪费严重,扩容需手动申请和配置虚拟机。
  • 运维成本高:服务监控、日志收集、故障恢复依赖人工操作,缺乏自动化机制,夜间故障响应延迟。

2. Docker+Kubernetes的解决方案

  • 环境一致性:Docker镜像包含应用及所有依赖,确保“一次构建,到处运行”,消除环境差异。
  • 轻量级隔离:容器共享主机内核但拥有独立文件系统,比虚拟机更轻量,资源利用率提升30%+。
  • 自动化编排:Kubernetes实现容器的自动部署、扩缩容、滚动更新和自愈,减少人工干预。
  • 服务治理:内置服务发现、负载均衡、配置管理,简化微服务架构的运维复杂度。

某电商平台的实践显示:采用Docker+Kubernetes部署Spring Boot微服务后,环境问题减少70%,部署时间从小时级缩短至分钟级,服务器资源利用率从40%提升至75%。

二、Docker基础:容器化Spring Boot应用

Docker是一种轻量级容器技术,通过镜像打包应用及依赖,实现跨环境的一致运行。本节将从Docker核心概念出发,详解Spring Boot应用的容器化过程。

1. Docker核心概念

  • 镜像(Image):应用及其依赖的只读模板,如Spring Boot应用的JAR包+JRE环境。
  • 容器(Container):镜像的运行实例,可被创建、启动、停止、删除,是动态的。
  • Dockerfile:用于构建镜像的文本文件,包含一系列构建指令。
  • 仓库(Repository):存储和分发镜像的平台,如Docker Hub、私有仓库。

2. 准备Spring Boot应用

首先创建一个简单的Spring Boot应用作为示例,后续将其容器化。

(1)创建Spring Boot项目

使用Spring Initializr创建项目,添加Spring Web依赖,项目结构如下:

spring-boot-docker-demo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           ├── controller/
│   │   │           │   └── DemoController.java
│   │   │           └── SpringBootDockerDemoApplication.java
│   │   └── resources/
│   │       └── application.properties
├── pom.xml
└── Dockerfile
(2)编写测试接口

DemoController中添加一个简单接口,用于验证应用运行状态:

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot with Docker!";
    }

    @GetMapping("/health")
    public String health() {
        return "OK";
    }
}
(3)配置端口

application.properties中设置服务端口:

server.port=8080
(4)打包应用

执行Maven命令打包生成JAR包:

mvn clean package -Dmaven.test.skip=true

打包成功后,在target目录下生成spring-boot-docker-demo-0.0.1-SNAPSHOT.jar

3. 编写基础Dockerfile

Dockerfile是构建镜像的“食谱”,定义了从基础镜像到应用部署的所有步骤。为Spring Boot应用编写基础Dockerfile:

# 基础镜像:使用官方OpenJDK 17镜像(Alpine版本体积更小)
FROM openjdk:17-jdk-slim

# 维护者信息
LABEL maintainer="yourname@example.com"

# 容器内工作目录
WORKDIR /app

# 将本地JAR包复制到容器内
COPY target/spring-boot-docker-demo-0.0.1-SNAPSHOT.jar app.jar

# 暴露应用端口(与application.properties中一致)
EXPOSE 8080

# 启动命令:运行Spring Boot应用
ENTRYPOINT ["java", "-jar", "app.jar"]
指令说明:
  • FROM:指定基础镜像,这里选择轻量级的openjdk:17-jdk-slim(比openjdk:17体积小约50%)。
  • WORKDIR:设置容器内的工作目录,后续命令将基于此目录执行。
  • COPY:将主机的JAR包复制到容器内的/app目录。
  • EXPOSE:声明容器运行时暴露的端口(仅作文档说明,实际需通过-p映射)。
  • ENTRYPOINT:容器启动时执行的命令,这里通过java -jar启动Spring Boot应用。

4. 构建并运行Docker镜像

(1)构建镜像

在Dockerfile所在目录执行以下命令构建镜像(注意末尾的.表示当前目录):

docker build -t spring-boot-docker-demo:v1 .
  • -t:为镜像指定标签(格式为仓库名:标签),方便后续引用。
  • 构建成功后,通过docker images查看镜像:
docker images | grep spring-boot-docker-demo
# 输出:spring-boot-docker-demo   v1      abc12345   2分钟前   420MB
(2)运行容器

使用docker run命令启动容器,并将容器的8080端口映射到主机的8080端口:

docker run -d -p 8080:8080 --name demo-app spring-boot-docker-demo:v1
  • -d:后台运行容器。
  • -p 8080:8080:将主机的8080端口映射到容器的8080端口(主机端口:容器端口)。
  • --name:为容器指定名称,方便管理。
(3)验证应用运行

通过docker ps查看运行中的容器:

docker ps | grep demo-app
# 输出容器ID、状态等信息

访问http://localhost:8080/hello,若返回Hello, Spring Boot with Docker!,说明应用部署成功。

(4)查看容器日志

通过docker logs命令查看应用日志:

docker logs -f demo-app  # -f:实时跟踪日志

可看到Spring Boot的启动日志,确认应用正常启动。

(5)停止并删除容器
# 停止容器
docker stop demo-app

# 删除容器
docker rm demo-app

# 删除镜像(可选)
docker rmi spring-boot-docker-demo:v1

5. 优化Docker镜像:多阶段构建

基础镜像构建的Spring Boot镜像体积较大(通常400MB+),主要原因是包含了完整的JDK和构建依赖。通过多阶段构建可显著减小镜像体积。

多阶段构建的原理:
  • 第一阶段(构建阶段):使用包含Maven/Gradle的镜像编译源码、打包JAR包。
  • 第二阶段(运行阶段):仅复制第一阶段的JAR包到轻量级基础镜像(如JRE),不含构建工具。
优化后的Dockerfile:
# 第一阶段:构建Spring Boot应用
FROM maven:3.8.5-openjdk-17 AS builder

# 设置工作目录
WORKDIR /app

# 复制pom.xml和源码
COPY pom.xml .
COPY src ./src

# 打包应用(跳过测试)
RUN mvn clean package -Dmaven.test.skip=true

# 第二阶段:运行应用(仅保留必要文件)
FROM openjdk:17-jre-slim

WORKDIR /app

# 从构建阶段复制JAR包到当前阶段
COPY --from=builder /app/target/spring-boot-docker-demo-0.0.1-SNAPSHOT.jar app.jar

# 非root用户运行(安全最佳实践)
RUN addgroup --system --gid 1001 appgroup && \
    adduser --system --uid 1001 --gid 1001 appuser
USER appuser

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]
优化点说明:
  • 多阶段构建AS builder标记第一阶段,第二阶段通过--from=builder复制JAR包,避免将Maven依赖和源码带入最终镜像。
  • 使用JRE而非JDK:运行Spring Boot应用只需JRE(Java Runtime Environment),无需JDK(Java Development Kit),镜像体积减少约30%。
  • 非root用户运行:创建appuser并切换为该用户运行容器,降低容器被入侵后的权限风险。
构建优化后的镜像:
docker build -t spring-boot-docker-demo:v2 .

通过docker images对比体积:v1约420MB,v2约280MB,体积减少33%。

6. 高级Dockerfile配置:自定义JVM参数

Spring Boot应用的性能与JVM参数密切相关,可在Dockerfile中通过ENTRYPOINT自定义JVM参数,如堆内存大小、GC策略等。

带JVM参数的Dockerfile片段:
# 自定义JVM参数(根据容器内存调整)
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"

# 启动命令中引用环境变量
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
运行时动态调整JVM参数:

启动容器时通过-e覆盖环境变量,灵活调整JVM参数:

docker run -d -p 8080:8080 -e JAVA_OPTS="-Xms512m -Xmx1024m" --name demo-app-v2 spring-boot-docker-demo:v2

三、Kubernetes基础:容器编排核心概念

Kubernetes(简称K8s)是开源的容器编排平台,提供容器的部署、扩展、负载均衡、自愈等能力。本节介绍K8s核心概念及与Spring Boot部署相关的核心组件。

1. K8s核心概念

  • Pod:K8s的最小部署单元,包含一个或多个容器,共享网络和存储。Spring Boot应用通常一个Pod运行一个容器。
  • Deployment:管理Pod的创建和更新,定义Pod的副本数、更新策略等,是部署无状态应用的首选。
  • Service:定义Pod的访问方式,提供固定访问地址和负载均衡,解决Pod动态IP变化的问题。
  • ConfigMap:存储非敏感配置数据(如应用.properties),可被Pod挂载为文件或环境变量。
  • Secret:存储敏感信息(如数据库密码、API密钥),与ConfigMap类似,但数据经过Base64编码。
  • Namespace:提供资源隔离,可按环境(dev/test/prod)或团队划分命名空间。
  • Node:K8s集群的工作节点(物理机或虚拟机),Pod运行在Node上。
  • Controller Manager:运行各种控制器(如Deployment控制器、StatefulSet控制器),实现自愈、扩缩容等功能。
  • kube-proxy:每个Node上的网络代理,负责Service的负载均衡和流量转发。

2. K8s部署架构

Spring Boot应用在K8s中的典型部署架构如下:

用户 → Service(负载均衡) → Deployment管理的多个Pod(Spring Boot容器) → 存储(ConfigMap/Secret/Volume)
  • 用户通过Service的固定IP或域名访问应用。
  • Service将请求转发到后端健康的Pod(通过标签选择器关联)。
  • Deployment确保Pod的副本数符合预期,若Pod故障则自动重建。
  • 应用配置通过ConfigMap/Secret注入,无需硬编码在镜像中。

3. 安装K8s环境(Minikube)

为了本地测试,推荐使用Minikube搭建单节点K8s集群:

(1)安装Minikube
minikube start --driver=docker  # 使用Docker驱动
(2)安装kubectl

kubectl是K8s的命令行工具,用于与集群交互:

kubectl get nodes  # 查看节点状态,输出Ready表示集群正常

四、Spring Boot在Kubernetes中的部署实践

本节通过完整示例讲解如何将Spring Boot应用部署到K8s,包括Deployment配置、Service暴露、配置管理等核心步骤。

1. 推送Docker镜像到仓库

K8s集群中的Node需要拉取Docker镜像,因此需将镜像推送到可访问的仓库(如Docker Hub、私有仓库)。

(1)登录Docker Hub
docker login  # 输入Docker Hub用户名和密码
(2)为镜像打标签

镜像标签需符合仓库格式(仓库用户名/镜像名:标签):

docker tag spring-boot-docker-demo:v2 yourusername/spring-boot-docker-demo:v2
(3)推送镜像
docker push yourusername/spring-boot-docker-demo:v2

推送成功后,可在Docker Hub上看到镜像。

2. 创建Deployment配置

Deployment是部署Spring Boot应用的核心资源,定义了Pod模板、副本数、更新策略等。

(1)基础Deployment配置(spring-boot-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-boot-demo  # Deployment名称
  namespace: default      # 命名空间(默认default)
  labels:
    app: spring-boot-demo # 标签,用于关联Service和Pod
spec:
  replicas: 2             # 副本数(2个Pod实例)
  selector:
    matchLabels:
      app: spring-boot-demo  # 匹配Pod的标签
  template:                # Pod模板
    metadata:
      labels:
        app: spring-boot-demo  # Pod标签,需与selector匹配
    spec:
      containers:
      - name: spring-boot-demo  # 容器名称
        image: yourusername/spring-boot-docker-demo:v2  # 镜像地址(替换为你的仓库)
        ports:
        - containerPort: 8080  # 容器暴露的端口(与应用端口一致)
        resources:            # 资源请求和限制
          requests:
            memory: "256Mi"  # 最小内存需求
            cpu: "200m"      # 最小CPU需求(1核=1000m)
          limits:
            memory: "512Mi"  # 最大内存限制
            cpu: "500m"      # 最大CPU限制
        env:                 # 环境变量
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"      # 激活prod环境配置
(2)部署Deployment
kubectl apply -f spring-boot-deployment.yaml
(3)查看部署状态
# 查看Deployment
kubectl get deployments | grep spring-boot-demo

# 查看Pod
kubectl get pods -l app=spring-boot-demo  # -l:按标签筛选
# 输出2个Pod,状态为Running表示部署成功
(4)查看Pod详情
kubectl describe pod <pod-name>  # 替换为实际Pod名称

可查看Pod的IP、启动日志、事件等,排查部署问题。

3. 创建Service暴露应用

Pod的IP是动态变化的,通过Service为应用提供固定访问地址,并实现负载均衡。

(1)Service配置(spring-boot-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-demo-service  # Service名称
  namespace: default
spec:
  selector:
    app: spring-boot-demo  # 关联标签为app=spring-boot-demo的Pod
  ports:
  - port: 80               # Service暴露的端口
    targetPort: 8080       # 映射到Pod的端口(与容器端口一致)
  type: NodePort           # 类型:NodePort(外部可访问)
  # 可选:clusterIP(仅集群内部访问)或LoadBalancer(云厂商负载均衡)
(2)创建Service
kubectl apply -f spring-boot-service.yaml
(3)查看Service
kubectl get services | grep spring-boot-demo-service
# 输出:spring-boot-demo-service   NodePort    10.96.xx.xx   <none>        80:31234/TCP   5m
# 31234是NodePort(随机分配,范围30000-32767)
(4)访问应用

通过Minikube获取访问地址:

minikube service spring-boot-demo-service --url
# 输出:http://192.168.49.2:31234 (Minikube节点IP:NodePort)

访问http://192.168.49.2:31234/hello,验证应用可访问。Service会自动将请求负载均衡到2个Pod实例。

4. 使用ConfigMap管理配置

Spring Boot应用的配置(如数据库地址、日志级别)应通过ConfigMap外部化管理,避免硬编码在镜像中。

(1)创建ConfigMap(spring-boot-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: spring-boot-config
  namespace: default
data:
  # 配置文件内容(键值对形式)
  application-prod.properties: |
    server.port=8080
    logging.level.root=INFO
    logging.level.com.example=DEBUG
    app.message=Hello from Kubernetes ConfigMap!

这里定义了application-prod.properties,与Spring Boot的prod环境配置对应。

(2)创建ConfigMap
kubectl apply -f spring-boot-configmap.yaml
(3)在Deployment中挂载ConfigMap

修改spring-boot-deployment.yaml,添加volumeMountsvolumes

spec:
  template:
    spec:
      containers:
      - name: spring-boot-demo
        # ... 其他配置省略 ...
        volumeMounts:
        - name: config-volume  # 与volumes.name对应
          mountPath: /app/config  # 容器内挂载路径
          readOnly: true       # 只读权限
      volumes:
      - name: config-volume
        configMap:
          name: spring-boot-config  # 关联的ConfigMap名称
          items:
          - key: application-prod.properties  # ConfigMap中的键
            path: application-prod.properties  # 挂载到容器内的文件名
(4)更新Deployment
kubectl apply -f spring-boot-deployment.yaml
(5)验证配置生效

进入Pod查看挂载的配置文件:

# 进入其中一个Pod
kubectl exec -it <pod-name> -- /bin/bash

# 查看配置文件
cat /app/config/application-prod.properties
# 输出ConfigMap中定义的内容

# 查看应用日志,确认日志级别生效
cat /app/logs/application.log  # 假设应用日志输出到该路径
(6)通过环境变量注入配置

除了挂载文件,也可将ConfigMap中的配置通过环境变量注入:

# 在Deployment的container中添加
env:
- name: APP_MESSAGE
  valueFrom:
    configMapKeyRef:
      name: spring-boot-config
      key: application-prod.properties  # 引用整个配置文件(不推荐)
# 或引用具体键(需ConfigMap定义单个键值)
# env:
# - name: APP_MESSAGE
#   valueFrom:
#     configMapKeyRef:
#       name: spring-boot-config
#       key: app.message

5. 使用Secret管理敏感信息

数据库密码、API密钥等敏感信息需用Secret存储,而非明文暴露在ConfigMap中。

(1)创建Secret

Secret的数据需经过Base64编码,首先编码敏感信息:

echo -n "dbpassword123" | base64  # 输出:ZGJwYXNzd29yZDEyMw==

创建spring-boot-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: spring-boot-secret
  namespace: default
type: Opaque  # 通用Secret类型
data:
  db-password: ZGJwYXNzd29yZDEyMw==  # Base64编码后的密码
  api-key: YXBpa2V5MTIzNDU2  # 示例API密钥
(2)创建Secret
kubectl apply -f spring-boot-secret.yaml
(3)在Deployment中使用Secret

有两种方式使用Secret:挂载为文件或注入环境变量。

方式一:挂载为文件
spec:
  template:
    spec:
      containers:
      - name: spring-boot-demo
        # ... 其他配置省略 ...
        volumeMounts:
        - name: secret-volume
          mountPath: /app/secrets
          readOnly: true
      volumes:
      - name: secret-volume
        secret:
          secretName: spring-boot-secret

应用可通过/app/secrets/db-password文件读取密码。

方式二:注入环境变量
spec:
  template:
    spec:
      containers:
      - name: spring-boot-demo
        # ... 其他配置省略 ...
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: spring-boot-secret
              key: db-password  # 引用Secret中的键
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: spring-boot-secret
              key: api-key

Spring Boot应用可通过@Value("${DB_PASSWORD}")获取密码。

五、进阶配置:健康检查、扩缩容与更新策略

生产环境中,需配置健康检查、资源限制、自动扩缩容和滚动更新策略,确保应用稳定运行。

1. 健康检查:Liveness和Readiness探针

K8s通过探针监测Pod健康状态,实现自愈能力:

  • Liveness探针:检测容器是否运行正常,失败则重启容器。
  • Readiness探针:检测容器是否准备好接收请求,失败则从Service中移除。
配置探针(更新Deployment):
spec:
  template:
    spec:
      containers:
      - name: spring-boot-demo
        # ... 其他配置省略 ...
        livenessProbe:
          httpGet:
            path: /health  # 健康检查接口(需Spring Boot Actuator支持)
            port: 8080
          initialDelaySeconds: 60  # 启动后延迟60秒开始检查
          periodSeconds: 10       # 检查间隔10秒
          timeoutSeconds: 5       # 超时时间5秒
          failureThreshold: 3     # 连续3次失败视为不健康
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5
集成Spring Boot Actuator:

为健康检查提供更丰富的指标,需添加Actuator依赖:

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置Actuator暴露健康端点:

# application-prod.properties
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always  # 显示详细健康信息

重建镜像并更新Deployment后,K8s将通过/health接口监控应用状态。

2. 资源配置:Requests和Limits

合理配置资源请求和限制,避免Pod之间资源竞争:

  • requests:Pod所需的最小资源,K8s scheduler根据requests调度Pod。
  • limits:Pod可使用的最大资源,超过则被限流或终止(OOM kill)。
推荐配置(根据应用实际需求调整):
resources:
  requests:
    memory: "512Mi"  # 内存请求
    cpu: "500m"      # CPU请求(500m=0.5核)
  limits:
    memory: "1Gi"    # 内存限制
    cpu: "1000m"     # CPU限制(1核)
  • 内存设置:参考应用启动后的内存占用,requests略低于正常运行内存,limits留一定冗余。
  • CPU设置:根据应用CPU使用率调整,避免设置过低导致频繁被限流。

3. 自动扩缩容:Horizontal Pod Autoscaler(HPA)

HPA根据CPU使用率、内存使用率或自定义指标自动调整Pod副本数,应对流量波动。

创建HPA配置(spring-boot-hpa.yaml):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: spring-boot-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: spring-boot-demo  # 关联的Deployment名称
  minReplicas: 2            # 最小副本数
  maxReplicas: 10           # 最大副本数
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # CPU使用率超过70%时扩容
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80  # 内存使用率超过80%时扩容
创建HPA:
kubectl apply -f spring-boot-hpa.yaml
查看HPA:
kubectl get hpa
# 输出当前副本数、目标指标、最小/最大副本数

当应用CPU使用率持续超过70%,HPA将自动增加Pod副本数;使用率下降后,会自动缩容至最小副本数。

4. 滚动更新与回滚

K8s支持滚动更新策略,确保应用更新过程中零停机;若更新失败,可快速回滚到上一版本。

配置滚动更新策略(更新Deployment):
spec:
  strategy:
    type: RollingUpdate  # 滚动更新(默认)
    rollingUpdate:
      maxSurge: 1        # 更新过程中最多可超出期望副本数的数量(或百分比,如25%)
      maxUnavailable: 0  # 更新过程中最多不可用的副本数(0表示零停机)
  • maxSurge=1:每次更新最多新增1个Pod。
  • maxUnavailable=0:更新过程中保持所有Pod可用。
执行滚动更新:
  1. 构建新版本镜像并推送:
docker build -t yourusername/spring-boot-docker-demo:v3 .
docker push yourusername/spring-boot-docker-demo:v3
  1. 更新Deployment的镜像版本:
kubectl set image deployment/spring-boot-demo spring-boot-demo=yourusername/spring-boot-docker-demo:v3
  1. 查看更新状态:
kubectl rollout status deployment/spring-boot-demo
# 输出更新进度,直到所有Pod更新完成
回滚到上一版本:

若新版本存在问题,执行回滚:

# 查看历史版本
kubectl rollout history deployment/spring-boot-demo

# 回滚到上一版本
kubectl rollout undo deployment/spring-boot-demo

# 回滚到指定版本(如版本1)
kubectl rollout undo deployment/spring-boot-demo --to-revision=1

六、实战案例:完整Spring Boot微服务部署流程

本节通过一个完整案例,演示从Spring Boot应用开发到K8s部署的全流程,包含配置文件、部署命令和验证步骤。

1. 应用准备

使用前文创建的spring-boot-docker-demo应用,包含/hello/health接口,集成Actuator健康检查。

2. 镜像构建与推送

# 多阶段构建镜像
docker build -t yourusername/spring-boot-docker-demo:v3 -f Dockerfile .

# 推送镜像
docker push yourusername/spring-boot-docker-demo:v3

3. K8s资源配置文件

创建以下配置文件,统一管理所有资源:

(1)命名空间(namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: spring-boot-demo-ns  # 专用命名空间
(2)ConfigMap(configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: spring-boot-config
  namespace: spring-boot-demo-ns
data:
  application-prod.properties: |
    server.port=8080
    management.endpoints.web.exposure.include=health,info
    management.endpoint.health.show-details=always
    app.version=v3
(3)Secret(secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: spring-boot-secret
  namespace: spring-boot-demo-ns
type: Opaque
data:
  db-password: ZGJwYXNzd29yZDEyMw==
(4)Deployment(deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-boot-demo
  namespace: spring-boot-demo-ns
  labels:
    app: spring-boot-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-boot-demo
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: spring-boot-demo
    spec:
      containers:
      - name: spring-boot-demo
        image: yourusername/spring-boot-docker-demo:v3
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: spring-boot-secret
              key: db-password
        volumeMounts:
        - name: config-volume
          mountPath: /app/config
          readOnly: true
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5
      volumes:
      - name: config-volume
        configMap:
          name: spring-boot-config
(5)Service(service.yaml
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-demo-service
  namespace: spring-boot-demo-ns
spec:
  selector:
    app: spring-boot-demo
  ports:
  - port: 80
    targetPort: 8080
  type: NodePort
(6)HPA(hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: spring-boot-hpa
  namespace: spring-boot-demo-ns
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: spring-boot-demo
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

4. 部署与验证

(1)创建命名空间
kubectl apply -f namespace.yaml
(2)部署ConfigMap和Secret
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
(3)部署Deployment和Service
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
(4)部署HPA
kubectl apply -f hpa.yaml
(5)验证部署
# 查看命名空间下的所有资源
kubectl get all -n spring-boot-demo-ns

# 查看Pod日志
kubectl logs -f <pod-name> -n spring-boot-demo-ns

# 获取Service访问地址
minikube service spring-boot-demo-service -n spring-boot-demo-ns --url
# 访问返回的URL+/hello,验证应用正常运行
(6)模拟负载测试(验证HPA)

使用hey工具模拟请求压力:

# 安装hey(macOS:brew install hey;Linux:下载二进制文件)
hey -n 10000 -c 50 http://<service-url>/hello  # 50并发,10000请求

观察HPA状态,CPU使用率上升后,Pod副本数会自动增加:

kubectl get hpa -n spring-boot-demo-ns -w  # -w:实时监控

七、最佳实践与性能优化

1. 镜像优化最佳实践

  • 使用多阶段构建:减少镜像体积,剔除构建依赖。
  • 选择合适基础镜像:优先使用Alpine或Slim版本(如openjdk:17-jre-slim)。
  • 合并RUN指令:通过&&合并多个RUN命令,减少镜像层数(每层镜像都会增加体积)。
  • 清理缓存:在构建阶段清理Maven/Gradle缓存(如RUN mvn clean package && rm -rf ~/.m2)。
  • 非root用户运行:创建专用用户,避免容器以root权限运行,降低安全风险。

2. 安全最佳实践

  • 镜像扫描:使用Trivy、Clair等工具扫描镜像漏洞,如 trivy image yourimage:tag
  • 限制Pod权限:通过SecurityContext限制容器权限,如禁止特权模式、只读文件系统。
    securityContext:
      runAsNonRoot: true
      readOnlyRootFilesystem: true
      allowPrivilegeEscalation: false
    
  • 敏感数据加密:Secret仅Base64编码,生产环境需启用K8s Secrets加密(EncryptionConfiguration)。
  • 网络策略:通过NetworkPolicy限制Pod间通信,仅允许必要的网络流量。

3. 配置管理最佳实践

  • 外部化配置:所有环境相关配置通过ConfigMap/Secret注入,镜像与配置解耦。
  • 多环境配置:通过命名空间区分dev/test/prod环境,避免配置混淆。
  • 配置热更新:Spring Boot可通过@RefreshScope实现配置热更新(需集成Spring Cloud Config或K8s ConfigMap reload)。
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
    </dependency>
    

4. 监控与日志最佳实践

  • 集成Prometheus+Grafana:监控应用 metrics(通过Spring Boot Actuator暴露)。
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
    
  • 集中式日志:使用ELK Stack(Elasticsearch+Logstash+Kibana)或EFK Stack收集容器日志。
  • 结构化日志:Spring Boot应用输出JSON格式日志,方便日志分析。
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
    </dependency>
    

八、问题排查与常见故障处理

在K8s部署Spring Boot应用时,可能遇到各种问题,以下是常见故障及排查方法。

1. Pod启动失败

症状:

kubectl get pods显示Pod状态为CrashLoopBackOffErrorPending

排查步骤:
  1. 查看Pod事件

    kubectl describe pod <pod-name> -n <namespace>
    

    关注Events部分,常见原因:

    • ImagePullBackOff:镜像拉取失败(镜像地址错误、仓库无权限)。
      • 解决:检查镜像标签是否正确,确保K8s节点能访问仓库(私有仓库需配置imagePullSecrets)。
    • Pending:调度失败(资源不足、节点亲和性问题)。
      • 解决:降低资源requests/limits,或扩容节点资源。
    • Failed:容器启动命令错误。
      • 解决:检查Dockerfile的ENTRYPOINT/CMD,确保命令正确。
  2. 查看容器日志

    kubectl logs <pod-name> -n <namespace>  # 查看当前日志
    kubectl logs <pod-name> -n <namespace> --previous  # 查看上一次启动日志
    

    常见日志错误:

    • java.lang.OutOfMemoryError:JVM内存不足,调整JVM参数或资源limits。
    • Could not find main class:JAR包损坏或路径错误,检查COPY指令和JAR包名称。

2. 应用访问不通

症状:

Service已创建,但无法通过Service IP或NodePort访问应用。

排查步骤:
  1. 检查Pod状态:确保Pod正常运行(Running状态)。

  2. 检查Pod内部端口:进入Pod验证应用是否监听正确端口:

    kubectl exec -it <pod-name> -- netstat -tuln  # 查看监听端口
    

    确认应用监听8080端口(与containerPort一致)。

  3. 检查Service配置

    kubectl describe service <service-name> -n <namespace>
    

    确保Selector标签与Pod标签一致,targetPort与应用端口一致。

  4. 检查网络策略:是否有NetworkPolicy阻止了流量,暂时删除策略测试。

3. 配置不生效

症状:

ConfigMap/Secret中的配置未被应用读取。

排查步骤:
  1. 检查挂载路径:进入Pod确认配置文件是否正确挂载:

    kubectl exec -it <pod-name> -- ls /app/config  # 查看挂载目录
    
  2. 检查配置文件内容

    kubectl exec -it <pod-name> -- cat /app/config/application-prod.properties
    

    确认内容与ConfigMap一致。

  3. 检查Spring Boot配置加载:查看应用日志,确认激活的profile和配置文件路径:

    kubectl logs <pod-name> | grep "Loaded configuration"
    

    确保SPRING_PROFILES_ACTIVE=prod,且应用加载了/app/config目录的配置。

九、总结与展望

通过Docker和Kubernetes部署Spring Boot应用,实现了环境一致性自动化运维弹性伸缩,解决了传统部署的诸多痛点。本文从基础镜像构建到生产级部署,详细讲解了Docker镜像优化、K8s资源配置、健康检查、自动扩缩容等核心内容,并通过实战案例演示了完整流程。

随着云原生技术的发展,Spring Boot与Kubernetes的集成将更加紧密:

  • Service Mesh:通过Istio等服务网格实现更细粒度的流量管理、熔断和监控。
  • Serverless:结合Knative实现Spring Boot应用的Serverless部署,进一步简化运维。
  • GitOps:通过ArgoCD等工具实现配置和部署的Git版本控制,自动化应用交付。

掌握Docker和Kubernetes部署技能,是现代Spring Boot开发者的必备能力。希望本文的实战经验能帮助读者快速上手,从“手动部署”走向“云原生自动化部署”,提升应用的可靠性和运维效率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值