欢迎光临
我们一直在努力

【架构实战】Jenkins+GitLab CI/CD:持续集成与持续部署实践

【架构实战】Jenkins+GitLab CI/CD:持续集成与持续部署实践

字数统计:约3800字

一、真实故事引入:从手动部署的噩梦到自动化救赎

2023年冬天,我刚入职一家生鲜电商公司,第一次参与大促前的上线。当时我们的部署流程还是纯手动:开发人员提交代码到GitLab,然后运维人员登录跳板机,拉取代码、手动执行mvn package打包、上传jar包到服务器、重启服务。那次上线前,我负责的一个优惠券服务因为打包时漏了一个application-prod.yml配置文件,导致线上服务启动失败,整个优惠券系统宕机了15分钟,影响了大促的预热活动,直接导致公司损失了近20万的订单额。

事故复盘会上,技术总监拍着桌子说:“再也不想看到手动部署了!” 于是我们痛定思痛,决定引入Jenkins+GitLab CI/CD来实现端到端的自动化部署,从此告别了手动部署的噩梦。这就是我和CI/CD的第一次亲密接触,也让我深刻理解了自动化部署对团队效率的保障作用。

二、概念原理:CI/CD到底是什么?

2.1 核心概念解析

  • CI(持续集成):开发人员频繁地将代码合并到主干分支(通常每天多次),每次合并都触发自动构建和测试,尽早发现集成错误。核心目标是"早发现,早解决",避免集成地狱。
  • CD(持续交付/持续部署):
    • 持续交付:自动将通过所有测试的代码构建成可部署的制品,随时可以手动部署到生产环境
    • 持续部署:自动将通过所有测试的代码直接部署到生产环境,不需要人工干预
  • Jenkins:开源的自动化服务器,拥有超过1800个插件,支持构建、测试、部署全流程的自动化,适合复杂场景的定制化需求
  • GitLab CI/CD:GitLab内置的CI/CD能力,通过项目根目录的.gitlab-ci.yml文件定义流水线,轻量且和代码仓库深度集成

2.2 两者集成的核心逻辑

