news 2026/4/24 23:18:05

使用Jenkins 流水线同时构建前后端镜像并推送到Harbor仓库 一文带你搞定 如何实现流水线打包镜像

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用Jenkins 流水线同时构建前后端镜像并推送到Harbor仓库 一文带你搞定 如何实现流水线打包镜像
构建前后端镜像

由于jenkins服务是使用jenkins普通用户运行的,所以在调用docker命令的时候是没有权限的,所以需要提前将jenkins用户加入到docker的组里面.在 Jenkins 所在的服务器上执行以下命令:

把 jenkins 用户加入docker# sudo usermod -aG docker jenkins刷新用户组# newgrp docker

验证:重启 Jenkins 后,在 Jenkins 服务器上用 jenkins 用户身份测试:

运行

# su - jenkins# docker ps

如果能正常输出容器列表,说明权限已经生效。

部署harbor镜像仓库

172.20.10.3 安装docker和docker-compose 安装docker略 [root@localhost ~]# curl -SL https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose [root@localhost ~]# chmod +x /usr/local/bin/docker-compose [root@localhost ~]# docker-compose -v Docker Compose version v2.24.6 [root@localhost ~]# wget https://github.com/goharbor/harbor/releases/download/v2.15.0/harbor-offline-installer-v2.15.0.tgz [root@localhost ~]# tar xf harbor-offline-installer-v2.15.0.tar [root@localhost ~]# cd harbor [root@localhost harbor]# ls common.sh harbor.v2.15.0.tar.gz harbor.yml.tmpl install.sh LICENSE prepare [root@localhost harbor]# cp harbor.yml.tmpl harbor.yml #生成证书,生产环境中可购买证书使用 #生成 CA 根证书(自签) [root@localhost harbor]# mkdir -p /data/harbor/certs [root@localhost harbor]# cd /data/harbor/certs [root@localhost certs]# openssl genrsa -out ca.key 4096 Generating RSA private key, 4096 bit long modulus ..++ ......................++ e is 65537 (0x10001) [root@localhost certs]# openssl req -x509 -new -nodes -sha512 -days 3650 -subj "/C=CN/ST=Beijing/L=Beijing/O=Harbor/OU=IT/CN=172.20.10.3" -key ca.key -out ca.crt #生成服务器证书(IP) [root@localhost certs]# openssl genrsa -out harbor.key 4096 Generating RSA private key, 4096 bit long modulus ..........++ ........................................................................................................................................................................................................................++ e is 65537 (0x10001) [root@localhost certs]# openssl req -sha512 -new -subj "/C=CN/ST=Beijing/L=Beijing/O=Harbor/OU=IT/CN=172.20.10.3" -key harbor.key -out harbor.csr [root@localhost certs]# cat > v3.ext <<-EOF > authorityKeyIdentifier=keyid,issuer > basicConstraints=CA:FALSE > keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment > extendedKeyUsage = serverAuth > subjectAltName = IP:172.20.10.3 > EOF [root@localhost certs]# openssl x509 -req -sha512 -days 3650 \ > -extfile v3.ext \ > -CA ca.crt -CAkey ca.key -CAcreateserial \ > -in harbor.csr \ > -out harbor.crt Signature ok subject=/C=CN/ST=Beijing/L=Beijing/O=Harbor/OU=IT/CN=172.20.10.3 Getting CA Private Key [root@localhost certs]# ls ca.crt ca.key ca.srl harbor.crt harbor.csr harbor.key v3.ext [root@localhost harbor]# vim harbor.yml

[root@localhost harbor]# ./install.sh[root@localhost harbor]# docker-compose up -d[root@localhost harbor]# netstat -lntp | egrep ':80 | :443'tcp000.0.0.0:800.0.0.0:* LISTEN75137/docker-proxy tcp600:::80 :::* LISTEN75141/docker-proxy

客户端配置(Docker 信任自签证书)

  1. 服务端(Harbor 本机)
[root@localhost ~]# mkdir -p /etc/docker/certs.d/172.20.10.3 [root@localhost ~]# cp /data/harbor/certs/ca.crt /etc/docker/certs.d/172.20.10.3/ [root@localhost ~]# ls /etc/docker/certs.d/172.20.10.3/ ca.crt [root@localhost ~]# systemctl restart docker

2.其他 Docker 客户端

[root@jenkins-slave ~]# mkdir -p /etc/docker/certs.d/172.20.10.3[root@jenkins-slave ~]# scp root@172.20.10.3:/data/harbor/certs/ca.crt /etc/docker/certs.d/172.20.10.3[root@jenkins-slave ~]# systemctl restart docker#测试[root@jenkins-slave ~]# docker login https://172.20.10.3Username: admin Password: WARNING!Your password will be stored unencryptedin/root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded

在jenkins的主从节点配置以不安全的方式访问harbor仓库

[root@jenkins ~]# cat /etc/docker/daemon.json{"registry-mirrors":["https://08c765900e00f5d20f0dc0005a40c3a0.mirror.swr.myhuaweicloud.com"],"insecure-registries":["172.20.10.3"]}[root@jenkins ~]# systemctl daemon-reload[root@jenkins ~]# systemctl restart docker[root@jenkins-slave ~]# cat /etc/docker/daemon.json{"registry-mirrors":["https://08c765900e00f5d20f0dc0005a40c3a0.mirror.swr.myhuaweicloud.com"],"insecure-registries":["172.20.10.3"]}[root@jenkins-slave ~]# systemctl daemon-reload[root@jenkins-slave ~]# systemctl restart docker


在harbor仓库创建对应的项目仓库

在jenkins上面创建登录harbor仓库的凭据

选择用户名和密码类型的凭据



新建任务


编写流水线

pipeline { agent none options { timestamps() timeout(time: 1, unit: 'HOURS') buildDiscarder(logRotator(numToKeepStr: '10')) } environment { // ========== GIT 配置 ========== FRONTEND_GIT_URL = 'git@gitee.com:testpm/frontend-demo.git' BACKEND_GIT_URL = 'git@gitee.com:testpm/backend-demo.git' SLAVE_GIT_ID = 'jenkins-slave-git' // ========== 镜像仓库配置 ========== REGISTRY = '172.20.10.3' BACK_NAMESPACE = 'backend' BACK_APP_NAME = 'my-backend' FRONT_NAMESPACE = 'frontend' FRONT_APP_NAME = 'my-frontend' // ========== 镜像仓库凭证 ========== DOCKER_CRED = credentials('harbor-secret') } stages { // ========================== // 一、构建后端服务 // ========================== stage('🚀 构建后端镜像') { agent { node { label 'jenkins-slave' customWorkspace '/home/jenkins/backend-docker' } } steps { echo '============ 拉取后端代码 ============' git credentialsId: "${SLAVE_GIT_ID}", url: "${BACKEND_GIT_URL}", branch: 'master' echo '============ 计算镜像版本号 ============' // 关键修复:在 git 命令执行后,立即在当前目录计算 COMMIT_HASH script { // 这里会在 /home/jenkins/backend-docker 目录下执行,因为有 git 拉取,所以一定有 .git env.COMMIT_HASH = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() env.BACK_FULL_IMAGE = "${REGISTRY}/${BACK_NAMESPACE}/${BACK_APP_NAME}:${COMMIT_HASH}" } echo "镜像地址:${BACK_FULL_IMAGE}" sh 'pwd && ls -la | grep .git' // 验证目录和git存在 echo '============ 构建并推送后端镜像 ============' sh ''' set -eux echo "${DOCKER_CRED_PSW}" | docker login -u "${DOCKER_CRED_USR}" --password-stdin "${REGISTRY}" docker build -t $BACK_FULL_IMAGE . docker push $BACK_FULL_IMAGE docker logout $REGISTRY ''' } } // ========================== // 二、构建前端服务 // ========================== stage('🚀 构建前端镜像') { agent { node { label 'jenkins-slave' customWorkspace '/home/jenkins/frontend-docker' } } steps { echo '============ 拉取前端代码 ============' git credentialsId: "${SLAVE_GIT_ID}", url: "${FRONTEND_GIT_URL}", branch: 'master' echo '============ 计算镜像版本号 ============' script { // 前端目录计算版本号 env.COMMIT_HASH = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() env.FRONT_FULL_IMAGE = "${REGISTRY}/${FRONT_NAMESPACE}/${FRONT_APP_NAME}:${COMMIT_HASH}" } echo "镜像地址:${FRONT_FULL_IMAGE}" sh 'pwd && ls -la | grep .git' echo '============ 构建并推送前端镜像 ============' sh ''' set -eux docker login -u $DOCKER_CRED_USR -p $DOCKER_CRED_PSW $REGISTRY docker build -t $FRONT_FULL_IMAGE . docker push $FRONT_FULL_IMAGE docker logout $REGISTRY ''' } } } }

前后端的dockerfile

[root@jenkins-slave ~]# cat backend-demo/Dockerfile# 第一阶段:构建FROM maven:3.9.9-eclipse-temurin-21 AS build WORKDIR /app COPY pom.xml.RUN mvn dependency:go-offline-BCOPY src ./src RUN mvn clean package-DskipTests# 第二阶段:运行FROM openjdk:21-jre-slim WORKDIR /app COPY--from=build /app/target/*.jar app.jar EXPOSE8080ENTRYPOINT["java","-jar","app.jar"][root@jenkins-slave ~]# cat frontend-demo/Dockerfile# 构建阶段FROM node:16 as build-stage RUNnpmconfigsetregistry https://registry.npmmirror.com WORKDIR /app COPY package*.json ./ RUNnpminstallCOPY..RUNnpmrun build:prod# 生产阶段FROM nginx:alpine WORKDIR /usr/share/nginx/html COPY--from=build-stage /app/dist.COPY nginx.conf /etc/nginx/nginx.conf EXPOSE80CMD["nginx","-g","daemon off;"]

主要问题:

1.sh命令必须在node块 里运行,因为只有node块才会分配一个执行节点和对应的工作目录。

💡 为什么会这样?
Jenkins Pipeline 里,所有需要操作文件系统、执行 Shell 命令的步骤(sh/bat/git等),都必须运行在node块中。
node块会帮你分配一个执行节点、创建工作目录、初始化FilePath上下文。
没有node块,sh命令就像 “飘在空气里”,不知道要在哪台机器、哪个目录下运行,就会报错。

第二个问题:

执行的命令是git rev-parse --short HEAD(用来获取当前commit的短哈希)。报错说明 Jenkins 当前所在的目录里,没有找到 .git 子目录,即这个目录不是 Git 仓库。
结合日志看:

后端阶段:Runningin/home/jenkins/backend-docker 错误发生时:Runningin/home/jenkins/workspace/pm-docker

这说明,工作目录被意外切换了,导致你的COMMIT_HASH变量在错误的目录下执行。

🛠️ 根本原因与修复方案
你使用了customWorkspace自定义工作目录,这会改变节点的工作路径。但在 Groovy 中,直接在顶层environment里执行sh命令,不会自动进入你配置的customWorkspace,它会走默认的工作目录(workspace/pm-docker),从而找不到 .git。
修复方法:将COMMIT_HASH的计算移到steps阶段,确保在正确的目录下执行。

steps { echo '============ 拉取后端代码 ============' git credentialsId: "${SLAVE_GIT_ID}", url: "${BACKEND_GIT_URL}", branch: 'master' echo '============ 计算镜像版本号 ============' // 关键修复:在 git 命令执行后,立即在当前目录计算 COMMIT_HASH script { // 这里会在 /home/jenkins/backend-docker 目录下执行,因为有 git 拉取,所以一定有 .git env.COMMIT_HASH = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() env.BACK_FULL_IMAGE = "${REGISTRY}/${BACK_NAMESPACE}/${BACK_APP_NAME}:${COMMIT_HASH}" } echo "镜像地址:${BACK_FULL_IMAGE}" sh 'pwd && ls -la | grep .git' // 验证目录和git存在

💡 修复要点说明
移除了顶层environment中的COMMIT_HASH定义:因为顶层无法预知节点目录,直接执行 sh 100% 会失败。
使用script块动态赋值:在 git 拉取代码之后,使用env.COMMIT_HASH = ...动态定义环境变量。此时sh命令会继承当前node的路径,即/home/jenkins/backend-docker,这个目录下已经有 .git 文件了,所以能成功执行。
增加了目录验证:sh 'pwd && ls -la | grep .git'可以帮你确认当前目录和 .git 文件是否存在,排错更直观。

知识点整理

Jenkins 中env.COMMIT_HASH代码逐行详细解释

这是Jenkins Pipeline (Jenkinsfile)里非常经典的一行代码,核心作用是:获取当前 Git 仓库最新提交的短哈希值,并赋值给 Jenkins 环境变量

先把整行代码拆开,从整体到局部彻底讲清楚:

env.COMMIT_HASH=sh(returnStdout: true, script:'git rev-parse --short HEAD').trim()

  1. 整体功能一句话总结

执行 Git 命令,拿到当前代码最新提交的短 commit hash(例如:a7f3d92),存入 Jenkins 环境变量COMMIT_HASH,后续流水线可以直接使用。


  1. 逐部分拆解解释

env.COMMIT_HASH

  • env:Jenkins 内置的环境变量对象,所有流水线全局都能访问
  • COMMIT_HASH:自定义的环境变量名(你可以改成GIT_COMMIT_SHORT等任意名称)
  • 作用:把后面计算出来的值,存到这个全局变量里
  • 后续使用方式:${env.COMMIT_HASH}或直接$COMMIT_HASH

sh( ... )

  • Jenkins Pipeline 内置步骤,执行 Shell 命令
  • 专门用来在 Jenkins 节点上运行 Linux/macOS 终端命令(这里就是运行 Git 命令)

returnStdout: true

  • sh步骤的关键参数
  • 默认:sh只执行命令,不返回输出结果
  • 设置为true把 Shell 命令的输出结果作为字符串返回(必须加,否则拿不到 Git 哈希)

script: 'git rev-parse --short HEAD'

真正执行的Git 核心命令,逐段解释:

  • git rev-parse:Git 内置命令,用于解析 Git 内部对象(这里解析提交记录)
  • --short关键参数→ 只返回短哈希(默认 7 位,如a7f3d92),不加会返回完整 40 位哈希
  • HEAD:指向当前分支最新的一次提交
  • 命令效果:输出当前最新 commit 的短哈希

.trim()

  • Groovy 字符串方法
  • 作用:去掉命令输出首尾的空白字符、换行符
  • 原因:Shell 命令执行完会自带一个换行符\n,不修剪会导致变量带换行,拼接路径 / 文件名时报错
  1. 常见问题与注意事项

  2. 必须在 Git 仓库目录执行这行代码运行前,必须先执行git checkout/git cloue,否则会报错not a git repository

  3. 必须加.trim()不加会导致变量带换行,拼接文件名、Docker 标签时必报错

    1. Windows 节点不兼容sh是Linux命令,windows节点需要改成```bat 或用 Git Bash

总结

  1. 这行代码 =获取 Git 最新短提交哈希 + 存入 Jenkins 全局环境变量
  2. 核心命令:git rev-parse --short HEAD→ 取短哈希
  3. 关键参数:returnStdout: true(返回结果)+.trim()(去除换行)
  4. 用途:给构建产物打标签、版本号、镜像标记、日志追踪
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 23:16:18

Linux内核视角下的NVMe SSD电源状态切换与PCIe寄存器探秘

1. NVMe SSD电源状态切换的核心逻辑 NVMe固态硬盘作为现代存储设备的核心组件&#xff0c;其电源管理机制直接关系到数据安全性和系统能效。在Linux内核视角下&#xff0c;电源状态切换绝非简单的通电断电&#xff0c;而是一套精密的硬件寄存器操作序列。我曾在一台搭载Intel …

作者头像 李华
网站建设 2026/4/24 23:13:40

ROFL播放器:免费开源英雄联盟回放分析终极指南

ROFL播放器&#xff1a;免费开源英雄联盟回放分析终极指南 【免费下载链接】ROFL-Player (No longer supported) One stop shop utility for viewing League of Legends replays! 项目地址: https://gitcode.com/gh_mirrors/ro/ROFL-Player 还在为英雄联盟回放文件无法播…

作者头像 李华
网站建设 2026/4/24 23:13:39

从“声光栅”到激光脉冲:一张图看懂声光Q开关工作原理与选型要点

声光Q开关&#xff1a;用“光栅百叶窗”原理实现激光脉冲控制的工程艺术 想象一下&#xff0c;你手中握着一支能瞬间击穿钢板的激光笔——这不是科幻电影&#xff0c;而是现代激光加工系统的日常。而让普通连续激光变身"超级脉冲"的核心部件&#xff0c;正是今天要拆…

作者头像 李华
网站建设 2026/4/24 23:12:38

5分钟掌握BsMax:让3ds Max用户无缝切换到Blender的实战指南

5分钟掌握BsMax&#xff1a;让3ds Max用户无缝切换到Blender的实战指南 【免费下载链接】BsMax BsMax Blender Addon (UI simulator/ Modeling/ Rigg & Animation/ Render Tools and ... 项目地址: https://gitcode.com/gh_mirrors/bs/BsMax 你是否曾因为从3ds Max切…

作者头像 李华
网站建设 2026/4/24 23:11:25

计算机毕业设计:PythonA股交易管理驾驶舱 Django框架 requests爬虫 数据分析 可视化 大数据 大模型(建议收藏)✅

1、项目介绍 技术栈 Python语言、Django框架、requests爬虫、Echarts可视化、tushare模块、HTML 功能模块 系统首页&#xff08;数据可视化分析&#xff09;上证指数大盘k线分析股票信息管理交易记录管理新闻资讯管理新闻资讯内容展示评论信息管理用户信息管理后台数据管理注册…

作者头像 李华