百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Jenkins Pipeline 进阶:多平台账号关联与自动化部署

yuyutoo 2025-03-20 20:52 5 浏览 0 评论

引 言

在当今快速迭代的软件开发环境中,持续集成和持续交付 (CI/CD) 已成为不可或缺的实践。Jenkins 作为一款开源的自动化服务器,凭借其强大的插件生态系统和灵活的 Pipeline 功能,成为 CI/CD 领域的佼佼者。本文将介绍如何使用 Jenkins Pipeline 实现 CI/CD,并重点讲解如何将钉钉账号与 OpenLDAP 联动,以及如何实现 Jenkins、GitLab、Harbor 和禅道等多平台的账号关联和集成。

架构图

Jenkins Pipeline CI/CD

Jenkins Pipeline 是一种将构建、测试和部署等步骤以代码形式定义的机制。它使用 Groovy DSL 语法,可以将整个 CI/CD 流程编写成一个脚本文件,方便版本控制和重复使用。

部署

如果还没有部署Jenkins,可以使用下面的docker-compose.yml配置快速拉起一个Jenkins

docker-compose.yml

version: '3.7'
services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /data/jenkins/jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker
    ports:
      - "8080:8080"
      - "50000:500000"
    expose:
      - "8080"
      - "50000"
    privileged: true
    user: root
    restart: always

配置共享库

系统管理 ->系统配置-> Global Trusted Pipeline Libraries

Jenkins Pipeline CI

CI代码

下面是使用maven构建linux/win平台docker镜像的流程

@Library('jenkins-shared-library@main') _
// 引入 Jenkins 共享库,名称为 'jenkins-shared-library',使用 'main' 分支。


