Java 服务从虚拟机迁移到 Kubernetes(K8s)集群

文章目录

  • 前言
  • 现状
  • 具体步骤
    • [第一步:准备应用 JAR 文件](#第一步:准备应用 JAR 文件)
    • [第二步:编写 Dockerfile](#第二步:编写 Dockerfile)
    • [第三步:构建 Docker 镜像并推送到镜像仓库](#第三步:构建 Docker 镜像并推送到镜像仓库)
    • [第四步:编写 Kubernetes 部署文件](#第四步:编写 Kubernetes 部署文件)
    • [第六步:部署到 Kubernetes](#第六步:部署到 Kubernetes)
  • 优化空间

前言

本篇文章介绍一下如何将一个 Java 服务从虚拟机迁移到 Kubernetes(K8s)集群,涉及以下几个关键点:

  • 编写 Dockerfile 构建镜像
  • 构建并推送镜像到镜像仓库(如 Harbor、ACK 等)
  • 编写 KubernetesDeployment 和 Service 配置文件
  • 部署到 K8s 集群

现状

当前的Java服务有以下现状:

  1. 当前的Java服务通过mvn的命令打包成Jar包,运行在虚拟机中
  2. 当前的Jar包依赖JDK1.8运行
  3. 当前Jar包的启动命令如下:nohup java -jar -Dspring.profiles.active=test <包名> >/dev/null 2>&1 &

具体步骤

第一步:准备应用 JAR 文件

确保你本地或 CI/CD 环境中已经执行了:

bash 复制代码
mvn clean package -U -f ./pom.xml -Dmaven.test.skip=true

Jar包一般会生成在target路径:target/app.jar

注意:如果你后续使用 CI/CD 自动构建镜像,这一步会集成进流水线。这里我们先手动操作。

第二步:编写 Dockerfile

创建一个 Dockerfile,内容如下:

bash 复制代码
# 使用官方 OpenJDK 8 作为基础镜像(等价于 yum install java-1.8.0-openjdk-devel)
# FROM openjdk:8-jre-slim
FROM eclipse-temurin:8-jre-alpine

# 设置工作目录
WORKDIR /app

# 复制 JAR 文件到容器内(注意:这里先固定包名,后面通过 ENTRYPOINT 动态处理)
COPY *.jar app.jar

# 设置时区(可选,但推荐)
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 容器启动命令:允许通过环境变量或命令行参数传入 profile 和 jar 名称
# 但我们通常把 JAR 名固定为 app.jar,通过 CMD 覆盖启动参数
ENTRYPOINT ["sh", "-c", "java -jar -Dspring.profiles.active=$SPRING_PROFILES_ACTIVE /app/app.jar"]

🔍 说明:

  • 使用 openjdk:8-jre-slim:轻量、官方维护,等效于你安装的 java-1.8.0-openjdk-devel 把 JAR(openjdk:8-jre-slim不维护了,替换成eclipse-temurin:8-jre-alpine)
  • 重命名为 app.jar:简化管理。如果你确实需要动态指定 JAR 文件名(比如多个 JAR),可以进一步优化,但一般不建议。 通过环境变量
  • SPRING_PROFILES_ACTIVE 传入 profile,这是最通用且符合 K8s 最佳实践的方式。

第三步:构建 Docker 镜像并推送到镜像仓库

在项目根目录执行:

bash 复制代码
# 构建
docker build -t app:1.0 .
# 推送
docker push your-registry.com/your-namespace/app:1.0

第四步:编写 Kubernetes 部署文件

创建 deployment.yaml:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: default  # 可根据需要修改
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
        - name: app
          image: your-registry.com/your-namespace/app:1.0
          imagePullPolicy: Always  # 如果是本地测试,可改为 IfNotPresent
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: "test"   # ←←← 这里就是你原来的 -Dspring.profiles.active=test
          ports:
            - containerPort: 8084  # 根据你应用实际端口调整
          resources:
            requests:
              memory: "1Gi"
              cpu: "500m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
---
apiVersion: v1
kind: Service
metadata:
  name: app-svc
  namespace: default  # 可根据需要修改
spec:
  selector:
    app: app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8084  # 应用监听端口
  type: NodePort  # 或 LoadBalancer / NodePort,按需调整

第六步:部署到 Kubernetes

bash 复制代码
kubectl apply -f deployment.yaml

查看状态,pod是否成功启动

bash 复制代码
kubectl get pods

启动测试容器测试连通性

bash 复制代码
kubectl run -it --rm --image=your-registry.com/your-namespace/netshoot:1.0 debug --restart=Never -- bash
curl http://app-svc.default.svc.cluster.local:80
curl http://app-svc.default.svc.cluster.local:80/url

成功有业务返回

优化空间

  1. 此篇文档仅演示迁移一个微服务,如果需要迁移一个微服务集群,需要有详细的架构设计
  2. Kubernetes 中既可以运行JAR包,也可以运行WAR包。推荐运行JAR包,自包含,部署简单
  3. 如果原来的微服务架构中存在如 Spring Cloud Gateway的网关服务,建议保留原有 Gateway,作为 K8s 内部服务,因为有些功能例如认证鉴权、限流熔断、日志/监控/审计等Ingress无法实现。
    架构:外部流量 → Ingress → Gateway Pod → 微服务
  4. 将原有 Gateway 容器化并部署到 K8s
  5. 使用 Ingress 仅暴露 Gateway 服务(不暴露其他微服务)
  6. 内部服务间通信走 Service DNS,不经过 Gateway
  7. 逐步评估是否替换为云原生网关(如 APISIX、Kong)
  8. 云原生网关(如 APISIX、Kong)架构:Client → [APISIX Ingress Controller] → [Microservice A/B/C]
相关推荐
独自破碎E2 小时前
如何用最短替换让字符串变平衡?
java·开发语言·算法·leetcode
老华带你飞2 小时前
宠物商城销售|基于Java+ vue宠物商城销售管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·宠物
武子康2 小时前
Java-190 EVCache入门:Netflix 级分布式缓存架构、性能指标与多区域部署全解析
java·redis·分布式·缓存·架构·guava·guava cache
曾几何时`2 小时前
字符串(七)409. 构造出来的最长回文串
java·前端·javascript
SadSunset2 小时前
(9)基于xml的自动装配
java·笔记·spring
董世昌413 小时前
JavaScript 变量声明终极指南:var/let/const 深度解析(2025 版)
java·服务器·前端
sheji34163 小时前
【开题答辩全过程】以 个人网站的设计与实现为例,包含答辩的问题和答案
java·eclipse
LFly_ice3 小时前
Nest-管道
android·java·数据库
剽悍一小兔3 小时前
为什么使用postman发送请求时不会有跨域问题?
java