当前位置: 首页 > news >正文

Jenkins运维之路(Slave容器节点)

在前面的文章里面介绍了自由风格和Pipeline方式(裸机部署,docker项目部署)的部署过程,也做出来了模板,目前遇到一个问题就是公司给的运维服务器有限,在构建的过程中发现项目并没有语言版本完全统一,比如有jdk17、jdk21、jdk8,node16,node18等等,这种多语言环境的情况如果混合部署在一台打包服务器上会出现各种奇奇怪怪的问题,所以这次采取了直接使用jenkins的容器节点来进行项目构建,这样能够减少多语言项目构建的冲突。

1. 构建打包容器

这里仅以一个版本作为示例,其他的语言版本可以按这个思路来进行构建,另外请注意 maven node go在打包前一定先把私服地址什么的设置好要不直接到国外服务器下载慢的一批

1.1 构建目录结构

# 目录结构(这套是带包的JDK21 Maven3.9 node-v22 sonar-5 docker-28.4)
root@ubuntu2204test99:~/cicd_images/cicd_jdk21_go_mvn3911_node22_sonarq5_docker284# tree -L 1
.
├── docker
├── Dockerfile
├── go
├── jdk-21.0.88cccaac7ca7e
├── maven-3.9.11
├── node-v22.19.0
└── sonar-scanner-5.0.2.4997

1.2 Dockerfile内容