pipeline {
    agent any
    // 指定 Pipeline 在任何可用的 Jenkins Agent 上运行。


    parameters {
        string(name: 'BRANCH_NAME', defaultValue: 'master', description: 'Git分支名称')
        // 定义一个字符串参数,用于指定 Git 分支名称,默认值为 'master'。


        string(name: 'ZENTAO_REPOPATH', defaultValue: '', description: 'gitlab 项目链接')
        // 定义一个字符串参数,用于指定 GitLab 项目的 URL。


        string(name: 'ZENTAO_ACCOUNT', defaultValue: '', description: 'zentao 用户')
        // 定义一个字符串参数,用于指定禅道(Zentao)的用户名。


        choice(name: 'BUILD_PLATFORM', choices: ['linux', 'windows'], description: '构建平台')
        // 定义一个选择参数,用于指定构建平台,可选值为 'linux' 或 'windows'。
    }


    environment {
        CURRENT_DATE = sh(script: "date +'%Y%m%d%H%M%S'", returnStdout: true).trim()
        // 定义一个环境变量 CURRENT_DATE,值为当前日期时间,格式为 'YYYYMMDDHHMMSS'。


        BUILD_COMMIT = ''
        // 定义一个环境变量 BUILD_COMMIT,初始值为空,后续会存储 Git 提交 ID。


        ImageName = ''
        // 定义一个环境变量 ImageName,初始值为空,后续会存储 Docker 镜像名称。


        GITLAB_GROUP = ''
        // 定义一个环境变量 GITLAB_GROUP,初始值为空,后续会存储 GitLab 项目组名称。


        GITLAB_PROJECT = ''
        // 定义一个环境变量 GITLAB_PROJECT,初始值为空,后续会存储 GitLab 项目名称。
    }


    stages {
        stage("提取项目和项目名") {
            steps {
                script {
                    def res = getGitLabGroupAndProject.extractGitLabInfo(params.ZENTAO_REPOPATH)
                    // 调用共享库中的方法 extractGitLabInfo,从 GitLab 项目 URL 中提取项目组和项目名称。


                    GITLAB_GROUP = res.group
                    // 将提取到的 GitLab 项目组名称赋值给环境变量 GITLAB_GROUP。


                    GITLAB_PROJECT = res.project
                    // 将提取到的 GitLab 项目名称赋值给环境变量 GITLAB_PROJECT。
                }
            }
        }


        stage("构建邮件通知") {
            steps {
                script {
                    // 触发构建消息。
                }
            }
        }


        stage('拉取代码并构建') {
            agent {
                docker {
                    image "${GlobalVars.MavenBuildImage}"
                    // 使用共享库中定义的 Maven 构建镜像。


                    args '-v /data/.m2:/root/.m2 -v /data/jar-package:/data/jar-package'
                    // 挂载宿主机的 Maven 缓存目录和构建产物目录到容器中。


                    reuseNode true
                    // 重用当前节点,避免切换到其他节点。
                }
            }
            steps {
                checkout scmGit(
                    branches: [[name: "*/${params.BRANCH_NAME}"]],
                    // 指定要拉取的分支名称,使用参数 BRANCH_NAME。


                    extensions: [],
                    // 无额外的 Git 扩展配置。


                    userRemoteConfigs: [[
                        credentialsId: "${GlobalVars.GitLabcredentialsId}",
                        // 使用共享库中定义的 GitLab 凭据 ID。


                        url: "https://${GlobalVars.GitlabUrl}/${GITLAB_GROUP}/${GITLAB_PROJECT}.git"
                        // 指定 GitLab 项目的 URL。
                    ]]
                )
                script {
                    sh 'mvn -B -DskipTests clean package'
                    // 使用 Maven 进行构建,跳过测试。


                    sh "cp ruoyi-admin/target/ruoyi-admin.jar /data/jar-package/${GITLAB_GROUP}-${GITLAB_PROJECT}-${CURRENT_DATE}.jar"
                    // 将构建产物(JAR 文件)复制到宿主机的指定目录,并重命名。
                }
            }
        }


        stage('保存构建产物') {
            steps {
                stash includes: 'ruoyi-admin/target/ruoyi-admin.jar', name: 'jar-package'
                // 将构建产物(JAR 文件)保存为 Jenkins 的 stash,供后续阶段使用。
            }
        }


        stage('构建信息') {
            steps {
                script {
                    wrap([$class: 'BuildUser']) {
                        // 使用 Jenkins 的 BuildUser 插件获取构建用户信息。


                        BUILD_USER = "${ZENTAO_ACCOUNT}"
                        // 将禅道用户赋值给环境变量 BUILD_USER。


                        BUILD_NAME = "${GITLAB_GROUP}/${GITLAB_PROJECT}"
                        // 将项目组和项目名称拼接后赋值给环境变量 BUILD_NAME。


                        BUILD_BRANCH = "${BRANCH_NAME}"
                        // 将分支名称赋值给环境变量 BUILD_BRANCH。


                        BUILD_COMMIT = "${getCommitId()}"
                        // 调用共享库中的方法 getCommitId,获取当前 Git 提交 ID,并赋值给环境变量 BUILD_COMMIT。


                        BUILD_PLATFORM = "${params.BUILD_PLATFORM}"
                        // 将构建平台赋值给环境变量 BUILD_PLATFORM。
                    }
                    currentBuild.description = """构建用户: ${BUILD_USER}\n构建项目: ${BUILD_NAME}\n构建分支: ${BUILD_BRANCH}\n构建平台: ${BUILD_PLATFORM}\nMessageID: ${BUILD_COMMIT}
                    """
                    // 设置当前构建的描述信息,包含构建用户、项目、分支、平台和提交 ID。
                }
            }
        }


        stage('构建镜像名称') {
            steps {
                script {
                    ImageName = "${GlobalVars.HarborUrl}/${GITLAB_GROUP}/${GITLAB_PROJECT}:${CURRENT_DATE}-${BUILD_COMMIT}-${params.BUILD_PLATFORM}"
                    // 根据 Harbor 地址、项目组、项目名称、日期、提交 ID 和构建平台生成 Docker 镜像名称。
                }
            }
        }


        stage('打包镜像') {
            steps {
                script {
                    if (params.BUILD_PLATFORM == 'linux') {
                        buildDockerImage.LinuxRuoyiBuild(ImageName)
                        // 如果构建平台是 Linux,调用共享库中的方法 LinuxRuoyiBuild 打包 Docker 镜像。
                    } else if (params.BUILD_PLATFORM == 'windows') {
                        node('win-agent') {
                            unstash 'jar-package'
                            // 如果构建平台是 Windows,切换到 Windows 节点,并恢复之前保存的构建产物。


                            buildDockerImage.WindowsRuoyiBuild(ImageName)
                            // 调用共享库中的方法 WindowsRuoyiBuild 打包 Docker 镜像。
                        }
                    }
                }
            }
        }


        stage('推送镜像') {
            steps {
                script {
                    if (params.BUILD_PLATFORM == 'linux') {
                        withDockerRegistry(credentialsId: "${GlobalVars.HarborcredentialsId}", url: "https://${GlobalVars.HarborUrl}") {
                            sh "docker push ${ImageName}"
                            // 如果构建平台是 Linux,推送 Docker 镜像到 Harbor。


                            sh "docker rmi ${ImageName}"
                            // 删除本地 Docker 镜像以释放空间。
                        }
                    } else if (params.BUILD_PLATFORM == 'windows') {
                        node('win-agent') {
                            withDockerRegistry(credentialsId: "${GlobalVars.HarborcredentialsId}", url: "https://${GlobalVars.HarborUrl}") {
                                bat "docker push ${ImageName}"
                                // 如果构建平台是 Windows,推送 Docker 镜像到 Harbor。


                                bat "docker rmi ${ImageName}"
                                // 删除本地 Docker 镜像以释放空间。
                            }
                        }
                    }
                }
            }
        }
    }


    post {
        always {
            cleanWs()
            // 无论构建成功与否,始终清理工作空间。
        }
        success {
            script {
                // 构建成功时发送通知。
            }
        }
        unsuccessful {
            script {
                // 构建失败时发送通知。
            }
        }
    }
}

