
前期知识点系列回顾:
CD部署环境准备:
需要一个Kubernetes集群,单节点,多节点,高可用集群都可以,当前演示的是基于Kubernetes v1.35.1,如果不会部署K8S的可以参考:kubernetes_v1.35.1高可用集群部署实战 。

在软件开发过程中,代码从编写完成到最终上线往往需要经历编译、测试、打包、部署等多个环节。如果这些工作依赖人工操作,不仅效率低下,而且容易因为人为失误导致发布失败,甚至影响生产环境的稳定运行。
通过 Jenkins 自动化流水线,开发团队可以实现:
-
代码提交后自动触发构建
-
自动执行单元测试与质量检查
-
自动打包生成发布产物
-
自动构建 Docker 镜像
-
自动推送至镜像仓库
-
自动部署至测试环境或生产环境
-
自动通知发布结果
从而将原本需要数十分钟甚至数小时的发布流程缩短至几分钟,大幅提升研发效率和交付质量。
通过学习本文,你将能够独立搭建一套完整的 Jenkins 自动化发布平台,实现从代码提交到应用部署的全流程自动化,为企业研发效能提升和 DevOps 体系建设奠定坚实基础。
我整理了这篇 2026 年最新 、 最完整的企业级 DevOps 全流程实战指南 。本文基于当前最稳定的版本:Kubernetes v1.35.1 、GitLab 18.10.3 、Harbor 2.11.0 和Jenkins 2.462.3,从最基础的环境准备开始,一步步带你完成:
文章没有空洞的理论,只有可直接复制执行的命令和配置文件。我会把自己踩过的每一个坑、每一个容易忽略的细节都毫无保留地分享给你。无论你是刚接触 DevOps 的新手,还是想要完善现有流水线的资深工程师,跟着本文操作,你将在半天内搭建起一套完整的、可用于生产环境的 CI/CD 流水线,实现代码提交后自动构建、自动测试、自动打包、自动部署到 Kubernetes 集群的全流程自动化。
一、部署前去工作节点确认基础信息
docker --version
docker Compose version v2.40.3

二、开始部署jenkins
安装方式根据实际情况自选安装方式。
docker pull jenkins/jenkins:2.555.1-lts-jdk21
docker tag jenkins/jenkins:2.555.1-lts-jdk21 192.168.1.20/jenkins/jenkins:2.555.1-lts-jdk21
docker push 192.168.1.20/jenkins/jenkins:2.555.1-lts-jdk21
使用新版本的jenkins启动依赖JDK版本是Java21或者更高版本,但是项目又是老项目使用jdk8,或者jdk17,需要把原理的配置"- PATH=...." 注释掉。
maven/go/nodejs/python 怎么用?打开 Jenkins → 系统管理 → 全局工具,配置新增 JDK/Maven/Golang,路径填你挂载的目录(如 /usr/local/maven),项目构建时直接选就行。
environment:
- JENKINS_OPTS=--prefix=/jenkins
- JDK17_HOME=/usr/local/jdk17
- MAVEN_HOME=/usr/local/maven
- PYTHON_HOME=/usr/local/python3
- GOPATH=$HOME/go
- GOROOT=/usr/local/go
- TZ=Asia/Shanghai
还有想保留"- PATH=...." 的话,可以自行安装java21,并挂载:
environment:
- JAVA_HOME=/usr/local/jdk21
- JDK17_HOME=/usr/local/jdk17
- MAVEN_HOME=/usr/local/maven
- PYTHON_HOME=/usr/local/python3
- PATH=$PATH:/usr/local/jdk17/bin:/usr/local/maven/bin:/usr/local/go/bin:/usr/local/nodejs/bin:/usr/local/python3/bin
- GOPATH=$HOME/go
- GOROOT=/usr/local/go
- TZ=Asia/Shanghai
三、jenkins安装必要插件
四、Jenkins 配置 Kubernetes 凭证
五、Jenkins 配置 gitlab 凭证
六、Jenkins 配置 Harbor 凭证

七、Jenkins 配置【系统配置】和【全局工具配置】
八、gitlab新建2个分支
✅ 开发主干 :develop
✅ 发版上线 :release/vx.x.x
8.1、新建开发分支develop


8.2、新建发版上线分支 ``release/vx.x.x

release/v1.0.0

8.3、两个分支创建完成