# Dockerfile构建内容 使用的公司私服FROM harbor.xxxxxx.com/devops/cicd-ubuntu2404-base:v1# 安装 Git 并清理缓存
RUN apt-get update && \apt-get install -y git && \apt-get clean && \rm -rf /var/lib/apt/lists/*COPY go /usr/local/go
COPY jdk-21.0.8 /usr/local/jdk-21.0.8
COPY maven-3.9.11 /usr/local/maven-3.9.11
COPY node-v22.19.0 /usr/local/node-v22.19.0
COPY sonar-scanner-5.0.2.4997 /usr/local/sonar-scanner-5.0.2.4997
COPY docker/docker /usr/bin/docker# 设置环境变量
ENV JAVA_HOME=/usr/local/jdk-21.0.8
ENV SCANNER_HOME=/usr/local/sonar-scanner-5.0.2.4997
ENV MAVEN_HOME=/usr/local/maven-3.9.11
ENV NODE_HOME=/usr/local/node-v22.19.0
ENV GO_HOME=/usr/local/go# 更新 PATH 和 CLASSPATH
ENV PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin:$NODE_HOME/bin:$GO_HOME/bin:$SCANNER_HOME/bin
ENV CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV NODE_OPTIONS=--max_old_space_size=2048# 设置 Go 和 npm 的国内源
RUN go env -w GO111MODULE=on && \go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct && \npm config set registry http://mirrors.cloud.tencent.com/npm/# 使用 bash 启动容器
CMD ["/bin/bash"]

1.3 打包并推送至私服

root@ubuntu2204test99:~# docker build -t harbor.muscledog.top/devops/cicd-ubuntu2404-jdk21:v1 .
root@ubuntu2204test99:~# docker push harbor.muscledog.top/devops/cicd-ubuntu2404-jdk21:v1

2. 容器节点流水线

2.1 项目Dockerfile内容

# 项目的dockerfile名称MutilBuildDockerfile,使用多段构建FROM harbor.xxx.com/devops/cicd-ubuntu2404-jdk21:v1 AS builder
WORKDIR /build
COPY . .
RUN mvn clean package -DskipTestsFROM harbor.xxx.top/devops/ubuntu/jre:21-24.04
WORKDIR /app
COPY --from=builder /build/target/spring-boot-3-hello-world-1.0.0-SNAPSHOT.jar /app/spring-boot-3-hello-world-1.0.0-SNAPSHOT.jar
ENTRYPOINT ["java", "-jar", "spring-boot-3-hello-world-1.0.0-SNAPSHOT.jar"]

这里我将容器相关构建的代码片段输出到这里,全部的代码片段太多了而且和前面文章有些是一样的 没必要!

def createVersion() {// 创建了一个方法createVersion()// 定义一个时间戳+构建ID作为版本号,为tag使用return new Date().format('yyyyMMddHHmmss') + "_${env.BUILD_ID}"
}pipeline {agent { node { label 'node47' } }environment {GIT_REPO_URL = 'git@xxxx.com:xxxx/spring-boot-3-hello-world-jdk21.git'RED = "\u001B[31m"GREEN = "\u001B[32m"   // 绿色PURPLE = "\u001B[35m"  // 紫色RESET = "\u001B[0m"    // 重置REGISTRY_URL = 'harbor.xxxx.com'HARBOR_URL = 'https://harbor.xxxx.com'PROJECT_GROUP = 'devops'PROJECT_NAME = 'hello-world-jdk21'CONTAINER_NAME = 'hello-world-jdk21'OPS_SHARE_LIBRARY = 'git@xxxx.com:xxxx/ops-share-librarya.git'}options {timeout(time: 10, unit: 'MINUTES')disableConcurrentBuilds()timestamps()}parameters {gitParameter(name: 'BRANCH_TAG',type: 'PT_BRANCH_TAG',defaultValue: 'master',description: '请选择你要部署的分支或Tag',useRepository: 'git@xxxx.com:xxxx/spring-boot-3-hello-world-jdk21.git',quickFilterEnabled: true)booleanParam(defaultValue: false, description: '是否进行项目回滚?', name: 'ROLLBACK_TAG')}stages {stage('Check requirement') {steps {script {if (params.ROLLBACK_TAG && env.BRANCH_TAG.startsWith('rel-')) {echo "条件满足:ROLLBACK_TAG 为真,且 BRANCH_TAG 以 rel- 开头,继续执行后续步骤。"} else if (!params.ROLLBACK_TAG && !env.BRANCH_TAG.startsWith('rel-')) {echo "条件满足:ROLLBACK_TAG 为假,且BRANCH_TAG 不以 rel- 开头,继续执行后续步骤。"} else {echo "条件不满足,终止整个流程。"error("构建中止:条件不满足。")}}}}stage('Show info') {steps {script {if (params.ROLLBACK_TAG) {wrap([$class: 'BuildUser']) {echo "Built by: ${env.BUILD_USER_ID}"currentBuild.description = "Built by: ${env.BUILD_USER_ID}, Rollback: ${env.BRANCH_TAG}, tag: '项目回滚'}"}} else {_tag = createVersion()wrap([$class: 'BuildUser']) {echo "Built by: ${env.BUILD_USER_ID}"currentBuild.description = "Built by: ${env.BUILD_USER_ID}, Branch: ${env.BRANCH_TAG}, tag: ${_tag}"}}}}}stage('CleanWorkDir') {steps {cleanWs()}}//这里开始使用容器进行构建stage('SlaveDocker49Node'comsteps {node('node47') {script {//这里使用先前构建好的打包容器进行项目构建,将构建的缓存映射到服务器目录当中加快以后构建的速度,同时将主机的docker进程映射到docker容器中docker.image('harbor.xxx.com/devops/cicd-ubuntu2404-jdk21:v1').inside('-v /root/Cache/m2/:/usr/local/maven-3.9.11/m2 -v /var/run/docker.sock:/var/run/docker.sock -v /root/.ssh:/root/.ssh') {stage('Checkout') {script {checkout([$class: 'GitSCM', branches: [[name: params.BRANCH_TAG]], userRemoteConfigs: [[url: "${GIT_REPO_URL}", credentialsId: "GiteeKey"]]])}}stage('Build') {script {if (!params.BRANCH_TAG.startsWith('rel-')) {ansiColor('xterm') {echo "${GREEN}项目开始构建${RESET}"echo "Building"}// 构建项目镜像,指定Dockerfile名称def image = docker.build("${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${_tag}", "-f MutilBuildDockerfile .")// 构建成功后推送到私服当中docker.withRegistry("${HARBOR_URL}", "Harbor") {image.push()}// 删除已经推送完毕的镜像sh "docker rmi ${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${_tag}"if (params.BRANCH_TAG.startsWith("rel-")) {echo "选择的是标签,不打标签。"} else if (params.BRANCH_TAG == "master" || params.BRANCH_TAG == "origin/master") {sh "git tag rel-${_tag}"sh "git push origin rel-${_tag}"echo "为分支 ${params.BRANCH_TAG} 打标签 rel-${_tag}。"} else {echo "当前分支 ${params.BRANCH_TAG} 不是 master,不打标签。"}} else {echo "跳过构建:BRANCH_TAG 以 'rel-' 开头。"}}}}}}}}// 同样Ansible部署也使用了容器来对项目进行推送部署stage('SlaveAnsible49Node') {steps {node('node47') {script {// 这里使用私服中的ansible镜像进行部署,将密钥挂载到容器当中docker.image('harbor.xxx.com/devops/ansible:2.18.6').inside('-v /root/.ssh:/root/.ssh') {stage('Checkout') {script {checkout([$class: 'GitSCM', branches: [[name: '*/master']], userRemoteConfigs: [[url: "${OPS_SHARE_LIBRARY}", credentialsId: "GiteeKey"]]])}}stage('Ansible in Slave1') {script {// 定义私服和ansible的相关变量信息withCredentials([usernamePassword(credentialsId: 'Harbor', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD')]) {def extraVars = [container_name: "${CONTAINER_NAME}",docker_registry: "${HARBOR_URL}",docker_username: "${HARBOR_USERNAME}",docker_password: "${HARBOR_PASSWORD}"].collectEntries { [(it.key): it.value] }if (!env.BRANCH_TAG.startsWith('rel-')) {extraVars['new_image_name'] = "${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${_tag}"ansiblePlaybook(playbook: "./Ansible/HelloWorld-Pipeline/deploy.yml",inventory: "./Ansible/HelloWorld-Pipeline/hosts",extraVars: extraVars)} else {def branchTag = env.BRANCH_TAGdef extractedValue = branchTag.replaceFirst(/^rel-/, '')println "Extracted Value: ${extractedValue}"extraVars['rel_image_name'] = "${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${extractedValue}"ansiblePlaybook(playbook: "./Ansible/HelloWorld-Pipeline/rollback.yml",inventory: "./Ansible/HelloWorld-Pipeline/hosts",extraVars: extraVars)}}}}}}}}}} // 结束 stagespost {always {script {println("流水线结束后,经常做的事情")}}success {script {def notificationText = []notificationText.add("- 成功构建部署: ${JOB_NAME}项目!\n")notificationText.add("- 版本: ${BRANCH_TAG}\n")notificationText.add("- 持续时间: ${currentBuild.durationString}\n")notificationText.add("- 任务: #${JOB_NAME}")if (params.BRANCH_TAG == "master" || params.BRANCH_TAG == "origin/master") {notificationText.add("- 标签: rel-${_tag}\n")}dingtalk (robot: 'BuildBoy',type: 'MARKDOWN',title: "success: ${JOB_NAME}",text: notificationText)}}failure {dingtalk (robot: 'BuildBoy',type: 'MARKDOWN',title: "fail: ${JOB_NAME}",text: ["- 失败构建部署:${JOB_NAME}项目!\n- 版本:${BRANCH_TAG}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"])}aborted {dingtalk (robot: 'BuildBoy',type: 'MARKDOWN',title: "aborted: ${JOB_NAME}",text: ["- 流水线被取消:${JOB_NAME}项目!\n- 版本:${BRANCH_TAG}\n- 持续时间:${currentBuild.durationString}\n- 任务:${JOB_NAME}"])}}
}

