Skip to content
清晨的一缕阳光
返回

Spring Boot CI/CD 流水线实战

前言

DevOps 是开发与运维的协作实践,通过自动化工具实现持续集成和持续部署。Spring Boot 项目可以通过 Jenkins、GitHub Actions、GitLab CI 等工具实现 DevOps 流程。本文将介绍 Spring Boot DevOps 的完整方案。

CI/CD 流程

1. 典型流程

┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│  Code   │────▶│  Build  │────▶│  Test   │────▶│ Package │────▶│ Deploy  │
│  Push   │     │  Maven  │     │  Unit   │     │  Docker │     │  K8s    │
└─────────┘     └─────────┘     └─────────┘     └─────────┘     └─────────┘

2. 阶段说明

阶段说明工具
Code代码提交Git
Build编译构建Maven
Test单元测试JUnit
Package打包镜像Docker
Deploy部署应用K8s/Helm

GitHub Actions

1. 基础 CI

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: maven
      
      - name: Build with Maven
        run: mvn -B clean package -DskipTests
      
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: demo-jar
          path: target/*.jar

2. 完整流水线

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]
    tags: ['v*']

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: maven
      
      - name: Run tests
        run: mvn -B test
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
  
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
  
  deploy-dev:
    needs: build
    runs-on: ubuntu-latest
    environment: development
    steps:
      - name: Deploy to K8s
        run: |
          kubectl set image deployment/demo demo=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} -n dev
  
  deploy-prod:
    needs: deploy-dev
    runs-on: ubuntu-latest
    environment: production
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - name: Deploy with Helm
        run: |
          helm upgrade demo ./helm/demo --set image.tag=${{ github.ref_name }}

3. 密钥管理

# 在 GitHub Settings → Secrets 中配置
# Secrets:
# - KUBE_CONFIG: K8s 配置文件
# - DOCKER_PASSWORD: Docker 密码
# - SONAR_TOKEN: SonarQube Token

Jenkins

1. Jenkinsfile

// Jenkinsfile
pipeline {
    agent any
    
    tools {
        maven 'Maven 3.9'
        jdk 'JDK 21'
    }
    
    environment {
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'demo'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        
        stage('Test') {
            steps {
                sh 'mvn test'
                junit 'target/surefire-reports/*.xml'
            }
        }
        
        stage('Build Docker Image') {
            steps {
                script {
                    docker.build("${REGISTRY}/${IMAGE_NAME}:${env.BUILD_ID}")
                }
            }
        }
        
        stage('Push Docker Image') {
            steps {
                script {
                    docker.withRegistry("https://${REGISTRY}", 'docker-credentials') {
                        docker.image("${REGISTRY}/${IMAGE_NAME}:${env.BUILD_ID}").push()
                    }
                }
            }
        }
        
        stage('Deploy to Dev') {
            when {
                branch 'develop'
            }
            steps {
                sh 'kubectl set image deployment/demo demo=${REGISTRY}/${IMAGE_NAME}:${BUILD_ID} -n dev'
            }
        }
        
        stage('Deploy to Prod') {
            when {
                branch 'main'
            }
            steps {
                input message: 'Deploy to production?', ok: 'Deploy'
                sh 'kubectl set image deployment/demo demo=${REGISTRY}/${IMAGE_NAME}:${BUILD_ID} -n prod'
            }
        }
    }
}

2. 多分支流水线

// Jenkinsfile
pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        
        stage('Deploy') {
            steps {
                script {
                    if (env.BRANCH_NAME == 'main') {
                        deploy('prod')
                    } else if (env.BRANCH_NAME == 'develop') {
                        deploy('dev')
                    }
                }
            }
        }
    }
}

def deploy(String env) {
    // 部署逻辑
}

GitLab CI

1. 基础配置

# .gitlab-ci.yml
stages:
  - build
  - test
  - package
  - deploy

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

cache:
  paths:
    - .m2/repository/

build:
  stage: build
  image: maven:3.9-eclipse-temurin-21
  script:
    - mvn clean package -DskipTests
  artifacts:
    paths:
      - target/*.jar

test:
  stage: test
  image: maven:3.9-eclipse-temurin-21
  script:
    - mvn test

package:
  stage: package
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main

deploy-dev:
  stage: deploy
  image: bitnami/kubectl
  script:
    - kubectl set image deployment/demo demo=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n dev
  environment:
    name: development
  only:
    - develop

deploy-prod:
  stage: deploy
  image: bitnami/kubectl
  script:
    - kubectl set image deployment/demo demo=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n prod
  environment:
    name: production
  when: manual
  only:
    - main

2. GitLab Runner

# 安装 Runner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | bash
apt-get install gitlab-runner

# 注册 Runner
gitlab-runner register \
  --url https://gitlab.com/ \
  --registration-token YOUR_TOKEN \
  --executor docker

ArgoCD GitOps

1. 安装 ArgoCD

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

2. 创建应用

# argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/demo-k8s.git
    targetRevision: HEAD
    path: k8s/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

3. GitOps 流程

Code → CI → Git Ops Repo → ArgoCD → K8s

自动化部署

1. 蓝绿部署

# k8s/blue-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-blue
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: demo
          image: demo:1.0.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-green
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: demo
          image: demo:1.1.0
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  selector:
    version: blue  # 切换到 green
  ports:
    - port: 80
      targetPort: 8080

2. 金丝雀发布

# k8s/canary.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: demo
spec:
  hosts:
    - demo
  http:
    - route:
        - destination:
            host: demo
            subset: stable
          weight: 90
        - destination:
            host: demo
            subset: canary
          weight: 10

3. 滚动更新

# k8s/rolling-update.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    spec:
      containers:
        - name: demo
          image: demo:1.1.0

监控与告警

1. Prometheus 监控

# prometheus-rule.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: demo-alerts
spec:
  groups:
    - name: demo
      rules:
        - alert: ServiceDown
          expr: up{job="demo"} == 0
          for: 1m
          annotations:
            summary: "Service is down"
        
        - alert: HighErrorRate
          expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
          for: 5m
          annotations:
            summary: "High error rate"

2. 告警通知

# alertmanager.yaml
route:
  receiver: 'slack-notifications'

receivers:
  - name: 'slack-notifications'
    slack_configs:
      - api_url: https://hooks.slack.com/services/xxx
        channel: '#alerts'

最佳实践

1. 流水线优化

# ✅ 推荐
# 并行执行
jobs:
  test:
  security-scan:
  lint:

# 缓存依赖
cache:
  paths:
    - .m2/repository/

# ❌ 不推荐
# 串行执行
# 不缓存

2. 安全配置

# ✅ 推荐
# 使用密钥管理
secrets:
  - KUBE_CONFIG
  - DOCKER_PASSWORD

# 镜像扫描
- name: Scan image
  uses: aquasecurity/trivy-action@master

# ❌ 不推荐
# 硬编码密钥

3. 版本管理

# 语义化版本
tags:
  - 'v1.0.0'
  - 'v1.0.1'
  - 'v1.1.0'

# Git 标签
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0

4. 回滚策略

# 自动回滚
spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

# 手动回滚
helm rollback demo 1
kubectl rollout undo deployment/demo

总结

DevOps 要点:

DevOps 是现代软件交付的核心实践。


分享这篇文章到:

上一篇文章
MySQL 分页优化方案
下一篇文章
Spring Boot 配置管理详解