九、Jenkins流水线构建Java后端项目
十、Jenkins 让GitLab自动触发构建
十一、Jenkins 流水线构建vue前端项目
11.1、安装流水线必要插件
Pipeline Stage View : 给你的 Pipeline 提供可视化的阶段进度视图。轻量可视化,不改变原有界面,只在构建详情页加一个进度条。
Blue Ocean: 给 Jenkins 提供一个全新的、更现代化的流水线专属 UI。全新 UI 体验,重写了 Jenkins 的流水线界面,更美观但资源占用稍高。
Slack Notification:企业微信 / 钉钉通知插件-根据团队沟通工具选择,实时推送构建结果。
List Git Branches Parameter:直接配置仓库 URL,不依赖任务 SCM 配置GitHub,获取多分支。
Active Choices :是 Jenkins 中一款功能极强的动态参数化构建插件,它解决了传统静态参数的局限性,让构建表单变得更智能、更灵活。




11.2、使用List Git Branches Parameter插件获取多分支
toolbox-web

参数化构建过程-->List Git branches (and more)

List Git branches (and more):
Name:BRANCH_NAME
Description:选择要构建的toolbox-web分支
Repository URL:http://192.168.1.50:8980/aiops-team/toolbox-web.git
Credentials:gitlab开发组凭证
Parameter Type:Branch
Sort Mode: ASCENDING
Default Value: develop
其它保持默认

Pipeline script:
pipeline {
agent any
environment {
// 环境变量 PATH
PATH = "${env.PATH}:" +
"${env.JAVA_HOME}/bin:" +
"${env.JDK17_HOME}/bin:" +
"${env.MAVEN_HOME}/bin:" +
"${env.GO_HOME}/bin:" +
"${env.NODE_HOME}/bin:" +
"${env.PYTHON_HOME}/bin"
}
stages {
stage('获取分支') {
steps {
script {
echo "原始分支参数:${params.BRANCH_NAME}"
}
}
}
stage('拉代码') {
steps {
// 直接在行内替换 refs/heads/ 前缀,兼容两种格式
git url: 'http://192.168.1.50:8980/aiops-team/toolbox-web.git',
credentialsId: 'gitlab-group-token',
branch: "${params.BRANCH_NAME.replace('refs/heads/', '')}"
}
}
stage('前端构建') {
steps {
echo "=== NODE_HOME (${env.NODE_HOME}) ==="
sh "node -v"
sh "npm -v"
sh "npm ci"
sh "npm run build"
sh "ls -la dist/"
}
}
}
}
动态获取gitlab的分支:



11.3、使用Active Choices Parameter插件实时获取多分支
进入任务配置页:打开 toolbox-web 任务 → 点击「Configure」
开启参数化构建:勾选「参数化构建过程」
添加 Active Choices 参数:点击「添加参数」→ 选择「Active Choices Parameter」

1、Jenkins 连通性测试
在 Jenkins 服务器上执行以下命令,测试 GitLab API 是否能正常访问
curl -H "PRIVATE-TOKEN: glpat-Ir1swBgzZEQ4q9Gs7sgHZW86MQp1OjEH.01.0w1r0a1al" \ "http://192.168.1.50:8980/api/v4/projects/aiops-team%2Ftoolbox-web/repository/branches"

2、Active Choices Parameter 配置
Name:
BRANCH
Groovy Script:
// 1. 配置GitLab 信息
def gitlabHost = "http://192.168.1.50:8980"
def projectPath = "aiops-team/toolbox-web"
def gitlabApiToken = "glpat-Ir1swBgzZEQ4q9Gs7sgHZW86MQp1OjEH.01.0w1r0a1al"
// 2. URL 编码项目路径(处理特殊字符,修正原脚本的转义问题)
def encodedProjectPath = URLEncoder.encode(projectPath, "UTF-8").replaceAll("\\+", "%20")
// 3. 调用 GitLab API 获取分支列表
def apiUrl = "${gitlabHost}/api/v4/projects/${encodedProjectPath}/repository/branches?per_page=100"
def connection = new URL(apiUrl).openConnection() as HttpURLConnection
connection.setRequestMethod("GET")
connection.setRequestProperty("PRIVATE-TOKEN", gitlabApiToken)
connection.setRequestProperty("Accept", "application/json")
// 4. 处理 API 响应
if (connection.responseCode == 200) {
def response = new groovy.json.JsonSlurper().parse(connection.inputStream)
def branches = response.collect { it.name }.sort()
return branches
} else {
// API 调用失败时返回空列表,避免构建报错
return []
}

直接点击右边的 Approve script 按钮批准这个版本:

直接取消勾选脚本下方的 Use Groovy Sandbox 复选框:

Description:选择要构建的toolbox-web分支

