找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2297

积分

0

好友

331

主题
发表于 17 小时前 | 查看: 4| 回复: 0

面对每天数百次的部署需求,传统的发布流程往往会成为效率瓶颈。本文分享一个在Kubernetes(K8s)环境中,整合Jenkins与ArgoCD等工具,构建高并发、可扩展的CI/CD方案的实际案例,旨在为面临类似挑战的团队提供参考。

环境背景

  • 服务运行环境: K8s
  • 应用模块数量: 500+
  • 应用运行环境: 开发、测试、预发、生产
  • 语言类型: Java、Golang、Node.js(动/静态)、PHP(同语言存在多版本情况)
  • 功能需求:
    • 代码管理、质量扫描、编译打包、单元测试、镜像构建
    • 灰度/全量发布、回滚、权限控制
    • 开发、测试环境提交代码后自动触发CI/CD,其他环境手动触发
    • 发布窗口功能(非发布窗口走紧急审批路线)
    • 实时进度反馈与异常错误输出
    • 多环境独立部署与流程控制(先灰度后全量)
    • 集成飞书同步任务结果
    • 合理利用资源,控制成本

工具选型

CI/CD涵盖的功能众多,支撑工具也多种多样。我们根据实际技术栈和需求,经过综合考量后,选择了以下方案:

CI/CD工具选型表

架构拓扑

基于上述选型,我们组合出了如下的整体运维/DevOps/SRE架构拓扑图:

整体架构拓扑图

为了应对“高并发构建任务+合理利用资源”的场景,我们将CI Job运行在K8s集群动态创建的临时Pod中。任务执行结束后,Pod自动销毁,这样可以充分利用K8s的碎片化资源。

接下来,我们看看各环节的具体实施细节。

标准规范

面对数百个应用模块,没有统一的标准规范会让后期维护变得异常困难。因此,我们与研发团队共同制定了以下规范:

  • 应用名称

    • 统一应用命名标准,使其能够直接关联CI/CD任务名称、K8s Service、Deployment、容器名称以及告警指标标签等,减少额外的映射关系维护成本。
  • 环境与分支

    • 所有应用的不同环境,都绑定到固定的Git分支,对应关系如下:

环境与分支对应表

  • 语言类型与编译规范

    • 同类型语言采用统一的构建方式。在CI过程中,只需判断语言类型即可获取对应的编译指令。对于存在特殊差异的应用,可通过分支标签作为附加判断条件。
  • 运行环境

    • 为了简化CD部署逻辑,我们统一了应用的运行环境标准,包括:运行目录、启动脚本(如 start.sh ${env})、服务端口、日志路径等。

规范制定主要基于自身环境需求,方法论可灵活参考。

CI方案实施

下面,我们按照标准CI/CD流程,看看如何将这套定制化方案落地。组件部署相对简单,本文重点在于打通整体功能链路。整个流程可参考下图:

CI/CD完整流程图

  • 自动化构建部署
    • 利用GitLab的Webhook功能,将需要自动部署的项目/环境与对应的Jenkins Job关联。当相关分支有代码提交时,即可自动触发构建任务。
    • GitLab端配置

GitLab Webhook配置

*   **Jenkins Job配置**:

Jenkins触发器配置

  • Jenkins动态创建Slave节点
    • 基于Jenkins的Kubernetes插件和Agent机制,实现任务执行时在K8s集群中自动创建临时的Slave Pod,任务完成后自动销毁以释放资源。
    • 配置步骤:
      1. 创建K8s RBAC,配置Jenkins访问K8s集群的权限,然后在Jenkins中创建Cloud。

Jenkins配置K8s Cloud

    2.  配置Pod Template,按不同语言类型划分,以应对环境差异。

Java语言Pod模板配置
Pod YAML配置片段

    *   **注意**:为了提高编译效率,我们配置了缓存机制,需要将PV/PVC挂载到任务Pod中。
  • 运行CI构建环境
    • 基于Jenkins Pipeline,根据项目语言类型和环境参数,选择不同的代码分支和构建环境。配置片段如下:
node() {
    stage('Set Agent') {
        AGENT_LABEL = "${params.LANGUAGE}"
    }
}

pipeline {
    agent {
        kubernetes {
            inheritFrom "${AGENT_LABEL}"
        }
    }
    stages {
        stage("Initialization") {
            steps {
                script {
                    if (params.BUILD_ENV == 'prod') {
                        env.GIT_BRANCH = 'release'
                    } else {
                        env.GIT_BRANCH = "${params.BUILD_ENV}"
                    }
                }
            }
        }
        stage('Git Clone Code') {
            steps {
                checkout([
                    $class: 'GitSCM',
                    branches: [[name: "${env.GIT_BRANCH}"]],
                    extensions: [
                        [$class: 'RelativeTargetDirectory', relativeTargetDir: 'code-clone'],
                        [$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true]],
                    userRemoteConfigs: [[credentialsId: "jenkins", url: "${GITLAB_URL}"]]
                ])
            }
        }
        stage('Compile Package') {
            steps {
                script {
                    def LANGUAGE = "${params.LANGUAGE}"
                    container("${LANGUAGE}") {
                        sh "bash -x codecompile.sh ${LANGUAGE}"
                    }
                }
            }
        }
        stage('Image Build And Publish') {
            steps {
                script {
                    container('docker') {
                        sh "bash -x imagebuild.sh ${LANGUAGE}"
                    }
                }
            }
        }
    }
}
  • 质量检测与单元测试
    • 代码质量检测和单元测试是保障软件质量的关键环节。业界常用SonarQube进行代码质量分析(确保代码“做好事”),用JUnit进行单元测试(确保代码“做对事”)。二者常协同工作,构建从单元测试到全面代码质量分析的保障体系。
    • 我们通过Jenkins插件集成SonarQube和JUnit,并利用JaCoCo收集测试覆盖率报告,实现“质量门禁”功能。效果参考如下:

SonarQube质量分析结果
JUnit测试结果趋势图

*   在CI/CD流程中结合二者,能有效降低代码缺陷率,提升软件可维护性和安全性。
  • 代码编译和镜像构建
    • 根据前文制定的标准规范,通过不同的语言参数变量来获取对应的编译指令。例如:
function JUDGE_COMPILE_CMD() {
    if [[ ${LANGUAGE} == "java" ]];then
            EXEC_COMPILE_CMD="mvn clean package -DskipTests"
    elif [[ ${LANGUAGE} == "golang1-17" || ${LANGUAGE} == "golang1-18" ]] ;then
            EXEC_COMPILE_CMD="build.sh"
    elif [[ ${LANGUAGE} == "php" ]] ;then
            EXEC_COMPILE_CMD="xxxxx.sh"
    elif [[ ${LANGUAGE} == "nodejs" ]] ;then
            EXEC_COMPILE_CMD="npm install && npm i nodeinstall -g && nodeinstall"
    fi
}
*   镜像构建逻辑类似,根据语言类型选择相应的基础镜像。Dockerfile支持自定义,也支持动态生成。示例片段如下:
function DOCKER_FILE() {
    if [[ ! -f "deploy/Dockerfile" ]]; then
        echo "FROM ${BASE_IMAGE}" > Dockerfile
        echo "WORKDIR /home/services" >>  Dockerfile
        echo "ADD start_env/ /home/services/" >> Dockerfile

        mkdir -p ./start_env/logs

        if [[ ${LANGUAGE} == "java" ]]; then
            cp -a code-clone/target/${SERVICE_NAME}.jar ./start_env/project/${SERVICE_NAME}.jar
        elif [[ ${LANGUAGE} == "nodejs" ]]; then
            cp -a code-clone/dist ./start_env/project
        else
            cp -a code-clone ./start_env/project
        fi
    else
        cp code-clone/deploy/Dockerfile ./
    fi
}