3.测试流水线

image-20250919143911634

image-20250919143911634

这样整个构建的过程就再容器当中进行,而不会影响到你现在的系统环境,感觉这种方式还比较nice,如果文章帮助到了您记得来个赞哦!

http://www.dtcms.com/a/391153.html

相关文章:

  • Amazon Lambda + API Gateway 实战,无服务器架构入门
  • 芯片管脚的源电流与漏电流
  • Django+ARIMA微博舆情预警系统 SnowNLP情感分析 Echarts可视化 机器学习 大数据项目✅
  • SIMetrix 8.30仿真蓝牙天线上的无源滤波器
  • [x-cmd] 升级 x-cmd 指南
  • AXI4-Stream总线流控握手实战经验总结
  • RAWSim-O-main项目Trae解析
  • react固定容器标签超出n+展示
  • ​​HarmonyOS应用开发:从入门到实战的完整指南​
  • QT与GTK生态最新进展及特性对比(2025年)
  • 包管理器分析
  • XC7K325T-2FBG676I Xilinx AMD Kintex-7 FPGA
  • FPGA入门-红外遥控
  • qml实现多页面切换显示的导航栏
  • 20250919的学习笔记
  • iOS 26 游戏测试实战,兼容性、帧率、GPU 性能与 Liquid Glass 动效在游戏中的影响(游戏开发与 uni-app 场景指南)
  • kind部署K8S集群并将“修仙业务“部署到kind集群
  • 《C++程序设计》笔记p2
  • 第1章:项目前言
  • 实现类似word 文档下划线输入功能
  • AR技术赋能高风险作业:重塑安全与效率
  • Axure-图片旋转人机验证
  • web:ts中class、interface、type的区别
  • css中的vm和vh,页面滚动的卡片网页
  • git cherry pick怎么用
  • 40岁从智驾转具身智能之路
  • 串口连接失败排坑步骤
  • QML学习笔记(三)QML基础语法其一:Rectangle及帮助文档的使用
  • 工业相机与镜头靶面尺寸的关系:从原理到选型的避坑指南
  • javaweb tomcat的使用