我们的集成方案采用"GitLab触发Jenkins"的模式:

  • 开发人员提交代码到GitLab的指定分支(如main)
  • GitLab通过Webhook通知Jenkins有新的代码变更
  • Jenkins拉取代码,执行预定义的流水线(构建、测试、打包、部署)
  • 流水线执行结果反馈到GitLab的Pipeline页面
  • 这种组合既利用了GitLab的代码管理能力,又发挥了Jenkins插件生态丰富的优势。

    三、配置代码:从零搭建集成环境

    3.1 基础环境准备

    # 1. 用Docker部署Jenkins(LTS版本)
    docker run -d –name jenkins \\
    -p 8080:8080 -p 50000:50000 \\
    -v jenkins_home:/var/jenkins_home \\
    -v /var/run/docker.sock:/var/run/docker.sock \\
    jenkins/jenkins:lts

    # 2. 部署GitLab(如果还没部署的话)
    docker run -d –name gitlab \\
    -p 443:443 -p 80:80 -p 22:22 \\
    -v gitlab_config:/etc/gitlab \\
    -v gitlab_logs:/var/log/gitlab \\
    -v gitlab_data:/var/opt/gitlab \\
    gitlab/gitlab-ce:latest

    3.2 Jenkins配置GitLab集成

  • 在GitLab生成Access Token:进入User Settings → Access Tokens,勾选api权限,生成token后保存
  • 在Jenkins安装GitLab Plugin,进入Manage Jenkins → Configure System → GitLab
    • 填写GitLab host URL:http://your-gitlab-url
    • Credentials选择GitLab API Token,填入上一步生成的token
    • 点击Test Connection验证连通性
  • 配置Webhook:在GitLab项目的Settings → Webhooks中
    • URL填写:http://your-jenkins-url/gitlab-webhook/
    • Secret Token:在Jenkins的GitLab配置中生成的Webhook Secret
    • 勾选Push events,分支过滤填写main
  • 3.3 核心流水线配置(Jenkinsfile)

    我们在项目根目录创建Jenkinsfile(声明式流水线):

    pipeline {
    agent any
    // 环境变量配置
    environment {
    DOCKER_REGISTRY = "harbor.example.com"
    PROJECT_NAME = "coupon-service"
    KUBECONFIG = credentials('k8s-kubeconfig')
    }
    stages {
    // 阶段1:拉取代码
    stage('拉取代码') {
    steps {
    git(
    url: 'http://gitlab.example.com/ecommerce/coupon-service.git',
    branch: 'main',
    credentialsId: 'gitlab-ssh-credentials'
    )
    }
    }
    // 阶段2:编译构建
    stage('编译构建') {
    steps {
    sh 'mvn clean package -DskipTests'
    }
    post {
    success {
    archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
    }
    }
    }
    // 阶段3:单元测试
    stage('单元测试') {
    steps {
    sh 'mvn test'
    }
    post {
    always {
    junit 'target/surefire-reports/*.xml'
    publishCoverage adapters: [jacocoAdapter('target/site/jacoco/jacoco.xml')], sourceFileResolver: sourceFiles('STORE_LAST_BUILD')
    }
    }
    }
    // 阶段4:代码质量检查(集成SonarQube)
    stage('代码质量检查') {
    steps {
    sh 'mvn sonar:sonar -Dsonar.projectKey=coupon-service -Dsonar.host.url=http://sonarqube.example.com -Dsonar.login=${SONAR_TOKEN}'
    }
    }
    // 阶段5:构建Docker镜像
    stage('构建镜像') {
    steps {
    script {
    def imageName = "${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER}"
    sh "docker build -t ${imageName} ."
    sh "docker push ${imageName}"
    }
    }
    }
    // 阶段6:部署到测试环境
    stage('部署测试环境') {
    steps {
    sh "kubectl set image deployment/${PROJECT_NAME} ${PROJECT_NAME}=${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER} -n test"
    sh "kubectl rollout status deployment/${PROJECT_NAME} -n test"
    }
    }
    // 阶段7:自动化测试(可选)
    stage('自动化测试') {
    steps {
    sh 'curl -X POST http://test.example.com/api/health-check'
    }
    }
    }
    // 流水线后置处理
    post {
    success {
    echo '流水线执行成功!'
    // 发送成功通知到企业微信
    sh 'curl -X POST https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx -d "{\\"msgtype\\":\\"text\\",\\"text\\":{\\"content\\":\\"优惠券服务部署成功,版本:${BUILD_NUMBER}\\"}}"'
    }
    failure {
    echo '流水线执行失败!'
    // 发送失败通知
    sh 'curl -X POST https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx -d "{\\"msgtype\\":\\"text\\",\\"text\\":{\\"content\\":\\"优惠券服务部署失败,请查看控制台日志\\"}}"'
    }
    }
    }

    3.4 GitLab CI补充配置(可选)

    如果不需要Jenkins的复杂能力,也可以直接用GitLab CI,在项目根目录创建.gitlab-ci.yml:

    stages:
    build
    test
    deploy

    build_job:
    stage: build
    image: maven:3.8openjdk17
    script:
    mvn clean package DskipTests
    artifacts:
    paths:
    target/*.jar

    test_job:
    stage: test
    image: maven:3.8openjdk17
    script:
    mvn test
    artifacts:
    reports:
    junit: target/surefirereports/*.xml

    deploy_job:
    stage: deploy
    image: bitnami/kubectl:latest
    script:
    kubectl set image deployment/couponservice couponservice=harbor.example.com/couponservice:${CI_PIPELINE_ID} n prod
    only:
    main

    四、实战案例:优惠券服务的CI/CD全流程

    我们的优惠券服务是一个Spring Boot项目,部署在K8s集群中,完整流程如下:

  • 开发人员完成需求开发,提交代码到main分支,触发GitLab Webhook
  • Jenkins自动拉取代码,执行流水线:
    • 编译打包:耗时2分钟,生成coupon-service-1.0.0.jar
    • 单元测试:耗时1分钟,覆盖率85%,无失败用例
    • 代码质量检查:SonarQube评分A,无阻断性问题
    • 构建Docker镜像:基于openjdk:17-alpine,镜像大小280MB,推送到Harbor仓库
    • 部署到测试环境:K8s滚动更新,耗时30秒,无downtime
  • 测试人员验证测试环境功能,确认通过后,手动点击Jenkins的"部署生产"按钮
  • 生产环境滚动更新,健康检查通过后,流水线结束
  • 总耗时:从代码提交到生产部署完成,约8分钟(纯自动化部分5分钟)
  • 对比原来的手动部署流程(30分钟+容易出错),效率提升了6倍,且零人为错误。

    五、踩坑实录:那些年我们踩过的坑

    5.1 Jenkins插件版本不兼容

    • 现象:安装最新的GitLab Plugin 1.5.0后,Webhook触发时报403 Forbidden
    • 原因:Jenkins版本是2.346,和插件1.5.0不兼容
    • 解决:降级GitLab Plugin到2.36.0版本,重启Jenkins后解决

    5.2 Docker镜像推送失败

    • 现象:执行docker push时报x509: certificate signed by unknown authority
    • 原因:Harbor仓库用的是自签名证书,Docker默认不信任
    • 解决:在Jenkins节点上配置Docker的insecure-registries:// /etc/docker/daemon.json
      {
      "insecure-registries": ["harbor.example.com"]
      }

      然后重启Docker服务

    5.3 K8s部署权限问题

    • 现象:执行kubectl set image时报Error from server (Forbidden): deployments.apps is forbidden
    • 原因:Jenkins使用的kubeconfig文件权限不足,没有test和prod命名空间的部署权限
    • 解决:在K8s中创建ServiceAccount,绑定edit角色:apiVersion: v1
      kind: ServiceAccount
      metadata:
      name: jenkinssa
      namespace: test

      apiVersion: rbac.authorization.k8s.io/v1
      kind: RoleBinding
      metadata:
      name: jenkinsrolebinding
      namespace: test
      subjects:
      kind: ServiceAccount
      name: jenkinssa
      namespace: test
      roleRef:
      kind: Role
      name: edit
      apiGroup: rbac.authorization.k8s.io

    5.4 流水线执行到一半卡住

    • 现象:单元测试阶段执行了10分钟还没结束
    • 原因:单元测试中用了@SpringBootTest,加载了完整的Spring上下文,且有一个测试用例死锁
    • 解决:优化单元测试,减少@SpringBootTest的使用,用@MockBean模拟依赖,执行时间从10分钟降到1分钟

    5.5 Webhook触发重复执行

    • 现象:一次代码提交触发了3次流水线执行
    • 原因:GitLab Webhook配置了Push events和Merge Request events,且合并请求时也会触发push事件
    • 解决:在Jenkins的流水线中增加分支过滤,只处理main分支的push事件

    六、总结与思考

    6.1 核心总结

    Jenkins+GitLab CI/CD的组合完美覆盖了从代码提交到生产部署的全流程,核心价值在于:

    • 效率提升:原来30分钟的部署流程缩短到8分钟
    • 质量保障:自动化测试+代码质量检查,提前拦截低质量代码
    • 可追溯性:所有部署记录都在Jenkins和GitLab中留存,出现问题可快速回滚
    • 减少人为错误:零手动操作,避免漏打包、错配置等问题

    6.2 思考题

  • 如果要在CI/CD流程中加入"蓝绿部署"或"金丝雀发布",应该如何修改流水线配置?
  • 如何优化流水线的执行速度?比如缓存Maven依赖、并行执行测试等?
  • 对于多环境(开发、测试、预发、生产)的部署,如何复用流水线配置?
  • 6.3 个人观点

    CI/CD不是银弹,需要根据团队规模和项目特点灵活调整:

    • 小团队(5人以下):直接用GitLab CI即可,无需引入Jenkins,减少维护成本
    • 中大型团队:Jenkins的插件生态更丰富,适合复杂场景的定制化需求
    • 核心是"适合自己",不要为了用工具而用工具,解决团队的痛点才是关键

    另外,CI/CD只是DevOps的一部分,还要配合监控、告警、可观测性体系,才能构建完整的研发运维闭环。

    赞(0)
    未经允许不得转载:171主机测评 » 【架构实战】Jenkins+GitLab CI/CD:持续集成与持续部署实践
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址