function IMAGE_BUILD() {
    docker build -t "${IMAGE_NAME}" .
    docker push ${IMAGE_NAME}
}
*   以上代码仅为展示方案逻辑,供参考。
*   **关于CI产物**:还有一种方案是CI过程中不构建镜像,只生成编译包并上传至制品库,在发布时再拉取产物运行。可根据实际环境选择。

CD方案实施

对于持续部署(CD)部分,无论是使用Jenkins原生方式还是其K8s插件,体验都不够友好。因此我们选用了云原生交付工具ArgoCD。下面看看具体细节:

  • CI/CD打通

    • 我们在Jenkins中将CI和CD任务分离为独立的Job,并且每个环境都有独立的CD Job(支持独立发布,多环境部署互不阻塞)。在Pipeline中定义:当CI构建成功时,自动触发对应的CD Job。参考片段:
      build job: "${SERVICE_ENV}", parameters: [
      string(name: "IMAGE_VERSION", value: "${IMAGE_VERSION}")
      ]
  • 部署逻辑

    • 部署逻辑很简单:修改应用配置中的镜像版本号,然后提交到Git仓库。ArgoCD监听配置仓库的变更,一旦发现变更即触发部署。
    • 为了提高部署响应速度,我们没有完全依赖原生的监听触发机制(可能存在延迟),而是在提交配置后,直接通过命令行执行sync操作来立即触发部署。
    • ArgoCD和Jenkins本是独立工具,为了提升用户体验,我们通过脚本将二者串联,使得开发者无需在CI完成后登录ArgoCD控制台,而可以直接在Jenkins Job中触发部署。命令参考:
      argocd app sync "$JOB_BASE_NAME" --grpc-web
  • 部署状态与异常信息捕获

    • 触发部署后,需要实时获取部署进度、状态,并在发布异常时查看具体的错误信息。错误信息通常包含K8s集群的编排错误和应用运行时日志。
    • 虽然可以通过登录ArgoCD控制台查看,但为了体验统一,我们通过API或CLI方式将这些信息整合到了Jenkins构建结果中。效果参考:

部署错误日志详情

  • 结果同步到飞书
    • 构建/发布的结果和状态需要实时推送到协同办公工具。常见的钉钉、飞书、企业微信都有相关插件。
    • 我们使用飞书,默认插件支持群机器人。也可以采用应用机器人,通过接口实现消息定向发送给个人。效果参考:

Jenkins构建成功通知

  • 关于发布窗口和权限控制
    • 为了保障线上稳定性,让“变更”可控,我们针对版本迭代定制了固定的发版窗口(例如每周二、周四),并对项目发布权限进行严格控制,通常只有项目责任人和技术负责人拥有发布权限。
    • Jenkins可以通过Role-based Authorization Strategy插件进行权限管理,但发布窗口功能没有现成的原生支持。当然,可以自行实现:在执行发布前加入时间判断逻辑。
    • 由于我们已有自研的运维管理平台,因此将整个CI/CD流程基于Jenkins封装了一层,将发布窗口和精细化的权限管理功能交由平台实现,CI/CD引擎专注于核心的集成与交付功能。

结语

CI/CD方案众多,没有最好的,只有最适合的。关键在于根据实际业务场景、团队技术栈和运维体系进行合理选型与定制。希望本文分享的这个基于云原生/IaaS环境的实战案例,能为正在构建或优化自身CI/CD流水线的团队带来一些启发和帮助。

欢迎在技术社区进行更深入的探讨,例如在云栈社区的相关板块交流CI/CD、K8s和DevOps实践。




上一篇:Python免费代理IP利器free-proxy:自动抓取验证,助爬虫突破访问限制
下一篇:Linux du命令详解:快速定位磁盘空间占用与清理大文件
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-16 21:13 , Processed in 0.224189 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表