解锁 Jenkins Agent 技巧,容器化轻松实现分布式构建节点扩展

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ]

📢 大家好,我是 WeiyiGeek,一个正在向全栈工程师(SecDevOps)前进的计算机技术爱好者,欢迎各位道友一起学习交流、一起进步 🚀,若此文有帮助请点个关注,后续追番不迷路 ❤️。

解锁 Jenkins Agent 技巧,容器化轻松实现分布式构建节点扩展

描述:Jenkins 是一个开源的自动化构建工具,广泛用于持续集成和持续部署(CI/CD),它通过插件支持多种编程语言和构建工具,所以其功能非常的强大。为了处理大规模的项目或高并发场景,Jenkins 支持分布式构建架构,允许将任务分散到多个节点上执行。Jenkins Agent 是 Jenkins 实现分布式构建的关键组件,通过将任务分发到不同节点,显著提升了 CI/CD 的效率和灵活性。合理使用固定 Agent 和动态 Agent,可以平衡稳定性和成本。此处 Jenkins 介绍安装就不在累述,不了解的童鞋 推荐参考作者以前发布的【Jenkins】专栏。

本文将介绍 Jenkins Agent 使用技巧,作者从实践视角出发,通过使用 Docker 搭建部署 Jenkins Agent 容器环境,并通过 SSH 方式快速接入,从而轻松实现分布式构建节点,最后使用加入的Agent节点,用于构建作者以 Golang 开发的运维Api接口项目。


前置知识

什么是 Jenkins Agent?

Jenkins Agent(也称为 Jenkins 节点或从节点)是 Jenkins 分布式构建架构中的工作节点,用于执行主节点(Master)分发的任务(如代码构建、测试、部署等)。它允许将工作负载分散到多台机器上,提高并发处理能力和资源利用率。


什么是主节点(Master)、代理节点(Agent)

描述:在 Jenkins 中,主节点(Master,实际上就是部署的 jekins 服务那台主机就是主节点)是控制中心,负责管理任务、插件配置和用户界面,通常不直接执行耗时任务。代理节点(Agent)也称工作节点,它是执行实际构建任务的机器,可以是固定节点也可是动态扩展,支持不同操作系统和环境,可以是物理机、虚拟机、容器或 Kubernetes Pod。


Jenkins Agent 的类型

  • 固定 Agent:常驻节点,手动配置(如专用服务器、虚拟服务器、容器)。

  • 动态 Agent:按需创建(如通过 Kubernetes/Docker 临时启动,任务完成后销毁)。

  • 云 Agent:集成云平台(AWS、Azure 等)自动扩缩容。


Jenkins Agent 核心功能

  • 任务执行: 运行 Jenkins 分发的构建任务(如编译代码、运行测试)。

  • 环境隔离: 不同 Agent 可以配置不同的工具链(JDK、Node.js 等),适配多项目需求。

  • 负载均衡: 多个 Agent 并行处理任务,加快流水线速度。

  • 资源扩展: 动态 Agent 在任务高峰期自动扩容,空闲时释放资源。

Jenkins Agent 的通信方式


实践之路

环境介绍

# Docker 20.10.12 部署 Jenkins 2.387.3 (需要 JDK 11+)
$ docker ps | grep jenkins_server
d6b80d5b2fb6   jenkins/jenkins:2.387.3-alpine   "/sbin/tini -- /usr/…"   13 months ago   Up 1 months (healthy)   0.0.0.0:8080->8080/tcp, 0.0.0.0:8443->8443/tcp, 0.0.0.0:50000->50000/tcp   jenkins_server

# Docker 20.10.12 部署 jenkins/ssh-agent:jdk11
$ docker ps | grep jenkins-ssh-agent
242249caaea6   jenkins/ssh-agent:jdk11   "setup-sshd"  8 days ago   Up 8 days  0.0.0.0:2222->22/tcp  jenkins-ssh-agent

操作步骤

1.首先,进入到 Jenkins Server 容器中,并且在 Jenkins 主节点上生成 SSH 密钥对。