该 Jenkins Pipeline 脚本实现了一个完整的 CI 流程

  1. 参数化构建:支持动态指定 Git 分支、GitLab 项目、禅道用户和构建平台。
  2. 代码拉取与构建:从 GitLab 拉取代码并使用 Maven 进行构建。
  3. 镜像打包与推送:根据构建平台(Linux 或 Windows)打包 Docker 镜像并推送到 Harbor。
  4. 构建信息记录:记录构建用户、项目、分支、平台和提交 ID 等信息。
  5. 通知与清理:支持构建成功或失败时发送通知,并始终清理工作空间。

通过共享库的使用,脚本的逻辑更加简洁和模块化,便于维护和扩展。

创建构建流程

写好CI流程,下面在Jenkins添加构建流程

基于禅道关联Jenkins任务

禅道平台配置关联jenkins,方便用户调用触发

Jenkins Pipeline CD

CD 代码

@Library('jenkins-shared-library@main') _
// 引入 Jenkins 共享库,名称为 'jenkins-shared-library',使用 'main' 分支。


pipeline {
    agent any
    // 指定 Pipeline 在任何可用的 Jenkins Agent 上运行。


    parameters {
        string(name: 'SSH_USER', defaultValue: 'root', description: 'SSH User')
        // 定义一个字符串参数,用于指定 SSH 用户,默认值为 'root'。


        string(name: 'TARGET_IP', defaultValue: '', description: '部署机器 IP 地址')
        // 定义一个字符串参数,用于指定目标机器的 IP 地址。


        string(name: 'DOCKER_IMAGE', defaultValue: '', description: '运行服务镜像地址')
        // 定义一个字符串参数,用于指定 Docker 镜像的名称。


        string(name: 'IMAGE_VERSION', defaultValue: '', description: '运行服务镜像版本号')
        // 定义一个字符串参数,用于指定 Docker 镜像的版本号。


        string(name: 'SERVICE_NAME', defaultValue: '', description: '容器运行名称')
        // 定义一个字符串参数,用于指定 Docker 容器的名称。


        string(name: 'PORT_MAPPING', defaultValue: '', description: '端口映射,eg: 8080:8080')
        // 定义一个字符串参数,用于指定 Docker 容器的端口映射。


        string(name: 'ENV_VARIABLES', defaultValue: '', description: '环境变量,eg: KEY="VALUE",KEY2="VALUE2"')
        // 定义一个字符串参数,用于指定 Docker 容器的环境变量。
    }


    environment {
        SEND_BUILD_USER_EMAIL = ''
        // 定义一个环境变量 SEND_BUILD_USER_EMAIL,初始值为空,后续会存储构建用户的邮箱。
    }


    stages {
        stage('获取参数') {
            steps {
                script {
                    withBuildUser {
                        SEND_BUILD_USER_EMAIL = env.BUILD_USER_EMAIL
                        // 使用 Jenkins 的 BuildUser 插件获取构建用户的邮箱,并赋值给环境变量 SEND_BUILD_USER_EMAIL。
                    }


                    // 检查参数是否为空
                    if (!params.SSH_USER?.trim()) {
                        error "SSH 用户不能为空!"
                        // 如果 SSH 用户为空,抛出错误并终止 Pipeline。
                    }
                    if (!params.TARGET_IP?.trim()) {
                        error "目标机器 IP 地址不能为空!"
                        // 如果目标机器 IP 地址为空,抛出错误并终止 Pipeline。
                    }
                    if (!params.SERVICE_NAME?.trim()) {
                        error "服务名称不能为空!"
                        // 如果服务名称为空,抛出错误并终止 Pipeline。
                    }
                    if (!params.DOCKER_IMAGE?.trim()) {
                        error "Docker 镜像名称不能为空!"
                        // 如果 Docker 镜像名称为空,抛出错误并终止 Pipeline。
                    }
                    if (!params.IMAGE_VERSION?.trim()) {
                        error "Docker 镜像版本号不能为空!"
                        // 如果 Docker 镜像版本号为空,抛出错误并终止 Pipeline。
                    }
                    if (!params.PORT_MAPPING?.trim()) {
                        error "端口映射不能为空!"
                        // 如果端口映射为空,抛出错误并终止 Pipeline。
                    }
                }
            }
        }


        stage('部署服务') {
            steps {
                script {
                    def envVariables = ""
                    if (params.ENV_VARIABLES?.trim()) {
                        def envList = params.ENV_VARIABLES.split(',')
                        envVariables = envList.collect { envVar ->
                            "-e ${envVar.trim()}"
                        }.join(' ')
                        // 如果环境变量参数不为空,将其拆分为多个环境变量,并格式化为 Docker 命令中的 `-e` 参数。
                    }


                    def dockerCommand = ""
                    withCredentials([usernamePassword(credentialsId: 'devops_harbor', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
                        // 使用 Jenkins 的 Credentials 插件获取 Harbor 仓库的用户名和密码。


                        dockerCommand = """
                            docker login harbor.wuyinit.com -u $HARBOR_USERNAME -p $HARBOR_PASSWORD &&
                            docker pull ${params.DOCKER_IMAGE}:${params.IMAGE_VERSION} &&
                            docker stop ${params.SERVICE_NAME} || true &&
                            docker rm ${params.SERVICE_NAME} || true &&
                            docker run -itd --name ${params.SERVICE_NAME} -p ${params.PORT_MAPPING} ${envVariables} ${params.DOCKER_IMAGE}:${params.IMAGE_VERSION}
                        """
                        // 构建 Docker 命令,包括:
                        // 1. 登录 Harbor 仓库。
                        // 2. 拉取指定版本的 Docker 镜像。
                        // 3. 停止并删除已存在的同名容器。
                        // 4. 启动新的容器,并指定名称、端口映射和环境变量。
                    }


                    sshagent(['deploy-agent-key']) {
                        // 使用 Jenkins 的 SSH Agent 插件,加载指定的 SSH 私钥('deploy-agent-key')。


                        sh """
                        ssh -o StrictHostKeyChecking=no ${params.SSH_USER}@${params.TARGET_IP} << 'EOF'
                        ${dockerCommand}
EOF
                        """
                        // 通过 SSH 连接到目标机器,并执行 Docker 命令。
                    }
                }
            }
        }
    }


    post {
        success {
            script {
                // 构建成功时发送邮件通知。
            }
        }


        unsuccessful {
            script {
                // 构建失败时发送邮件通知。
            }
        }
    }
}

该 Jenkins Pipeline 脚本实现了一个完整的服务部署流程,包括:

  1. 参数化构建:支持动态指定 SSH 用户、目标机器 IP、Docker 镜像、版本号、服务名称、端口映射和环境变量。
  2. 参数检查:确保所有必填参数不为空。
  3. 服务部署
  4. 登录 Harbor 仓库并拉取 Docker 镜像。
  5. 停止并删除已存在的同名容器。
  6. 启动新的容器,并指定名称、端口映射和环境变量。
  7. 通知与清理:支持构建成功或失败时发送通知。

通过共享库和 Jenkins 插件(如 Credentials、SSH Agent)的使用,脚本的逻辑更加简洁和模块化,便于维护和扩展。

钉钉与OpenLDAP的联动

为了实现统一身份认证,并基于现有的钉钉组织和人员信息,将Jenkins、GitLab、Harbor 和禅道等多平台的账号关联,我们可以使用 OpenLDAP 作为统一的用户认证中心。具体步骤如下:

1.配置 Go-Ldap-Admin 等服务:将钉钉通讯录中的用户和组织结构同步到 OpenLDAP 服务器。

2.配置各平台使用 OpenLDAP 认证: 在 Jenkins、GitLab、Harbor 和禅道的系统设置中,配置 LDAP 认证,并指定 OpenLDAP 服务器的地址、端口、Base DN 等信息。

docker-compose参考,详情查看

version: '3'


networks:
  go-ldap-admin:
    driver: bridge


services:
  go-ldap-admin:
    image: registry.cn-hangzhou.aliyuncs.com/eryajf/go-ldap-admin
    container_name: go-ldap-admin
    hostname: go-ldap-admin
    restart: always
    environment:
      WAIT_HOSTS: openldap:389
    configs:
      - source: go_ldap_admin_config
        target: /app/config.yml
    ports:
      - 8888:8888
    volumes:
      - ./data/go-ldap-admin:/app/data
    depends_on:
      - openldap
    links:
      - openldap:go-ldap-admin-openldap
    networks:
      - go-ldap-admin


  openldap:
    image: registry.cn-hangzhou.aliyuncs.com/eryajf/openldap:1.4.1
    container_name: go-ldap-admin-openldap
    hostname: go-ldap-admin-openldap
    restart: always
    environment:
      TZ: Asia/Shanghai
      LDAP_ORGANISATION: "eryajf.net"
      LDAP_DOMAIN: "eryajf.net"
      LDAP_ADMIN_PASSWORD: "123456"
    command: [ '--copy-service' ]
    volumes:
      - ./data/openldap/database:/var/lib/ldap
      - ./data/openldap/config:/etc/ldap/slapd.d
    ports:
      - 389:389
    networks:
      - go-ldap-admin

总 结

通过 Jenkins Pipeline、OpenLDAP 和钉钉的集成,我们可以实现高效的 CI/CD 流程,并统一管理多平台的用户账号。本文提供的方案只是一个简单的示例,实际应用中可以根据具体需求进行调整和扩展。

未来展望

未来,我们可以探索以下方向:

  • 使用 Kubernetes 进行容器化部署,进一步提高资源利用率和部署效率。
  • 引入代码质量分析工具,例如 SonarQube,提升代码质量。
  • 实现自动化测试,例如使用 Selenium 进行 UI 自动化测试。

如有相关问题,请在文章后面给小编留言,小编安排作者第一时间和您联系,为您答疑解惑。


参考文献

  • Jenkins 官方文档: https://www.jenkins.io/doc/
  • OpenLDAP 官方文档:https://www.openldap.org/doc/
  • 钉钉开发者文档:https://open.dingtalk.com/document/orgapp-server
  • Go-Ldap-Admin: https://ldapdoc.eryajf.net/

相关推荐

ETCD 故障恢复(etc常见故障)

概述Kubernetes集群外部ETCD节点故障,导致kube-apiserver无法启动。...

在Ubuntu 16.04 LTS服务器上安装FreeRADIUS和Daloradius的方法

FreeRADIUS为AAARadiusLinux下开源解决方案,DaloRadius为图形化web管理工具。...

如何排查服务器被黑客入侵的迹象(黑客 抓取服务器数据)

---排查服务器是否被黑客入侵需要系统性地检查多个关键点,以下是一份详细的排查指南,包含具体命令、工具和应对策略:---###**一、快速初步检查**####1.**检查异常登录记录**...

使用 Fail Ban 日志分析 SSH 攻击行为

通过分析`fail2ban`日志可以识别和应对SSH暴力破解等攻击行为。以下是详细的操作流程和关键分析方法:---###**一、Fail2ban日志位置**Fail2ban的日志路径因系统配置...

《5 个实用技巧,提升你的服务器安全性,避免被黑客盯上!》

服务器的安全性至关重要,特别是在如今网络攻击频繁的情况下。如果你的服务器存在漏洞,黑客可能会利用这些漏洞进行攻击,甚至窃取数据。今天我们就来聊聊5个实用技巧,帮助你提升服务器的安全性,让你的系统更...

聊聊Spring AI Alibaba的YuQueDocumentReader

序本文主要研究一下SpringAIAlibaba的YuQueDocumentReaderYuQueDocumentReader...

Mac Docker环境,利用Canal实现MySQL同步ES

Canal的使用使用docker环境安装mysql、canal、elasticsearch,基于binlog利用canal实现mysql的数据同步到elasticsearch中,并在springboo...

RustDesk:开源远程控制工具的技术架构与全场景部署实战

一、开源远程控制领域的革新者1.1行业痛点与解决方案...

长安汽车一代CS75Plus2020款安装高德地图7.5

不用破解原车机,一代CS75Plus2020款,安装车机版高德地图7.5,有红绿灯读秒!废话不多讲,安装步骤如下:一、在拨号状态输入:在电话拨号界面,输入:*#518200#*(进入安卓设置界面,...

Zookeeper使用详解之常见操作篇(zookeeper ui)

一、Zookeeper的数据结构对于ZooKeeper而言,其存储结构类似于文件系统,也是一个树形目录服务,并通过Key-Value键值对的形式进行数据存储。其中,Key由斜线间隔的路径元素构成。对...

zk源码—4.会话的实现原理一(会话层的基本功能是什么)

大纲1.创建会话...

Zookeeper 可观测性最佳实践(zookeeper能够确保)

Zookeeper介绍ZooKeeper是一个开源的分布式协调服务,用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题,如数据同步、配置管理、命名服务和集群...

服务器密码错误被锁定怎么解决(服务器密码错几次锁)

#服务器密码错误被锁定解决方案当服务器因多次密码错误导致账户被锁定时,可以按照以下步骤进行排查和解决:##一、确认锁定状态###1.检查账户锁定状态(Linux)```bash#查看账户锁定...

zk基础—4.zk实现分布式功能(分布式zk的使用)

大纲1.zk实现数据发布订阅...

《死神魂魄觉醒》卡死问题终极解决方案:从原理到实战的深度解析

在《死神魂魄觉醒》的斩魄刀交锋中,游戏卡死犹如突现的虚圈屏障,阻断玩家与尸魂界的连接。本文将从技术架构、解决方案、预防策略三个维度,深度剖析卡死问题的成因与应对之策,助力玩家突破次元壁障,畅享灵魂共鸣...

取消回复欢迎 发表评论: