本文目录导读:

下面是一个完整的Java项目CI/CD案例,涵盖环境搭建、Pipeline编写、自动化测试和部署。
项目架构概览
📦demo-java-ci-cd
┣ 📂src
┃ ┣ 📂main/java/com/example
┃ ┣ 📂test/java/com/example
┣ 📂scripts
┃ ┣ 📜deploy.sh
┣ 📜pom.xml
┣ 📜Dockerfile
┗ 📜.gitlab-ci.yml (或 Jenkinsfile / .github/workflows)
技术栈
| 工具 | 说明 |
|---|---|
| JDK 11/17 | 编译运行环境 |
| Maven | 依赖管理和构建 |
| JUnit 5 + Mockito | 单元测试 |
| SonarQube | 代码质量扫描 |
| Docker + Docker Compose | 容器化 |
| GitLab CI / Jenkins / GitHub Actions | CI/CD 编排 |
| Nginx / Kubernetes | 部署 |
CI/CD 流水线阶段设计
[Java代码提交] → [代码静态分析] → [编译打包] → [单元测试] → [生成Docker镜像] → [推送到仓库] → [部署到测试/生产环境]
完整配置示例 (以GitHub Actions为例)
.github/workflows/java-ci-cd.yml
name: Java CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test_db
ports:
- 3306:3306
steps:
- name: Checkout代码
uses: actions/checkout@v3
# 1️⃣ 设置JDK
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: maven
# 2️⃣ Maven构建 + 单元测试
- name: Build和测试
run: |
mvn clean compile
mvn test
# 3️⃣ 代码质量扫描
- name: SonarQube静态扫描
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
run: mvn sonar:sonar -Dsonar.token=$SONAR_TOKEN \
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.projectKey=demo-java
# 4️⃣ 打包成Jar包
- name: Package
run: mvn package -DskipTests
# 5️⃣ 登录Docker Hub
- name: Login Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# 6️⃣ 构建和推送Docker镜像
- name: Build and Push Docker Image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/demo-java:latest
${{ secrets.DOCKER_USERNAME }}/demo-java:${{ github.sha }}
# 7️⃣ 部署到测试环境
deploy-test:
needs: build
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- name: Deploy到测试环境
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.TEST_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/demo-java:latest
cd /opt/demo
docker-compose up -d
# 8️⃣ 生产环境部署(需手动审批)
deploy-prod:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy到生产环境
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/demo-java:latest
cd /opt/demo-prod
docker-compose up -d
Dockerfile
# 多阶段构建 FROM maven:3.8.4-openjdk-17-slim AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 运行阶段 FROM openjdk:17-jdk-slim WORKDIR /app COPY --from=build /app/target/*.jar app.jar # 设置时区 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
docker-compose.yml
version: '3.8'
services:
app:
image: your_username/demo-java:latest
container_name: demo-java-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
- DB_HOST=mysql
- DB_PORT=3306
depends_on:
- mysql
restart: always
mysql:
image: mysql:8.0
container_name: demo-mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: demo_db
MYSQL_USER: app_user
MYSQL_PASSWORD: app_password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
pom.xml 配置(示例)
<properties>
<java.version>17</java.version>
<sonar.organization>your-org</sonar.organization>
<jacoco.version>0.8.8</jacoco.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-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- JaCoCo 覆盖率 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
测试类示例
@SpringBootTest
@ActiveProfiles("test")
class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserRepository userRepository;
@Test
void testCreateUser() {
// 准备数据
User user = new User();
user.setName("张三");
user.setEmail("zhangsan@test.com");
// Mock行为
when(userRepository.save(any(User.class))).thenReturn(user);
// 调用测试
User savedUser = userService.createUser(user);
// 断言
assertNotNull(savedUser);
assertEquals("张三", savedUser.getName());
// 验证交互
verify(userRepository, times(1)).save(any(User.class));
}
}
部署脚本 deploy.sh
#!/bin/bash
set -e
echo "🚀 ========== 开始部署 =========="
# 获取参数
ENV_NAME=$1
IMAGE_TAG=$2
# 拉取最新镜像
echo "📦 拉取镜像: ${IMAGE_TAG}"
docker pull your_username/demo-java:${IMAGE_TAG}
# 停止并删除旧容器
echo "🛑 停止旧容器..."
docker-compose -f docker-compose.${ENV_NAME}.yml down || true
# 启动新容器
echo "▶️ 启动新容器..."
docker-compose -f docker-compose.${ENV_NAME}.yml up -d
# 检查健康状态
echo "🏥 检查健康状态..."
sleep 30
curl -f http://localhost:8080/api/health || exit 1
echo "✅ ========== 部署成功 =========="
Jenkins Pipeline 版本(可选)
pipeline {
agent any
tools {
jdk 'JDK17'
maven 'MAVEN3'
}
environment {
DOCKER_REGISTRY = 'registry.example.com'
APP_NAME = 'demo-java'
IMAGE_TAG = "${BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('静态代码分析') {
steps {
withSonarQubeEnv('sonarqube') {
sh 'mvn sonar:sonar -Dsonar.projectKey=demo-java'
}
}
}
stage('编译打包') {
steps {
sh 'mvn clean package'
}
}
stage('构建Docker镜像') {
steps {
script {
sh """
docker build -t ${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG} .
docker tag ${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG} ${DOCKER_REGISTRY}/${APP_NAME}:latest
"""
}
}
}
stage('推送到仓库') {
steps {
script {
withCredentials([usernamePassword(
credentialsId: 'dockerhub',
usernameVariable: 'USERNAME',
passwordVariable: 'PASSWORD'
)]) {
sh """
echo "${PASSWORD}" | docker login -u "${USERNAME}" --password-stdin
docker push ${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG}
docker push ${DOCKER_REGISTRY}/${APP_NAME}:latest
"""
}
}
}
}
stage('部署') {
input {
message "确认部署到生产环境?"
ok "部署"
}
steps {
sh """
ssh deploy-server "./deploy.sh prod ${IMAGE_TAG}"
"""
}
}
}
post {
success {
echo '构建成功!'
// 发送通知
slackSend channel: '#builds', message: "构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
failure {
echo '构建失败!'
slackSend channel: '#builds', message: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
}
十一、实际执行效果流程
[开发环境]
Git push → 编译 → 单元测试 → 代码扫描 → 生成镜像(dev tag) → 部署到dev环境
↓
[测试环境] 合并到develop分支
Git push → 编译 → 集成测试 → 代码扫描 → 生成镜像(test tag) → 部署到test环境
↓
[生产环境] 合并到main分支 + 审批
Git push → 编译 → 回归测试 → 安全扫描 → 生成镜像(prod tag) → 部署到生产
十二、最佳实践总结
-
分支策略
main→ 生产develop→ 测试feature/*→ 功能开发
-
安全控制
- 生产部署需要人工审批
- 密钥使用Secret管理,不写入代码
- 镜像签名验证
-
质量门槛
- 代码覆盖率 ≥ 80%
- SonarQube无Critical级别问题
- 所有单元测试通过
-
部署策略
- 蓝绿部署 / 滚动更新
- 健康检查自动回滚
- 日志集中收集
-
监控告警
- Prometheus + Grafana监控
- 告警通知接入钉钉/Slack
- 分布式链路追踪 (Jaeger)
这个案例涵盖了完整的Java CI/CD流程,可根据实际项目需求灵活调整,遇到具体问题时,欢迎继续交流!
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播