$ docker exec -it jenkins_server bash
d6b80d5b2fb6:/$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file inwhich to save the key (/var/jenkins_home/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /var/jenkins_home/.ssh/id_rsa
Your public key has been saved in /var/jenkins_home/.ssh/id_rsa.pub
...
$ ls  ~/.ssh/id_rsa*
/var/jenkins_home/.ssh/id_rsa  /var/jenkins_home/.ssh/id_rsa.pub

# 分别打开两个文件,复制公钥、密钥内容
cat ~/.ssh/id_rsa  -> Jenkins 凭据
cat ~/.ssh/id_rsa.pub -> Jenkins Agent 镜像所需瓶颈

2.然后,在 Jenkins 网页界面上,进入系统管理,选择 Credentials 管理,添加新的凭据,类型选择 SSH Username With Private Key 私钥,粘贴刚才生成的私钥内容,配置如下图所示。

weiyigeek.top-Jenkins中添加SSH凭据图
weiyigeek.top-Jenkins中添加SSH凭据图

3.其次,在安装有 Docker 环境的的主机上,拉取并运行 Jenkins SSH Agent 镜像,直接一梭子搞定

# 创建jenkins-ssh-agent目录,用于持久化数据存储
mkdir -p /data/jenkins
chown -R 1000:1000 /data/jenkins

# 拉取 jenkins-ssh-agent 镜像,由于 Jenkins 2.387.3 中 agent.jar 运行环境最低需要 JDK 11+,所以这里选择 jdk11 版本镜像
docker pull jenkins/ssh-agent:jdk11

# 运行 jenkins-ssh-agent 容器,映射端口2222到宿主机,挂载密钥文件和 Docker socket,此处重点是JENKINS_AGENT_SSH_PUBKEY填写前面在控制节点生成的公钥。
docker run -d --name=jenkins-ssh-agent \
           -p 2222:22 \
           -v /data/jenkins:/home/jenkins \
           -v /var/run/docker.sock:/var/run/docker.sock \
           -v /usr/bin/docker:/usr/bin/docker \
           -e "JENKINS_AGENT_SSH_PUBKEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQ*************注释****************sp4Uij6ykY0bd8= jenkins@d6b80d5b2fb6" jenkins/ssh-agent:jdk11

# 特别注意:此处有可能在构建上传镜像时出现权限不足的问题,这是由于我们在容器中直接使用了宿主机上的 /var/run/docker.sock ,而容器中使用的是 jenkins 用户所以导致权限不足,解决办法如下,宿主机中运行
$ chmod +666 /var/run/docker.sock

4.然后,在 jenkins 上添加 slave,返回到 Jenkins 网页界面上,进入系统管理,选择节点管理选项卡,点击 New Node 选择 SSH 方式添加 Agent,输入节点名称自定义即可,选择类型为固定节点,然后按照如下图进行配置,特别注意红色圈中选项即可,需更加你实际配置进行填写,特别是设置标签以空格进行分割。

weiyigeek.top-Jenkins添加agent工作节点图
weiyigeek.top-Jenkins添加agent工作节点图

5.最后,点击保存,Jenkins 会自动通过 SSH 连接到 Jenkins Agent 容器并自动拉取 remoting.jar 文件,并且其容器中运行,可通过节点日志查看连接过程,当日志中出现 Agent successfully connected and online 则表示连接成功。

weiyigeek.top-查看节点连接日志图
weiyigeek.top-查看节点连接日志图

6.至此,Jenkins Agent 已经成功添加到 Jenkins 主节点中,接下来就可以在 Jenkins 项目中使用该 Agent 进行 golang 项目构建工作了。

weiyigeek.top-节点列举图
weiyigeek.top-节点列举图

7.构建 golang 项目之前,需要先准备 Dockerfile 我已经放在项目中,并上传到私有的Gitlab仓库中,若还没搭建过Gitlab代码仓库的同学可参考作者此篇【GitOps实践 | 快速在银河麒麟KylinOS国产系统部署最新Gitlab-CE企业私有代码仓库】文章。

# Dockerfile 内容如下,采用多阶段构建,先编译项目生成二进制文件,再拷贝到 alpine 镜像中运行。
FROM golang:1.20.4-alpine3.18 AS gin-build
MAINTAINER DevOpsApi Application v1.1.3 - <master@weiyigeek.top> - WeiyiGeek
ENV GO111MODULE="on" GOPROXY="https://goproxy.cn,direct"
WORKDIR /app
COPY . "/app"
RUN GOOS=linux GOARCH=amd64 && go mod download && go mod verify && go build -v -o /app/devopsapi

FROM alpine:3.21.3
WORKDIR /app
COPY --from=gin-build /app/devopsapi /app/devopsapi
COPY --from=gin-build /app/configs /app/configs
COPY --from=gin-build /app/docs /app/docs
COPY --from=gin-build /app/static /app/static
COPY --from=gin-build /app/templates /app/templates
COPY ./setup.sh /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirror.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
    && apk update \
    && apk add --no-cache tzdata \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo"export LANG=zh_CN.UTF-8" > /etc/profile.d/locale.sh \
    && chmod +x /app/devopsapi  /app/setup.sh
EXPOSE 8080
ENTRYPOINT ["/app/setup.sh"]
weiyigeek.top-基于Golang的API运维接口构建文件图
weiyigeek.top-基于Golang的API运维接口构建文件图

8.接下来,在 Jenkins 项目中配置构建流水线,名称为 itops-api ,采用流水线方式,点击配置流水线,选择Pipeline script方式,如下所示。

// 作者抽取了部分关键代码片段

// [ 全局变量]
def PRJECT = "ssh://git@git.weiyieek.top/devops/itops-api.git"
def PUBKEY = "5e2f46d9-6725-4988-847f-dafb3f29d0ce"

// [ 全局函数]
def ENV_TEST() {
def config = [:]
  config.HARBOR_URL = "harbor.weiyiggek.top/app"
  config.HARBOR_AUTH = "d0ce1239-c4bf-1256-a4c6-660ab70d9b47"
return config
}

// 任务名称、任务工作空间POD名称
def JOB_NAME = "${env.JOB_NAME}-${env.BUILD_NUMBER}"
def JOB_WORKSPACE = "${env.WORKSPACE}"

// 获取真实项目路径
def GET_REAL_PROJECT(){
def PROJECT = [:]
  PROJECT.name="itops-api"
  PROJECT.version="v1.1.8"
  PROJECT.path=sh label:'pom_path',returnStdout:true, script:"""
    find ${env.WORKSPACE} -name main.go | head -n 1 | tr -d '\n' | sed 's/main.go//g'
  """
  PROJECT.commitmsg=sh label:'git_commitmsg',returnStdout:true, script:"""
    git show --oneline --ignore-all-space --text | head -n 1 |tr -d '\\n'
  """
  PROJECT.commitid=sh label:'git_commitid',returnStdout:true, script:"""
    git show --oneline --ignore-all-space --text | head -n 1 | cut -d ' ' -f 1 |tr -d '\\n'
  """
  PROJECT.imagename=sh label:'imagename',returnStdout:true, script:"""
    echo \${JOB_NAME#*-} |tr -d '\\n'
  """
return PROJECT
}

// [ 流水线代码 ]
pipeline {
  agent {
    // 通过标签选择构建节点,此处选择就是就是 jenkins-ssh-agent节点
    label 'ssh-agent'
  }

  options {
    timeout(time:30, unit:'MINUTES') 
  }

// 自定义环境变量, 通过 env.变量名访问 
  environment {
    // 代码仓库地址与认证地址
    GITLAB_URL = "${PRJECT}"
    GITLAB_PUB = "${PUBKEY}"
  }

// 自定义选择参数,在 sh 中可通过变量名访问,而在 script pipeline 脚本中通过 params.参数名称 访问 
  parameters {
    gitParameter branch:'', branchFilter:'origin/(.*)', defaultValue:'origin/master', description:'查看构建部署可用的Tag或Branch名称?', name:'TagBranchName', quickFilterEnabled:false, selectedValue:'NONE', sortMode:'DESCENDING_SMART', tagFilter:'*', type:'GitParameterDefinition'
    string(name:'RELEASE_VERSION', defaultValue:"master", description:'Message: 请选择构建部署的Tag或Branch名称?', trim:'True')
    choice(name:'PREJECT_ENVIRONMENT', choices: ['Test','Prod'], description:'Message: 选择项目部署环境?')
  choice(name:'PREJECT_OPERATION', choices: ['None', 'deploy', 'rollback', 'redeploy','deployTest'], description:'Message: 选择项目操作方式?')
    choice(name:'IS_IMAGEBUILD', choices: ['True','False'], description:'Message: 是否进行镜像构建操作?')
    choice(name:'IS_RELEASE', choices: ['False','True'], description:'Message: 是否进行编译成品发布?')
  }

// 主要阶段以及子阶段流程
  stages {
    // [ 阶段1.项目代码拉取 ]
    stage ('代码拉取') {
      steps {
        // 1.构建信息输出
        echo "任务名称: ${JOB_NAME}, 项目地址: ${env.GITLAB_URL}, 构建版本: ${params.RELEASE_VERSION}, 部署环境: ${params.PREJECT_ENVIRONMENT} \n 构建操作: ${params.PREJECT_OPERATION}, 镜像构建: ${params.IS_IMAGEBUILD} , 成品发布: ${params.IS_RELEASE},"
     
        // 2.超时时间设置5分钟
        timeout(time:5, unit:'MINUTES') {
          script {
            try {
              checkout([$class:'GitSCM', branches: [[name:"${params.RELEASE_VERSION}"]], doGenerateSubmoduleConfigurations:false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId:"${env.GITLAB_PUB}", url:"${env.GITLAB_URL}"]]])
            } catch(Exception err) {
              echo err.toString()   
              error "[-Error] : 代码拉取失败\n [-Msg] : ${err.getMessage()} "
            }
            // .......此处省略部分代码...........
            // 6.利用GET_REAL_PROJECT函数获取JAVA项目真实的构建空间路径以及获取pom.xml项目文件信息
            project = GET_REAL_PROJECT()

            // 7.验证部署环境进行预设环境参数值
            if ( "${params.PREJECT_ENVIRONMENT}" == "Prod" ) {
              config = ENV_PROD()
              print "${params.PREJECT_ENVIRONMENT}"
            } else {
              config = ENV_TEST()
              print "${params.PREJECT_ENVIRONMENT}"
            }

            // 8.通过企业微信进行构建通知
            echo  "项目真实路径: ${project.path} \n项目信息: ${project.name}  ${project.version}\nCommit信息:${project.commitmsg} \n镜像仓库与名称:${config.HARBOR_URL}/${project.imagename}"
            // .......此处省略部分代码...........
          }
        }
     }
   }
    stage ("项目构建") {
        steps {
        script {
            //仓库认证
            withCredentials([usernamePassword(credentialsId:"${config.HARBOR_AUTH}", passwordVariable:'HARBOR_PWD', usernameVariable:'HARBOR_USR')]) {
              sh """
                docker login -u ${HARBOR_USR} -p ${HARBOR_PWD} ${config.HARBOR_URL}
              """
            }
            
            sh """
              cd ${project.path} \
              && cat Dockerfile \
              && echo "${config.HARBOR_URL}/${project.imagename}:${params.PREJECT_ENVIRONMENT}" \
              && docker build -t ${config.HARBOR_URL}/${project.imagename}:${params.PREJECT_ENVIRONMENT} -t ${config.HARBOR_URL}/${project.imagename}:${project.version}-${params.PREJECT_ENVIRONMENT} . \
              && docker push ${config.HARBOR_URL}/${project.imagename}:${project.version}-${params.PREJECT_ENVIRONMENT} 
            """
        }
    }
    }
    // .......此处省略部分代码...........
  }
}
weiyigeek.top-Pipeline Script图
weiyigeek.top-Pipeline Script图

9.最后,点击保存,回到流水线列表界面,点击 Build with Parameters 按钮,选择合适的参数进行构建,测试效果如下:

weiyigeek.top-流水线构建图
weiyigeek.top-流水线构建图

若文章写得不错,不要吝惜手中转发,点赞、在看,若有疑问的小伙伴,可在评论区留言你想法哟💬!

欢迎各位看友加入到『 全栈工程师修炼指南』知识星球中,学习更多技能!

温馨提示:作者最近10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者账号[WeiyiGeeker],当前价格¥199,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

 学习推荐 往期文章

如果此篇文章对你有帮助,请你将它转发给更多的人! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈工程师修炼指南

原创不易,赞赏鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值