• 正文
    • 前置知識
    • 實踐之路
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

解鎖 Jenkins Agent 技巧,容器化輕松實現(xiàn)分布式構(gòu)建節(jié)點擴(kuò)展

10小時前
218
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是 WeiyiGeek,一個正在向全棧工程師(SecDevOps)前進(jìn)的計算機(jī)技術(shù)愛好者,歡迎各位道友一起學(xué)習(xí)交流、一起進(jìn)步 ,若此文有幫助請點個關(guān)注,后續(xù)追番不迷路 ??。

解鎖 Jenkins Agent 技巧,容器化輕松實現(xiàn)分布式構(gòu)建節(jié)點擴(kuò)展

描述:Jenkins 是一個開源的自動化構(gòu)建工具,廣泛用于持續(xù)集成和持續(xù)部署(CI/CD),它通過插件支持多種編程語言和構(gòu)建工具,所以其功能非常的強(qiáng)大。為了處理大規(guī)模的項目或高并發(fā)場景,Jenkins 支持分布式構(gòu)建架構(gòu),允許將任務(wù)分散到多個節(jié)點上執(zhí)行。Jenkins Agent 是 Jenkins 實現(xiàn)分布式構(gòu)建的關(guān)鍵組件,通過將任務(wù)分發(fā)到不同節(jié)點,顯著提升了 CI/CD 的效率和靈活性。合理使用固定 Agent 和動態(tài) Agent,可以平衡穩(wěn)定性和成本。此處 Jenkins 介紹安裝就不在累述,不了解的童鞋 推薦參考作者以前發(fā)布的【Jenkins】專欄。

本文將介紹 Jenkins Agent 使用技巧,作者從實踐視角出發(fā),通過使用 Docker 搭建部署 Jenkins Agent 容器環(huán)境,并通過 SSH 方式快速接入,從而輕松實現(xiàn)分布式構(gòu)建節(jié)點,最后使用加入的Agent節(jié)點,用于構(gòu)建作者以 Golang 開發(fā)的運維Api接口項目。

前置知識

什么是 Jenkins Agent?

Jenkins Agent(也稱為 Jenkins 節(jié)點或從節(jié)點)是 Jenkins 分布式構(gòu)建架構(gòu)中的工作節(jié)點,用于執(zhí)行主節(jié)點(Master)分發(fā)的任務(wù)(如代碼構(gòu)建、測試、部署等)。它允許將工作負(fù)載分散到多臺機(jī)器上,提高并發(fā)處理能力和資源利用率。

什么是主節(jié)點(Master)、代理節(jié)點(Agent)

描述:在 Jenkins 中,主節(jié)點(Master,實際上就是部署的 jekins 服務(wù)那臺主機(jī)就是主節(jié)點)是控制中心,負(fù)責(zé)管理任務(wù)、插件配置和用戶界面,通常不直接執(zhí)行耗時任務(wù)。代理節(jié)點(Agent)也稱工作節(jié)點,它是執(zhí)行實際構(gòu)建任務(wù)的機(jī)器,可以是固定節(jié)點也可是動態(tài)擴(kuò)展,支持不同操作系統(tǒng)和環(huán)境,可以是物理機(jī)、虛擬機(jī)、容器或 Kubernetes Pod。

Jenkins Agent 的類型

    固定 Agent:常駐節(jié)點,手動配置(如專用服務(wù)器、虛擬服務(wù)器、容器)。動態(tài) Agent:按需創(chuàng)建(如通過 Kubernetes/Docker 臨時啟動,任務(wù)完成后銷毀)。云 Agent:集成云平臺(AWS、Azure 等)自動擴(kuò)縮容。

Jenkins Agent 核心功能

    任務(wù)執(zhí)行: 運行 Jenkins 分發(fā)的構(gòu)建任務(wù)(如編譯代碼、運行測試)。環(huán)境隔離: 不同 Agent 可以配置不同的工具鏈(JDK、Node.js 等),適配多項目需求。負(fù)載均衡: 多個 Agent 并行處理任務(wù),加快流水線速度。資源擴(kuò)展: 動態(tài) Agent 在任務(wù)高峰期自動擴(kuò)容,空閑時釋放資源。

Jenkins Agent 的通信方式

    SSH:通過 SSH 協(xié)議連接 Linux/Unix 節(jié)點,本文將使用Docker容器方式部署,傳統(tǒng)物理機(jī)、虛擬機(jī)方式請參考:https://mp.weixin.qq.com/s/0w20D2Gs8JRK6Kb7lnTAhgJNLP(Java Web Start):Agent 主動連接 Master(適用于防火墻限制場景),實踐文章:https://mp.weixin.qq.com/s/0w20D2Gs8JRK6Kb7lnTAhgKubernetes:動態(tài)創(chuàng)建 Pod 作為臨時 Agent(需安裝 Kubernetes 插件),實踐文章:https://mp.weixin.qq.com/s/EI0qd0YsyRyIbkS4msWAVQ