3、流水线Pipeline 配置
pipeline {
agent any
environment {
// 合并系统PATH和自定义工具路径,确保所有命令都能找到
PATH = "${env.PATH}:" +
"${env.JAVA_HOME}/bin:" +
"${env.JDK17_HOME}/bin:" +
"${env.MAVEN_HOME}/bin:" +
"${env.GO_HOME}/bin:" +
"${env.NODE_HOME}/bin:" +
"${env.PYTHON_HOME}/bin"
}
stages {
stage('拉取代码') {
steps {
// 核心:直接使用 ${BRANCH} 引用用户选择的分支
git(
url: 'http://192.168.1.50:8980/aiops-team/toolbox-web.git',
branch: "${BRANCH}", // 必须用双引号!
credentialsId: 'gitlab-group-token' // 你的凭据ID
)
}
}
// 验证分支是否正确获取
stage('打印分支信息') {
steps {
echo "当前构建的分支是:${BRANCH}"
echo "通过环境变量获取:${env.BRANCH}"
}
}
}

实时动态获取的项目分支:

通过环境变量获取:develop

11.4、分支正常获取完善编译流程
pipeline {
agent any
environment {
// 合并系统PATH和自定义工具路径,确保所有命令都能找到
PATH = "${env.PATH}:" +
"${env.JAVA_HOME}/bin:" +
"${env.JDK17_HOME}/bin:" +
"${env.MAVEN_HOME}/bin:" +
"${env.GO_HOME}/bin:" +
"${env.NODE_HOME}/bin:" +
"${env.PYTHON_HOME}/bin"
}
stages {
stage('拉取代码') {
steps {
// 核心:直接使用 ${BRANCH} 引用用户选择的分支
git(
url: 'http://192.168.1.50:8980/aiops-team/toolbox-web.git',
branch: "${BRANCH}", // 必须用双引号!
credentialsId: 'gitlab-group-token' // 你的凭据ID
)
}
}
// 验证分支是否正确获取
stage('打印分支信息') {
steps {
echo "当前构建的分支是:${BRANCH}"
echo "通过环境变量获取:${env.BRANCH}"
}
}
stage('前端构建') {
steps {
sh '''
set -e
# 配置国内npm源,解决依赖下载慢/超时问题
npm config set registry https://registry.npmmirror.com/
# 验证Node/NPM版本
node -v
npm -v
# 安装依赖(ci比install更稳定,适合CI环境)
npm ci
# 生产环境打包
npm run build
# 验证构建产物
ls -la dist/
'''
}
}
}
}


11.5、在项目下创建docker目录准备构建镜像
docker pull nginx:1.31.1
docker pull nginx:1.31.1-alpine
docker save -o /home/nginx@1.31.1.tar docker.io/library/nginx:1.31.1
docker save -o /home/nginx@1.31.1-alpine.tar docker.io/library/nginx:1.31.1-alpine
docker tag docker.io/library/nginx:1.31.1 192.168.1.20/nginx/nginx:1.31.1
docker tag docker.io/library/nginx:1.31.1-alpine 192.168.1.20/nginx/nginx:1.31.1-alpine
docker push 192.168.1.20/nginx/nginx:1.31.1
docker push 192.168.1.20/nginx/nginx:1.31.1-alpine

FROM 192.168.1.20/nginx/nginx:1.31.1-alpine
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/shanghai" > /etc/timezone
COPY ./dist/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN set -ex && \
if ! id -u nginx >/dev/null 2>&1; then \
useradd -r -s /sbin/nologin nginx; \
fi && \
chown -R nginx:nginx /usr/share/nginx/html && \
chmod -R 755 /usr/share/nginx/html && \
chmod 644 /etc/nginx/conf.d/default.conf \

pipeline {
agent any
environment {
// 合并系统PATH和自定义工具路径,确保所有命令都能找到
PATH = "${env.PATH}:" +
"${env.JAVA_HOME}/bin:" +
"${env.JDK17_HOME}/bin:" +
"${env.MAVEN_HOME}/bin:" +
"${env.GO_HOME}/bin:" +
"${env.NODE_HOME}/bin:" +
"${env.PYTHON_HOME}/bin"
// 私有Harbor仓库配置
HARBOR_REGISTRY = "192.168.1.20"
HARBOR_PROJECT = "toolbox"
}
stages {
// 验证分支是否正确获取
stage('打印分支信息') {
steps {
echo "当前构建的分支是:${BRANCH}"
echo "通过环境变量获取:${env.BRANCH}"
}
}
stage('拉取代码') {
steps {
// 核心:直接使用 ${BRANCH} 引用用户选择的分支
git(
url: 'http://192.168.1.50:8980/aiops-team/toolbox-web.git',
branch: "${BRANCH}", // 必须用双引号!
credentialsId: 'gitlab-group-token' // 你的凭据ID
)
}
}
stage('前端构建') {
steps {
sh '''
set -e
# 配置国内npm源,解决依赖下载慢/超时问题
npm config set registry https://registry.npmmirror.com/
# 验证Node/NPM版本
node -v
npm -v
# 安装依赖(ci比install更稳定,适合CI环境)
npm ci
# 生产环境打包
npm run build
# 验证构建产物
ls -la dist/
'''
}
}
stage('拷贝产物至docker目录') {
steps {
sh '''
set -e
# 如果docker目录不存在则自动创建
mkdir -p docker
# 递归拷贝dist全部文件到docker目录
cp -r dist docker/
# 校验拷贝结果
ls -la docker/
'''
}
}
stage('构建镜像并推送Harbor') {
steps {
withCredentials([usernamePassword(
credentialsId: 'harbor-creds',
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
)]) {
sh '''
# 进入docker目录
cd docker
# 拼接前端镜像标签:仓库/项目/应用名:分支-构建号
IMAGE_TAG="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/toolbox-web:${BRANCH}-${BUILD_NUMBER}"
echo "🔨 开始构建镜像:${IMAGE_TAG}"
# 构建Docker镜像
docker build -t "${IMAGE_TAG}" .
if [ $? -eq 0 ]; then
echo "✅ 镜像构建成功!"
docker images "${IMAGE_TAG}"
else
echo "❌ 镜像构建失败!"
exit 1
fi
# 登录Harbor仓库
echo "🔐 登录Harbor仓库:${HARBOR_REGISTRY}"
docker login "${HARBOR_REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_PASS}"
if [ $? -eq 0 ]; then
echo "✅ Harbor登录成功!"
else
echo "❌ Harbor登录失败!"
exit 1
fi
# 推送镜像到Harbor
echo "🚀 推送镜像到Harbor:${IMAGE_TAG}"
docker push "${IMAGE_TAG}"
if [ $? -eq 0 ]; then
echo "✅ 镜像推送成功!"
else
echo "❌ 镜像推送失败!"
exit 1
fi
# 清理本地镜像,释放磁盘
echo "🧹 清理本地镜像"
docker rmi "${IMAGE_TAG}"
docker logout "${HARBOR_REGISTRY}"
echo "当前构建号:${BUILD_NUMBER}"
# 写入环境变量文件,供后续部署阶段读取
echo "IMAGE=${IMAGE_TAG}" > ../image.env
echo "BRANCH=${BRANCH}" >> ../image.env
echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ../image.env
echo "APP_NAME=toolbox-web" >> ../image.env
echo "🎉 镜像构建推送完成!"
echo "📦 最终镜像地址:${IMAGE_TAG}"
'''
}
}
}
}
}


11.6、在项目下创建K8S目录并创建部署剧本

apiVersion: apps/v1
kind: Deployment
metadata:
name: toolbox-web-platform
labels:
app: toolbox-web-platform
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: toolbox-web-platform
template:
metadata:
labels:
app: toolbox-web-platform
version: v1
spec:
containers:
- name: toolbox-web-platform
image: ${IMAGE}
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
apiVersion: v1
kind: Service
metadata:
name: toolbox-web-platform-service
labels:
app: toolbox-web
spec:
type: NodePort
selector:
app: toolbox-web-platform
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
nodePort: 30080
11.7、准备K8S集群config(客户端连接配置文件)


config 是kubectl 客户端连接 Kubernetes 集群的核心配置文件,它包含了集群地址、用户认证凭证、默认命名空间等所有必要信息。



stage('部署到K8S集群') { steps { withCredentials([file( credentialsId: KUBECONFIG_CRED, variable: 'KUBECONFIG_FILE' )]) { // Kubeconfig写入方式 sh ''' rm -rf /root/.kube/config mkdir -p /root/.kube cp ${KUBECONFIG_FILE} /root/.kube/config chmod 600 /root/.kube/config ''' echo "✅ Kubeconfig配置完成!" sh """ # 验证K8S连接 echo "🔍 验证K8S集群连接" kubectl cluster-info """ // 清理敏感文件(增强安全性,避免凭证和临时文件残留) sh """ # 删除Kubeconfig(包含K8s认证信息) rm -rf /root/.kube/config """ } } }
流水线和K8S集群交互正常:

11.8、流水线开始执行部署前端剧本
pipeline {
agent any
environment {
// 合并系统PATH和自定义工具路径,确保所有命令都能找到
PATH = "${env.PATH}:" +
"${env.JAVA_HOME}/bin:" +
"${env.JDK17_HOME}/bin:" +
"${env.MAVEN_HOME}/bin:" +
"${env.GO_HOME}/bin:" +
"${env.NODE_HOME}/bin:" +
"${env.PYTHON_HOME}/bin"
// 私有Harbor仓库配置
HARBOR_REGISTRY = "192.168.1.20"
HARBOR_PROJECT = "toolbox"
// K8s配置
KUBECONFIG_CRED = "kubeconfig-cred"
K8SNAMESPACE = "toolbox-bigdata"
// 应用配置
DEPLOYMENT_NAME = "toolbox-web-platform"
K8S_DEPLOY_FILE = "k8s/deployment.yaml"
K8S_SERVICE_FILE = "k8s/service.yaml"
}
stages {
// 验证分支是否正确获取
stage('打印分支信息') {
steps {
echo "当前构建的分支是:${BRANCH}"
echo "通过环境变量获取:${env.BRANCH}"
}
}
stage('拉取代码') {
steps {
// 核心:直接使用 ${BRANCH} 引用用户选择的分支
git(
url: 'http://192.168.1.50:8980/aiops-team/toolbox-web.git',
branch: "${BRANCH}", // 必须用双引号!
credentialsId: 'gitlab-group-token' // 你的凭据ID
)
}
}
stage('前端构建') {
steps {
sh '''
set -e
# 配置国内npm源,解决依赖下载慢/超时问题
npm config set registry https://registry.npmmirror.com/
# 验证Node/NPM版本
node -v
npm -v
# 安装依赖(ci比install更稳定,适合CI环境)
npm ci
# 生产环境打包
npm run build
# 验证构建产物
ls -la dist/
'''
}
}
stage('拷贝产物至docker目录') {
steps {
sh '''
set -e
# 如果docker目录不存在则自动创建
mkdir -p docker
# 递归拷贝dist全部文件到docker目录
cp -r dist docker/
# 校验拷贝结果
ls -la docker/
'''
}
}
stage('构建镜像并推送Harbor') {
steps {
withCredentials([usernamePassword(
credentialsId: 'harbor-creds',
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
)]) {
sh '''
# 进入docker目录
cd docker
# 拼接前端镜像标签:仓库/项目/应用名:分支-构建号
IMAGE_TAG="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/toolbox-web:${BRANCH}-${BUILD_NUMBER}"
echo "🔨 开始构建镜像:${IMAGE_TAG}"
# 构建Docker镜像
docker build -t "${IMAGE_TAG}" .
if [ $? -eq 0 ]; then
echo "✅ 镜像构建成功!"
docker images "${IMAGE_TAG}"
else
echo "❌ 镜像构建失败!"
exit 1
fi
# 登录Harbor仓库
echo "🔐 登录Harbor仓库:${HARBOR_REGISTRY}"
docker login "${HARBOR_REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_PASS}"
if [ $? -eq 0 ]; then
echo "✅ Harbor登录成功!"
else
echo "❌ Harbor登录失败!"
exit 1
fi
# 推送镜像到Harbor
echo "🚀 推送镜像到Harbor:${IMAGE_TAG}"
docker push "${IMAGE_TAG}"
if [ $? -eq 0 ]; then
echo "✅ 镜像推送成功!"
else
echo "❌ 镜像推送失败!"
exit 1
fi
# 清理本地镜像,释放磁盘
echo "🧹 清理本地镜像"
docker rmi "${IMAGE_TAG}"
docker logout "${HARBOR_REGISTRY}"
echo "当前构建号:${BUILD_NUMBER}"
# 写入环境变量文件,供后续部署阶段读取
echo "IMAGE=${IMAGE_TAG}" > ../image.env
echo "BRANCH=${BRANCH}" >> ../image.env
echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ../image.env
echo "APP_NAME=toolbox-web" >> ../image.env
echo "🎉 镜像构建推送完成!"
echo "📦 最终镜像地址:${IMAGE_TAG}"
'''
}
}
}
stage('部署到K8S集群') {
steps {
script {
// 镜像和版本信息
def imageEnv = readFile('image.env').trim()
def envMap = [:]
imageEnv.split('\n').each { line ->
def parts = line.split('=', 2)
envMap[parts[0]] = parts[1]
}
def IMAGE = envMap['IMAGE']
def PROJECT_VERSION = envMap['PROJECT_VERSION']
def CURRENT_BUILD = envMap['BUILD_NUMBER'] as Integer
def PREVIOUS_BUILD = CURRENT_BUILD - 1
def PROJECT_ARTIFACT_ID = envMap['PROJECT_ARTIFACT_ID']
echo "📦 待部署镜像: ${IMAGE}"
echo "🔢 当前构建号: ${CURRENT_BUILD}"
echo "🔙 上一个构建号: ${PREVIOUS_BUILD}"
withCredentials([file(
credentialsId: KUBECONFIG_CRED,
variable: 'KUBECONFIG_FILE'
)]) {
// 使用withEnv临时设置KUBECONFIG环境变量(安全且正确的方式)
// 无需复制到全局目录,凭证仅在当前代码块内有效
withEnv([
"KUBECONFIG=${KUBECONFIG_FILE}",
"NAMESPACE=${K8SNAMESPACE}",
"IMAGE=${IMAGE}",
"PROJECT_VERSION=${PROJECT_VERSION}",
"CURRENT_BUILD=${CURRENT_BUILD}",
"PREVIOUS_BUILD=${PREVIOUS_BUILD}",
"PROJECT_ARTIFACT_ID=${PROJECT_ARTIFACT_ID}"
]) {
sh '''
# Kubeconfig写入方式
rm -rf /root/.kube/config
mkdir -p /root/.kube
cp ${KUBECONFIG_FILE} /root/.kube/config
chmod 600 /root/.kube/config
# 验证K8S连接
echo "🔍 验证K8S集群连接"
kubectl cluster-info
if [ $? -ne 0 ]; then
echo "❌ K8S集群连接失败,请检查kubeconfig凭证"
exit 1
fi
# 验证部署文件存在
if [ ! -f "${K8S_DEPLOY_FILE}" ]; then
echo "❌ 未找到K8S部署文件: ${K8S_DEPLOY_FILE}"
exit 1
fi
# 替换部署文件中的变量占位符
echo "替换部署文件中的变量占位符"
sed -i 's|${IMAGE}|'"${IMAGE}"'|g' "${K8S_DEPLOY_FILE}"
# 预览变更
echo "即将应用的部署变更:"
kubectl diff -f "${K8S_DEPLOY_FILE}" -n ${NAMESPACE} || true
# 部署应用
echo "开始部署应用到命名空间: ${NAMESPACE}"
kubectl apply -f "${K8S_DEPLOY_FILE}" -n ${NAMESPACE}
# 部署Service
if [ -f "${K8S_SERVICE_FILE}" ]; then
kubectl apply -f "${K8S_SERVICE_FILE}" -n ${NAMESPACE}
echo "Service部署完成"
fi
# 等待部署完成
echo "等待Deployment滚动更新完成"
kubectl rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE} --timeout=300s
if [ $? -eq 0 ]; then
echo "✅ 应用部署成功!"
echo "📊 应用状态:"
kubectl get pods -n ${NAMESPACE} -l app=${DEPLOYMENT_NAME}
else
echo "❌ 部署失败,正在自动回滚到上一个稳定版本..."
# ✅ 回滚到上一个构建号的镜像(构建号减1)
PREVIOUS_IMAGE="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${PROJECT_ARTIFACT_ID}:${PROJECT_VERSION}-${PREVIOUS_BUILD}"
echo "🔙 回滚到镜像: ${PREVIOUS_IMAGE}"
# 更新Deployment到上一个版本的镜像
kubectl set image deployment/${DEPLOYMENT_NAME} \
${DEPLOYMENT_NAME}=${PREVIOUS_IMAGE} \
-n ${NAMESPACE}
# 等待回滚完成
kubectl rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE} --timeout=300s
echo "✅ 回滚完成,请检查应用日志"
exit 1
fi
# 清理临时文件
rm -f deploy.tmp.yaml image.env
# 删除Kubeconfig(包含K8s认证信息)
rm -rf /root/.kube/config
'''
}
}
}
}
}
}
}


查询前端端口:
[root@master1 ~]# kubectl get svc -n toolbox-bigdata -w
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
toolbox-web-platform-service NodePort 10.96.247.208 <none> 8080:30080/TCP 13m

11.9、改造构建参数界面
添加一个输入框,如果输入了版本号,就 把develop-x 替换成输入的版本号
docker pull 192.168.1.20/toolbox/toolbox-web:develop-1




pipeline {
agent any
environment {
// 合并系统PATH和自定义工具路径,确保所有命令都能找到
PATH = "${env.PATH}:" +
"${env.JAVA_HOME}/bin:" +
"${env.JDK17_HOME}/bin:" +
"${env.MAVEN_HOME}/bin:" +
"${env.GO_HOME}/bin:" +
"${env.NODE_HOME}/bin:" +
"${env.PYTHON_HOME}/bin"
// 私有Harbor仓库配置
HARBOR_REGISTRY = "192.168.1.20"
HARBOR_PROJECT = "toolbox"
// K8s配置
KUBECONFIG_CRED = "kubeconfig-cred"
K8SNAMESPACE = "toolbox-bigdata"
// 应用配置
DEPLOYMENT_NAME = "toolbox-web-platform"
K8S_DEPLOY_FILE = "k8s/deployment.yaml"
K8S_SERVICE_FILE = "k8s/service.yaml"
}
stages {
// 验证分支是否正确获取
stage('打印分支信息') {
steps {
echo "当前构建的分支是:${BRANCH}"
echo "通过环境变量获取:${env.BRANCH}"
echo "输入版本号:${VERSION}"
echo "通过环境变量获取输入版本号:${env.VERSION}"
}
}
stage('拉取代码') {
steps {
// 核心:直接使用 ${BRANCH} 引用用户选择的分支
git(
url: 'http://192.168.1.50:8980/aiops-team/toolbox-web.git',
branch: "${BRANCH}", // 必须用双引号!
credentialsId: 'gitlab-group-token' // 你的凭据ID
)
}
}
stage('前端构建') {
steps {
sh '''
set -e
# 配置国内npm源,解决依赖下载慢/超时问题
npm config set registry https://registry.npmmirror.com/
# 验证Node/NPM版本
node -v
npm -v
# 安装依赖(ci比install更稳定,适合CI环境)
npm ci
# 生产环境打包
npm run build
# 验证构建产物
ls -la dist/
'''
}
}
stage('拷贝产物至docker目录') {
steps {
sh '''
set -e
# 如果docker目录不存在则自动创建
mkdir -p docker
# 递归拷贝dist全部文件到docker目录
cp -r dist docker/
# 校验拷贝结果
ls -la docker/
'''
}
}
stage('构建镜像并推送Harbor') {
steps {
withCredentials([usernamePassword(
credentialsId: 'harbor-creds',
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
)]) {
sh '''
# 进入docker目录
cd docker
echo "输入版本号:${VERSION}"
# 判断VERSION是否为空
if [ -z ${VERSION} ]; then
IMAGE_TAG="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/toolbox-web:${BRANCH}-${BUILD_NUMBER}"
else
IMAGE_TAG="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/toolbox-web:${VERSION}"
fi
# 拼接前端镜像标签:仓库/项目/应用名:分支-构建号
# IMAGE_TAG="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/toolbox-web:${BRANCH}-${BUILD_NUMBER}"
echo "🔨 开始构建镜像:${IMAGE_TAG}"
# 构建Docker镜像
docker build -t "${IMAGE_TAG}" .
if [ $? -eq 0 ]; then
echo "✅ 镜像构建成功!"
docker images "${IMAGE_TAG}"
else
echo "❌ 镜像构建失败!"
exit 1
fi
# 登录Harbor仓库
echo "🔐 登录Harbor仓库:${HARBOR_REGISTRY}"
docker login "${HARBOR_REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_PASS}"
if [ $? -eq 0 ]; then
echo "✅ Harbor登录成功!"
else
echo "❌ Harbor登录失败!"
exit 1
fi
# 推送镜像到Harbor
echo "🚀 推送镜像到Harbor:${IMAGE_TAG}"
docker push "${IMAGE_TAG}"
if [ $? -eq 0 ]; then
echo "✅ 镜像推送成功!"
else
echo "❌ 镜像推送失败!"
exit 1
fi
# 清理本地镜像,释放磁盘
echo "🧹 清理本地镜像"
docker rmi "${IMAGE_TAG}"
docker logout "${HARBOR_REGISTRY}"
echo "当前构建号:${BUILD_NUMBER}"
# 写入环境变量文件,供后续部署阶段读取
echo "IMAGE=${IMAGE_TAG}" > ../image.env
echo "BRANCH=${BRANCH}" >> ../image.env
echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ../image.env
echo "APP_NAME=toolbox-web" >> ../image.env
echo "🎉 镜像构建推送完成!"
echo "📦 最终镜像地址:${IMAGE_TAG}"
'''
}
}
}
stage('部署到K8S集群') {
steps {
script {
// 镜像和版本信息
def imageEnv = readFile('image.env').trim()
def envMap = [:]
imageEnv.split('\n').each { line ->
def parts = line.split('=', 2)
envMap[parts[0]] = parts[1]
}
def IMAGE = envMap['IMAGE']
def PROJECT_VERSION = envMap['PROJECT_VERSION']
def CURRENT_BUILD = envMap['BUILD_NUMBER'] as Integer
def PREVIOUS_BUILD = CURRENT_BUILD - 1
def PROJECT_ARTIFACT_ID = envMap['PROJECT_ARTIFACT_ID']
echo "📦 待部署镜像: ${IMAGE}"
echo "🔢 当前构建号: ${CURRENT_BUILD}"
echo "🔙 上一个构建号: ${PREVIOUS_BUILD}"
withCredentials([file(
credentialsId: KUBECONFIG_CRED,
variable: 'KUBECONFIG_FILE'
)]) {
// 使用withEnv临时设置KUBECONFIG环境变量(安全且正确的方式)
// 无需复制到全局目录,凭证仅在当前代码块内有效
withEnv([
"KUBECONFIG=${KUBECONFIG_FILE}",
"NAMESPACE=${K8SNAMESPACE}",
"IMAGE=${IMAGE}",
"PROJECT_VERSION=${PROJECT_VERSION}",
"CURRENT_BUILD=${CURRENT_BUILD}",
"PREVIOUS_BUILD=${PREVIOUS_BUILD}",
"PROJECT_ARTIFACT_ID=${PROJECT_ARTIFACT_ID}"
]) {
sh '''
# Kubeconfig写入方式
rm -rf /root/.kube/config
mkdir -p /root/.kube
cp ${KUBECONFIG_FILE} /root/.kube/config
chmod 600 /root/.kube/config
# 验证K8S连接
echo "🔍 验证K8S集群连接"
kubectl cluster-info
if [ $? -ne 0 ]; then
echo "❌ K8S集群连接失败,请检查kubeconfig凭证"
exit 1
fi
# 验证部署文件存在
if [ ! -f "${K8S_DEPLOY_FILE}" ]; then
echo "❌ 未找到K8S部署文件: ${K8S_DEPLOY_FILE}"
exit 1
fi
# 替换部署文件中的变量占位符
echo "替换部署文件中的变量占位符"
sed -i 's|${IMAGE}|'"${IMAGE}"'|g' "${K8S_DEPLOY_FILE}"
# 预览变更
echo "即将应用的部署变更:"
kubectl diff -f "${K8S_DEPLOY_FILE}" -n ${NAMESPACE} || true
# 部署应用
echo "开始部署应用到命名空间: ${NAMESPACE}"
kubectl apply -f "${K8S_DEPLOY_FILE}" -n ${NAMESPACE}
# 部署Service
if [ -f "${K8S_SERVICE_FILE}" ]; then
kubectl apply -f "${K8S_SERVICE_FILE}" -n ${NAMESPACE}
echo "Service部署完成"
fi
# 等待部署完成
echo "等待Deployment滚动更新完成"
kubectl rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE} --timeout=300s
if [ $? -eq 0 ]; then
echo "✅ 应用部署成功!"
echo "📊 应用状态:"
kubectl get pods -n ${NAMESPACE} -l app=${DEPLOYMENT_NAME}
else
echo "❌ 部署失败,正在自动回滚到上一个稳定版本..."
# ✅ 回滚到上一个构建号的镜像(构建号减1)
PREVIOUS_IMAGE="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${PROJECT_ARTIFACT_ID}:${PROJECT_VERSION}-${PREVIOUS_BUILD}"
echo "🔙 回滚到镜像: ${PREVIOUS_IMAGE}"
# 更新Deployment到上一个版本的镜像
kubectl set image deployment/${DEPLOYMENT_NAME} \
${DEPLOYMENT_NAME}=${PREVIOUS_IMAGE} \
-n ${NAMESPACE}
# 等待回滚完成
kubectl rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE} --timeout=300s
echo "✅ 回滚完成,请检查应用日志"
exit 1
fi
# 清理临时文件
rm -f deploy.tmp.yaml image.env
# 删除Kubeconfig(包含K8s认证信息)
rm -rf /root/.kube/config
'''
}
}
}
}
}
}
}

192.168.1.20/toolbox/toolbox-web:v0.0.1
