基于CI/CD+K8s部署Java项目(复用现有环境,零额外搭建)
前言:前面我们已经搭建好了 Harbor 私有镜像仓库、 K8s 1.35.3 集群、 GitLab 代码仓库、 GitLab Runner 全套基础环境,今天就复用这套环境,手把手教大家部署Java项目(SpringBoot),实现「代码提交→自动编译→镜像构建→推送Harbor→部署K8s」全流程自动化,和前端项目部署逻辑对齐,新手也能一步到位,完全还原企业Java项目容器化发布场景!
**一、科普:**Java 项目 CI/CD 和前端有啥区别?
很多新手会疑惑,同样是部署到K8s,Java项目和前端项目的CI/CD流程为啥不一样?核心差异就在于「编译打包」环节------
- 前端项目:纯静态文件(HTML/CSS/JS),无需编译,直接打包成Nginx镜像即可;
- Java项目(SpringBoot):需要先通过 Maven/Gradle 编译源代码、打包成WAR/JAR包,再封装到Docker镜像(需依赖JDK运行环境),最后部署到K8s。
本次教程的核心亮点:全程镜像化 + 复用现有环境,无需在Runner节点额外部署复杂依赖,通过Docker封装Java运行环境,避免版本冲突,同时沿用之前的GitLab CI/CD流水线逻辑,降低学习成本。
二、前置环境说明
在开始前,请确认以下环境已正常运行(前面教程已部署完成),无需额外操作:
- Harbor私有仓库:地址 192.168.223.200,项目 k8s-project,账号 admin/Harbor12345;
- K8s集群:1主2从,所有节点处于Ready状态,Flannel网络插件正常运行;
- GitLab:地址 192.168.223.23,已创建root管理员账号,可正常创建项目;
- GitLab Runner:已注册并绑定 shared 标签,以root权限运行,可正常访问K8s集群。
|------------------------------------------------------------|
| 提示:若之前环境未部署完成,可先回顾前面的Harbor、K8s、GitLab部署教程,确保所有组件正常运行后再继续。 |
**三、核心准备:**Maven+JDK 环境配置( Master+Runner 节点)
Java项目编译需要JDK和Maven,我们在Master节点(本地测试)和GitLab Runner节点(流水线执行)都安装,避免编译报错,推荐版本:JDK 11 + Maven 3.9.6(版本兼容,稳定无坑)。
3.1****安装 JDK 11 (全局生效)
-
上传 jdk-11_linux-x64_bin.tar.gz 到节点的 /root 目录,执行解压并重命名:
cd /root
tar -zxf jdk-11_linux-x64_bin.tar.gz
mv jdk-11 jdk11 -
配置全局环境变量(永久生效):
cat > /etc/profile.d/jdk.sh << 'EOF'
export JAVA_HOME=/root/jdk11
export PATH=JAVA_HOME/bin:PATH
export CLASSPATH=.:$JAVA_HOME/lib
EOF -
生效并验证安装:
source /etc/profile.d/jdk.sh
java -version # 输出JDK 11版本即成功
3.2****安装 Maven 3.9.6 (适配 JDK 11 )
-
下载Maven安装包(官方源,稳定可用):
cd /root
wget https://archive.apache.org/dist/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz -
解压并重命名,方便后续配置:
tar -zxf apache-maven-3.9.6-bin.tar.gz
mv apache-maven-3.9.6 maven -
配置全局环境变量:
cat > /etc/profile.d/maven.sh << 'EOF'
export MAVEN_HOME=/root/maven
export PATH=MAVEN_HOME/bin:PATH
EOF
source /etc/profile.d/maven.sh -
验证安装,并配置Maven阿里云镜像加速(解决编译慢、依赖拉取超时问题):
mvn -version # 输出Maven 3.9.6版本即成功
配置阿里云镜像
vim /root/maven/conf/settings.xml
在 <mirrors> 标签内添加如下镜像(替换默认中央仓库):
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<url>https://maven.aliyun.com/repository/public/</url>
</mirror>
四、实操步骤:部署****Java 项目
本次我们部署一个最简SpringBoot Web项目,全程分为「创建项目目录→编写核心文件→配置CI/CD→推送触发部署→验证结果」5步,所有命令均经过实测,可直接复制执行。
4.1****第一步:创建 Java 项目目录( Master 节点)
回到部署根目录,创建Java项目专属目录,和前端项目同级,方便管理:
cd /root/k8s/deploy
mkdir java-demo
cd java-demo
4.2****第二步:编写 4 个核心文件(直接复制,无需修改)
这4个文件是Java项目部署的核心,分别对应「源代码、Maven配置、Docker镜像、K8s部署」,全程复制即可,无需手动编写。
1. Java****源代码( SpringBoot 最简 Web 项目)
创建SpringBoot的Controller和启动类,实现一个简单的接口,用于后续验证部署结果:
# 创建源代码目录结构
mkdir -p src/main/java/com/demo
# 编写HelloController(接口层)
cat > src/main/java/com/demo/HelloController.java <<EOF
package com.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "Java-Demo 部署成功!K8s + GitLab CI/CD 正常运行!";
}
}
EOF
# 编写DemoApplication(启动类,适配WAR包)
cat > src/main/java/com/demo/DemoApplication.java <<EOF
package com.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(DemoApplication.class);
}
}
EOF
2. Maven****配置文件( pom.xml )
固定JDK版本、SpringBoot依赖、打包方式(WAR包),适配Tomcat运行环境,避免版本兼容问题:
cat > pom.xml <<EOF
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>java-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 固定为war包,适配Tomcat -->
<packaging>war</packaging>
<!-- 统一JDK 11编译版本,避免版本不兼容 -->
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- SpringBoot父依赖(2.7.15适配JDK 11 + Tomcat 9) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/>
</parent>
<dependencies>
<!-- Web核心依赖,排除内置Tomcat(用外部Tomcat) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Tomcat 9适配的Servlet API(固定版本) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<!-- 固定WAR包名称为ROOT,避免访问路径加项目名 -->
<finalName>ROOT</finalName><plugins>
<!-- SpringBoot打包插件(固定) -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.15</version>
</plugin>
</plugins>
</build>
</project>
EOF
3. Dockerfile**(镜像化编译** + 运行)
基于Tomcat+JDK11镜像,封装Java项目,先加载本地镜像并推送至Harbor,避免Runner拉取镜像超时:
# 加载本地Tomcat+JDK11镜像(提前准备好离线包)
docker load -i tomcat-9.0-jdk11.tar
# 给镜像打标签,适配Harbor仓库地址
docker tag tomcat:9.0-jdk11 192.168.223.200/k8s-project/tomcat:9.0-jdk11
# 推送镜像到Harbor私有仓库
docker push 192.168.223.200/k8s-project/tomcat:9.0-jdk11
# 编写Dockerfile
cat > Dockerfile <<EOF
FROM 192.168.223.200/k8s-project/tomcat:9.0-jdk11
WORKDIR /usr/local/tomcat
RUN rm -rf webapps/*
COPY target/ROOT.war webapps/ROOT.war
EXPOSE 8080
CMD ["catalina.sh", "run"]
EOF
4. K8s****部署文件( java.yaml )
定义Deployment(部署)和Service(暴露端口),采用NodePort类型,方便外部访问,端口使用30084(避免和前端30083冲突):
cat > java.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-demo
spec:
replicas: 1
selector:
matchLabels:
app: java-demo
template:
metadata:
labels:
app: java-demo
spec:
containers:
- name: java-demo
image: $IMAGE
imagePullPolicy: Always
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: java-demo-svc
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 30084
selector:
app: java-demo
EOF
4.3第三步:本地测试编译(可选,验证环境)
在Master节点执行编译命令,验证Maven+JDK环境是否正常,同时生成WAR包,避免后续流水线编译报错:
cd /root/k8s/deploy/java-demo
mvn clean package -DskipTests # 跳过测试,快速编译
# 编译成功后,会在target目录生成ROOT.war文件
ls target/ROOT.war # 能看到文件即成功
4.4****第四步:编写 GitLab CI/CD 配置( .gitlab-ci.yml )
沿用前端项目的流水线逻辑,定义「build(编译+构建镜像)、push(推送镜像)、deploy(部署K8s)」三个阶段,直接复制即可:
vim .gitlab-ci.yml
cat > .gitlab-ci.yml <<EOF
stages:
- build
- push
- deploy
variables:
HARBOR_REGISTRY: "192.168.223.200"
HARBOR_PROJECT: "k8s-project"
IMAGE_NAME: "java-demo"
TAG: "$CI_COMMIT_SHORT_SHA"
NAMESPACE: "default"
build:
stage: build
tags:
- shared
script:
- echo "=== 开始编译打包Java项目 ==="
- mvn clean package -DskipTests
- echo "=== 编译完成,开始构建Docker镜像 ==="
- docker build -t ${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG} .
- docker tag ${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG} ${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}:latest
push:
stage: push
tags:
- shared
script:
- echo "=== 登录Harbor,推送镜像 ==="
- docker login ${HARBOR_REGISTRY} -u admin -p Harbor12345
- docker push ${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG}
- docker push ${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}:latest
deploy:
stage: deploy
tags:
- shared
script:
- echo "=== 开始部署Java项目到K8s集群 ==="
- export IMAGE=${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG}
- envsubst '$IMAGE' < java.yaml > java-deploy.yaml
- kubectl apply -f java-deploy.yaml
- kubectl rollout status deployment java-demo -n ${NAMESPACE} --timeout=120s
- rm -f java-deploy.yaml
- echo "=== 部署成功,查看Pod状态 ==="
- kubectl get pods -n ${NAMESPACE}
EOF
4.5****第五步:推送到 GitLab 仓库,触发自动部署
初始化Git仓库,将所有文件推送到GitLab,自动触发CI/CD流水线,全程无需手动干预:
# 配置Git全局信息(首次执行)
git config --global user.name "Administrator"
git config --global user.email "gitlab_admin_f440b6@example.com"
# 初始化Git仓库
git init --initial-branch=main
# 添加所有文件
git add .
# 提交代码
git commit -m "Initial commit: Java项目初始化,适配CI/CD+K8s部署"
# 关联GitLab远程仓库(替换为你的GitLab项目地址)
git remote add origin http://192.168.223.23/root/java-demo.git
# 推送代码,触发自动部署
git push --set-upstream origin main
五、验证部署结果
5.1****查看 GitLab CI/CD 流水线
打开GitLab → 进入java-demo项目 → 左侧菜单「CI/CD → Pipelines」,查看 build、push、deploy 三个阶段,全部显示绿色对勾 ✅,说明流水线执行成功。
5.2查看 K8s Pod 和 Service 状态
在K8s Master节点执行命令,查看Pod是否正常运行、Service是否成功暴露端口:
# 查看Java项目Pod状态(Running即正常)
kubectl get pods -n default | grep java-demo
# 正常输出示例:java-demo-7f98d7c6b4-2xqzk 1/1 Running 0 30s
# 查看Service状态(NodePort为30084即正常)
kubectl get svc -n default | grep java-demo
# 正常输出示例:java-demo-svc NodePort 10.102.158.78 <none> 8080:30084/TCP 1min
5.3****浏览器访问 Java 项目
打开浏览器,输入地址:http://任意K8s节点IP:30084,看到如下内容,说明部署成功:
Java-Demo 部署成功! K8s + GitLab CI/CD 正常运行!
六、常见问题排查(新手必看)
- 问题 1 : Maven 编译报错 " 依赖拉取失败 "
- 原因:Maven镜像未配置或配置错误,无法拉取SpringBoot依赖;
- 解决:重新配置settings.xml中的阿里云镜像,确保标签格式正确,重启Maven(source /etc/profile.d/maven.sh)。
- 问题 2 : K8s Pod 报 "ImagePullBackOff"
- 原因:K8s节点未配置Harbor为可信仓库,无法拉取镜像;
- 解决:在所有K8s节点执行以下命令,重启Docker后重新登录Harbor:
echo '{ "insecure-registries": ["192.168.223.200"] }' > /etc/docker/daemon.json
systemctl restart docker
docker login 192.168.223.200 -u admin -p Harbor12345
- 问题 3 :流水线 deploy 阶段报错 "kubectl: command not found"
- 原因:GitLab Runner节点未安装kubectl,无法操作K8s集群;
- 解决:在Runner节点配置K8s YUM源,安装kubectl,同时拷贝Master节点的~/.kube/config文件,确保权限正常。
- 问题 4 :端口冲突
- 原因:30084端口已被其他服务占用;
- 解决:修改java.yaml中的nodePort,替换为未被占用的端口(范围30000-32767),重新推送代码触发部署。
七、总结
本次教程复用了已有的Harbor、K8s、GitLab、GitLab Runner环境,实现了Java项目的全流程自动化部署,核心亮点的是「镜像化编译+复用流水线逻辑」,避免了重复搭建环境的麻烦。
整个流程的核心逻辑:代码提交 →Maven 编译打包 →Docker 构建镜像 → 推送 Harbor→K8s 部署,和前端项目部署流程高度对齐,掌握后可无缝迁移到真实企业Java项目(微服务、单体项目均可)。
通过这套架构,我们真正实现了"一次搭建,多项目复用",开发者只需专注于代码开发,提交代码后即可自动完成部署,极大提升了开发效率,也贴合企业容器化、自动化的生产需求。
后续可在此基础上扩展:添加测试阶段、实现蓝绿部署、集成Helm管理K8s资源,让整个CI/CD流程更贴合企业生产场景!