本文目录导读:

我来为您提供一个完整的Java CI/CD实现案例,包括CI(持续集成)和CD(持续部署)的完整流程。
项目结构
java-cicd-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ └── java/
│ │ └── com/example/
│ │ └── Application.java
│ └── test/
│ └── java/
│ └── com/example/
│ └── ApplicationTest.java
├── Dockerfile
├── docker-compose.yml
├── Jenkinsfile
├── .github/
│ └── workflows/
│ └── ci-cd.yml
└── k8s/
└── deployment.yaml
示例项目代码
Maven配置文件 (pom.xml)
<?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.example</groupId>
<artifactId>java-cicd-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.0</version>
<scope>test</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.0</version>
</plugin>
<!-- JaCoCo 代码覆盖率插件 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- SpotBugs 静态代码分析 -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.7.0</version>
</plugin>
</plugins>
</build>
</project>
主应用类 (Application.java)
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/")
public String home() {
return "Hello, CI/CD Demo!";
}
@GetMapping("/health")
public Health health() {
return new Health("UP", System.currentTimeMillis());
}
static class Health {
private String status;
private long timestamp;
public Health(String status, long timestamp) {
this.status = status;
this.timestamp = timestamp;
}
public String getStatus() {
return status;
}
public long getTimestamp() {
return timestamp;
}
}
}
测试类 (ApplicationTest.java)
package com.example;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testHomeEndpoint() {
ResponseEntity<String> response = restTemplate.getForEntity("/", String.class);
assertEquals(200, response.getStatusCodeValue());
assertTrue(response.getBody().contains("CI/CD"));
}
@Test
public void testHealthEndpoint() {
ResponseEntity<Application.Health> response =
restTemplate.getForEntity("/health", Application.Health.class);
assertEquals(200, response.getStatusCodeValue());
assertEquals("UP", response.getBody().getStatus());
}
@Test
public void testAddition() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
static class Calculator {
public int add(int a, int b) {
return a + b;
}
}
}
CI/CD配置文件
Dockerfile
# 多阶段构建 # 阶段1:构建应用 FROM maven:3.8.6-openjdk-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 阶段2:生成最终镜像 FROM openjdk:11-jre-slim WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENV JAVA_OPTS="" ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Jenkinsfile (Jenkins CI/CD)
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
DOCKER_IMAGE = "${DOCKER_REGISTRY}/java-cicd-demo"
DOCKER_TAG = "${BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Package') {
steps {
sh 'mvn package -DskipTests'
}
}
stage('Static Analysis') {
steps {
sh 'mvn spotbugs:check jacoco:report'
}
}
stage('Docker Build') {
steps {
script {
docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
}
}
}
stage('Push to Registry') {
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").push()
docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").push('latest')
}
}
}
}
stage('Deploy to Staging') {
steps {
sh """
kubectl set image deployment/java-cicd-demo \
java-cicd-demo=${DOCKER_IMAGE}:${DOCKER_TAG} \
--record
"""
}
}
stage('Smoke Test') {
steps {
sh '''
curl -s http://${STAGING_URL}/health | grep -q "UP"
'''
}
}
stage('Deploy to Production') {
when {
expression {
currentBuild.result == null || currentBuild.result == 'SUCCESS'
}
}
steps {
input "Deploy to production?"
sh """
kubectl set image deployment/java-cicd-demo \
java-cicd-demo=${DOCKER_IMAGE}:${DOCKER_TAG} \
--record
"""
}
}
}
post {
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed.'
}
always {
cleanWs()
}
}
}
GitHub Actions (CI/CD配置)
name: Java CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
release:
types: [ created ]
env:
AWS_REGION: us-east-1
ECR_REPOSITORY: java-cicd-demo
KUBE_NAMESPACE: default
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn clean compile
- name: Run Tests
run: mvn test
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: |
target/surefire-reports/
target/site/jacoco/
- name: Run Static Analysis
run: mvn spotbugs:check jacoco:report
package-and-deploy:
needs: build-and-test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Package Application
run: mvn package -DskipTests
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push Docker image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Deploy to Kubernetes
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
# Update deployment image
kubectl set image deployment/java-cicd-demo \
java-cicd-demo=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
--namespace $KUBE_NAMESPACE \
--record
Docker Compose 配置
version: '3.8'
services:
app:
image: java-cicd-demo:latest
container_name: java-cicd-app
ports:
- "8080:8080"
environment:
JAVA_OPTS: "-Xmx256m"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
ports:
- "8081:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
sonarqube:
image: sonarqube:9.6.1-community
container_name: sonarqube
ports:
- "9000:9000"
environment:
- SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
volumes:
jenkins_home:
sonarqube_data:
sonarqube_extensions:
Kubernetes 部署文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-cicd-demo
namespace: default
labels:
app: java-cicd-demo
spec:
replicas: 3
selector:
matchLabels:
app: java-cicd-demo
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: java-cicd-demo
spec:
containers:
- name: java-cicd-demo
image: java-cicd-demo:latest
ports:
- containerPort: 8080
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx512m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: java-cicd-demo-service
namespace: default
spec:
selector:
app: java-cicd-demo
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: java-cicd-demo-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: java-cicd-demo
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
CI/CD流程说明
graph TD
A[源码提交] --> B{CI流程}
B --> C[代码检查]
B --> D[单元测试]
B --> E[静态分析]
C --> F[构建打包]
D --> F
E --> F
F --> G{CD流程}
G --> H[构建Docker镜像]
H --> I[推送到镜像仓库]
I --> J[部署到测试环境]
J --> K[自动化测试]
K --> L[部署到生产环境]
L --> M[监控告警]
使用说明
本地运行测试
# 构建项目 mvn clean compile # 运行测试 mvn test # 生成覆盖率报告 mvn jacoco:report # 运行静态分析 mvn spotbugs:check
使用Jenkins
- 安装Jenkins并配置必要的插件(Git、Maven、Docker)
- 创建新的Pipeline任务
- 将Jenkinsfile配置到项目中
- 配置Docker registry凭证
- 触发构建
使用GitHub Actions
- 在GitHub中添加必要的Secrets
- 配置AWS或阿里云等云服务凭证
- 推送代码到main分支触发CI/CD流程
完整流程
# 1. 构建Docker镜像 docker build -t java-cicd-demo:latest . # 2. 本地测试 docker-compose up -d # 3. 部署到Kubernetes kubectl apply -f k8s/deployment.yaml # 4. 检查部署状态 kubectl get pods kubectl get svc kubectl logs deployment/java-cicd-demo
最佳实践
- 版本控制:所有配置文件都应该纳入版本控制
- 环境隔离:开发、测试、生产环境严格隔离
- 自动化测试:确保每个阶段都有自动化测试
- 回滚策略:实施前准备回滚方案
- 监控告警:建立完善的监控和告警机制
- 安全性:密码和密钥使用安全存储
- 日志记录:记录完整的部署日志
- 性能优化:使用多阶段构建减少镜像大小
这个完整的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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播