[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ]
📢 大家好,我是 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 的通信方式
SSH:通过 SSH 协议连接 Linux/Unix 节点,本文将使用Docker容器方式部署,传统物理机、虚拟机方式请参考:https://mp.weixin.qq.com/s/0w20D2Gs8JRK6Kb7lnTAhg
JNLP(Java Web Start):Agent 主动连接 Master(适用于防火墙限制场景),实践文章:https://mp.weixin.qq.com/s/0w20D2Gs8JRK6Kb7lnTAhg
Kubernetes:动态创建 Pod 作为临时 Agent(需安装 Kubernetes 插件),实践文章:https://mp.weixin.qq.com/s/EI0qd0YsyRyIbkS4msWAVQ
实践之路
环境介绍
# 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 私钥,粘贴刚才生成的私钥内容,配置如下图所示。

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,输入节点名称自定义即可,选择类型为固定节点
,然后按照如下图进行配置,特别注意红色圈中选项即可,需更加你实际配置进行填写,特别是设置标签以空格进行分割。

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

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

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"]

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}
"""
}
}
}
// .......此处省略部分代码...........
}
}

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

若文章写得不错,不要吝惜手中转发,点赞、在看,若有疑问的小伙伴,可在评论区留言你想法哟💬!
欢迎各位看友加入到『 全栈工程师修炼指南』知识星球中,学习更多技能!
温馨提示:作者最近10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者账号[WeiyiGeeker],当前价格¥199,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!
学习推荐 往期文章
💡【相关】GitOps实践 | 企业生产环境Jenkins流水线分享,从Gitlab到镜像构建到部署测试以及企业微信消息通知
💡【相关】持续集成案例之使用Docker运行自构建Jenkins的Agent镜像固定工作节点实践(分享企业项目流水线代码)
如果此篇文章对你有帮助,请你将它转发给更多的人!