實踐之路

環(huán)境介紹

# 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.首先,進(jìn)入到 Jenkins Server 容器中,并且在 Jenkins 主節(jié)點上生成 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

# 分別打開兩個文件,復(fù)制公鑰、密鑰內(nèi)容
cat ~/.ssh/id_rsa ?-> Jenkins 憑據(jù)
cat ~/.ssh/id_rsa.pub -> Jenkins Agent 鏡像所需瓶頸

2.然后,在 Jenkins 網(wǎng)頁界面上,進(jìn)入系統(tǒng)管理,選擇 Credentials 管理,添加新的憑據(jù),類型選擇 SSH Username With Private Key 私鑰,粘貼剛才生成的私鑰內(nèi)容,配置如下圖所示。

weiyigeek.top-Jenkins中添加SSH憑據(jù)圖

3.其次,在安裝有 Docker 環(huán)境的的主機(jī)上,拉取并運行 Jenkins SSH Agent 鏡像,直接一梭子搞定

# 創(chuàng)建jenkins-ssh-agent目錄,用于持久化數(shù)據(jù)存儲
mkdir -p /data/jenkins
chown -R 1000:1000 /data/jenkins

# 拉取 jenkins-ssh-agent 鏡像,由于 Jenkins 2.387.3 中 agent.jar 運行環(huán)境最低需要 JDK 11+,所以這里選擇 jdk11 版本鏡像
docker pull jenkins/ssh-agent:jdk11

# 運行 jenkins-ssh-agent 容器,映射端口2222到宿主機(jī),掛載密鑰文件和 Docker socket,此處重點是JENKINS_AGENT_SSH_PUBKEY填寫前面在控制節(jié)點生成的公鑰。
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

# 特別注意:此處有可能在構(gòu)建上傳鏡像時出現(xiàn)權(quán)限不足的問題,這是由于我們在容器中直接使用了宿主機(jī)上的 /var/run/docker.sock ,而容器中使用的是 jenkins 用戶所以導(dǎo)致權(quán)限不足,解決辦法如下,宿主機(jī)中運行
$ chmod +666 /var/run/docker.sock

4.然后,在 jenkins 上添加 slave,返回到 Jenkins 網(wǎng)頁界面上,進(jìn)入系統(tǒng)管理,選擇節(jié)點管理選項卡,點擊 New Node 選擇 SSH 方式添加 Agent,輸入節(jié)點名稱自定義即可,選擇類型為固定節(jié)點,然后按照如下圖進(jìn)行配置,特別注意紅色圈中選項即可,需更加你實際配置進(jìn)行填寫,特別是設(shè)置標(biāo)簽以空格進(jìn)行分割。

weiyigeek.top-Jenkins添加agent工作節(jié)點圖

5.最后,點擊保存,Jenkins 會自動通過 SSH 連接到 Jenkins Agent 容器并自動拉取 remoting.jar 文件,并且其容器中運行,可通過節(jié)點日志查看連接過程,當(dāng)日志中出現(xiàn) Agent successfully connected and online 則表示連接成功。

weiyigeek.top-查看節(jié)點連接日志圖

6.至此,Jenkins Agent 已經(jīng)成功添加到 Jenkins 主節(jié)點中,接下來就可以在 Jenkins 項目中使用該 Agent 進(jìn)行 golang 項目構(gòu)建工作了。

weiyigeek.top-節(jié)點列舉圖

7.構(gòu)建 golang 項目之前,需要先準(zhǔn)備 Dockerfile 我已經(jīng)放在項目中,并上傳到私有的Gitlab倉庫中,若還沒搭建過Gitlab代碼倉庫的同學(xué)可參考作者此篇【GitOps實踐 | 快速在銀河麒麟KylinOS國產(chǎn)系統(tǒng)部署最新Gitlab-CE企業(yè)私有代碼倉庫】文章。

# Dockerfile 內(nèi)容如下,采用多階段構(gòu)建,先編譯項目生成二進(jìn)制文件,再拷貝到 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運維接口構(gòu)建文件圖

8.接下來,在 Jenkins 項目中配置構(gòu)建流水線,名稱為?itops-api?,采用流水線方式,點擊配置流水線,選擇Pipeline script方式,如下所示。

// 作者抽取了部分關(guān)鍵代碼片段

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

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

// 任務(wù)名稱、任務(wù)工作空間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 {
? ??// 通過標(biāo)簽選擇構(gòu)建節(jié)點,此處選擇就是就是 jenkins-ssh-agent節(jié)點
? ? label?'ssh-agent'
? }

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

