文章目录
- 前言
- 现状
- 具体步骤
-
- [第一步:准备应用 JAR 文件](#第一步:准备应用 JAR 文件)
- [第二步:编写 Dockerfile](#第二步:编写 Dockerfile)
- [第三步:构建 Docker 镜像并推送到镜像仓库](#第三步:构建 Docker 镜像并推送到镜像仓库)
- [第四步:编写 Kubernetes 部署文件](#第四步:编写 Kubernetes 部署文件)
- [第六步:部署到 Kubernetes](#第六步:部署到 Kubernetes)
- 优化空间
前言
本篇文章介绍一下如何将一个 Java 服务从虚拟机迁移到 Kubernetes(K8s)集群,涉及以下几个关键点:
- 编写 Dockerfile 构建镜像
- 构建并推送镜像到镜像仓库(如 Harbor、ACK 等)
- 编写 KubernetesDeployment 和 Service 配置文件
- 部署到 K8s 集群
现状
当前的Java服务有以下现状:
- 当前的Java服务通过mvn的命令打包成Jar包,运行在虚拟机中
- 当前的Jar包依赖JDK1.8运行
- 当前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
成功有业务返回
优化空间
- 此篇文档仅演示迁移一个微服务,如果需要迁移一个微服务集群,需要有详细的架构设计
- Kubernetes 中既可以运行JAR包,也可以运行WAR包。推荐运行JAR包,自包含,部署简单
- 如果原来的微服务架构中存在如 Spring Cloud Gateway的网关服务,建议保留原有 Gateway,作为 K8s 内部服务,因为有些功能例如认证鉴权、限流熔断、日志/监控/审计等Ingress无法实现。
架构:外部流量 → Ingress → Gateway Pod → 微服务 - 将原有 Gateway 容器化并部署到 K8s
- 使用 Ingress 仅暴露 Gateway 服务(不暴露其他微服务)
- 内部服务间通信走 Service DNS,不经过 Gateway
- 逐步评估是否替换为云原生网关(如 APISIX、Kong)
- 云原生网关(如 APISIX、Kong)架构:Client → [APISIX Ingress Controller] → [Microservice A/B/C]