// 自定義環(huán)境變量, 通過 env.變量名訪問?
? environment {
? ??// 代碼倉庫地址與認(rèn)證地址
? ? GITLAB_URL =?"${PRJECT}"
? ? GITLAB_PUB =?"${PUBKEY}"
? }

// 自定義選擇參數(shù),在 sh 中可通過變量名訪問,而在 script pipeline 腳本中通過 params.參數(shù)名稱 訪問?
? parameters {
? ? gitParameter?branch:'',?branchFilter:'origin/(.*)',?defaultValue:'origin/master',?description:'查看構(gòu)建部署可用的Tag或Branch名稱?',?name:'TagBranchName',?quickFilterEnabled:false,?selectedValue:'NONE',?sortMode:'DESCENDING_SMART',?tagFilter:'*',?type:'GitParameterDefinition'
? ? string(name:'RELEASE_VERSION',?defaultValue:"master",?description:'Message: 請選擇構(gòu)建部署的Tag或Branch名稱?',?trim:'True')
? ? choice(name:'PREJECT_ENVIRONMENT',?choices:?['Test','Prod'],?description:'Message: 選擇項目部署環(huán)境?')
? choice(name:'PREJECT_OPERATION',?choices:?['None',?'deploy',?'rollback',?'redeploy','deployTest'],?description:'Message: 選擇項目操作方式?')
? ? choice(name:'IS_IMAGEBUILD',?choices:?['True','False'],?description:'Message: 是否進(jìn)行鏡像構(gòu)建操作?')
? ? choice(name:'IS_RELEASE',?choices:?['False','True'],?description:'Message: 是否進(jìn)行編譯成品發(fā)布?')
? }

// 主要階段以及子階段流程
? stages {
? ??// [ 階段1.項目代碼拉取 ]
? ? stage ('代碼拉取') {
? ? ? steps {
? ? ? ??// 1.構(gòu)建信息輸出
? ? ? ? echo?"任務(wù)名稱: ${JOB_NAME}, 項目地址: ${env.GITLAB_URL}, 構(gòu)建版本: ${params.RELEASE_VERSION}, 部署環(huán)境: ${params.PREJECT_ENVIRONMENT} n 構(gòu)建操作: ${params.PREJECT_OPERATION}, 鏡像構(gòu)建: ${params.IS_IMAGEBUILD} , 成品發(fā)布: ${params.IS_RELEASE},"
? ? ?
? ? ? ??// 2.超時時間設(shè)置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函數(shù)獲取JAVA項目真實的構(gòu)建空間路徑以及獲取pom.xml項目文件信息
? ? ? ? ? ? project = GET_REAL_PROJECT()

? ? ? ? ? ??// 7.驗證部署環(huán)境進(jìn)行預(yù)設(shè)環(huán)境參數(shù)值
? ? ? ? ? ??if?(?"${params.PREJECT_ENVIRONMENT}"?==?"Prod"?) {
? ? ? ? ? ? ? config = ENV_PROD()
? ? ? ? ? ? ? print?"${params.PREJECT_ENVIRONMENT}"
? ? ? ? ? ? }?else?{
? ? ? ? ? ? ? config = ENV_TEST()
? ? ? ? ? ? ? print?"${params.PREJECT_ENVIRONMENT}"
? ? ? ? ? ? }

? ? ? ? ? ??// 8.通過企業(yè)微信進(jìn)行構(gòu)建通知
? ? ? ? ? ? echo ?"項目真實路徑: ${project.path} n項目信息: ${project.name} ?${project.version}nCommit信息:${project.commitmsg} n鏡像倉庫與名稱:${config.HARBOR_URL}/${project.imagename}"
? ? ? ? ? ??// .......此處省略部分代碼...........
? ? ? ? ? }
? ? ? ? }
? ? ?}
? ?}
? ? stage ("項目構(gòu)建") {
? ? ? ? steps {
? ? ? ? script {
? ? ? ? ? ??//倉庫認(rèn)證
? ? ? ? ? ? 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圖

9.最后,點擊保存,回到流水線列表界面,點擊 Build with Parameters 按鈕,選擇合適的參數(shù)進(jìn)行構(gòu)建,測試效果如下:

weiyigeek.top-流水線構(gòu)建圖

溫馨提示:作者最近10年的工作學(xué)習(xí)筆記(涉及網(wǎng)絡(luò)、安全、運維、開發(fā)),需要學(xué)習(xí)實踐筆記的看友,可添加作者賬號[WeiyiGeeker],當(dāng)前價格¥199,除了獲得從業(yè)筆記的同時還可進(jìn)行問題答疑以及每月遠(yuǎn)程技術(shù)支持,希望大家多多支持,收獲定大于付出!

如果此篇文章對你有幫助,請你將它轉(zhuǎn)發(fā)給更多的人!?

相關(guān)推薦