目录
[安装docker/docker compose](#安装docker/docker compose)
[创建群组 / 项目 / 添加人员](#创建群组 / 项目 / 添加人员)
[Create a job](#Create a job)
[Create a job](#Create a job)
[推送代码 打Tag使用](#推送代码 打Tag使用)
[推送代码 打Tag触发](#推送代码 打Tag触发)
一、准备devops的环境
项目拉取地址:git clone https://gitee.com/is-that-you/diagramhub.git
什么是Devops
软件开发最开始是由两个团队组成:
开发计划由开发团队从头开始设计和整体系统的搭建,需要系统不停的迭代更新。
运维团队将开发团队的Code进行测试后部署上线,希望系统稳定安全运行。
这看似两个目标不同的团队需要协同完成一个软件的开发。
在开发团队指定好计划完成coding后,需要提供到运维团队。
运维团队向开发团队反馈需要修复的BUG以及一些需要返工的任务。
这时开发团队需要经常等待运维团队的反馈,这无疑延长了事件并推迟了整个软件开发的周期。
会有一种方式,在开发团队等待的时候,让开发团队转移到下一个项目中,等待运维团队之前的代码提供反馈。
可是这样就意味着一个完整的项目需要一个更长的周期才能开发出最终代码。
基于现在的互联网现状,更推崇敏捷式开发,这样就导致项目的迭代速度更快,但是由于开发团队与运维团队的沟通问题,会导致新版本上线的时间成本很高,这又违背的敏捷式开发的最初的目的。
那么如果让开发团队和运维团队整合到成一个团队,协同应对一套软件呢?这就被称为DevOps。
DevOps,字面意思是Development & Operations的缩写,就是运维&开发。
虽然字面意思只涉及到了开发团队合运维团队,其实QA测试团队也是参与其中的。
网上可以查看到DevOps的符号类似于一个无穷大的符号

这表明DevOps是一个不断提高效率并且持续不断工作的过程
DevOps的方式可以让公司能够更快地应对更新和市场发展变化,开发可以快速交付,部署也更加稳定。
核心就在于++简化Dev和Ops团队之间德流程,使整体软件开发过程更快速。++
1、准备Harbor
安装docker/docker compose
# 卸载旧版本
[root@k8s-node01 ~]# yum remove -y docker docker-client docker-compose
# 安装依赖
[root@k8s-node01 ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加阿里云的docker源
[root@k8s-node01 ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 安装docker
[root@k8s-node01 ~]# yum install -y docker-ce docker-ce-cli
# 设置开机自启
[root@k8s-node01 ~]# systemctl start docker && systemctl enable docker
# 安装docker-compose
[root@k8s-node01 ~]# curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
[root@k8s-node01 ~]# chmod +x /usr/local/bin/docker-compose
[root@k8s-node01 ~]# ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# 验证
[root@k8s-node01 ~]# docker --version
Docker version 26.1.4, build 5650f9b
[root@k8s-node01 ~]# docker-compose --version
Docker Compose version v5.1.4
安装Harbor
# 下载安装包
[root@k8s-node01 ~]# wget https://github.com/goharbor/harbor/releases/download/v1.10.19/harbor-offline-installer-v1.10.19.tgz
# 解压
[root@k8s-node01 ~]# tar -zxf harbor-offline-installer-v1.10.19.tgz
# 编辑配置文件
[root@k8s-node01 ~]# vim harbor/harbor.yml
hostname: 192.168.1.13 #访问的IP
port: 8899 #端口
harbor_admin_password: Tcloud123@ #密码
#把https段注释掉
#https:
# # https port for harbor, default is 443
# port: 443
# # The path of cert and key files for nginx
# certificate: /your/certificate/path
# private_key: /your/private/key/path
# 安装
[root@k8s-node01 ~]# harbor/install.sh
[Step 5]: starting Harbor ...
WARN[0000] /root/harbor/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] up 10/10
✔ Network harbor_harbor Created 0.0s
✔ Container harbor-log Started 0.3s
✔ Container harbor-db Started 0.7s
✔ Container registryctl Started 0.7s
✔ Container registry Started 0.7s
✔ Container harbor-portal Started 0.6s
✔ Container redis Started 0.7s
✔ Container harbor-core Started 1.0s
✔ Container harbor-jobservice Started 1.3s
✔ Container nginx Started 1.3s
✔ ----Harbor has been installed and started successfully.----
创建仓库
账号:admin
密码:Tcloud123@

仓库名字:diagramhub

集群所有节点配置Harbor信任
[root@k8s-master01 ~]# cat > /etc/containerd/certs.d/192.168.1.13:8899/hosts.toml <<EOF
server = "http://192.168.1.13:8899"
[host."http://192.168.1.13:8899"]
capabilities = ["pull", "resolve", "push"]
skip_verify = true
EOF
[root@k8s-master01 ~]# systemctl restart containerd
创建Harbor的Secret
没有这个命名空间先创建这个命名空间
[root@k8s-master01 DiagramHub]# kubectl create secret docker-registry harbor-secret --docker-server=192.168.1.13:8899 --docker-username=admin --docker-password=Tcloud123@ -n diagramhub
2、准备jenkins
安装jdk21
从Oracle官网下载JDK21上传到我们的服务器
# 创建存放目录
[root@k8s-node02 ~]# mkdir -p /opt/environment
# 解压
[root@k8s-node02 ~]# tar -zxvf jdk-21_linux-x64_bin.tar.gz -C /opt/environment/
# 配置环境变量
[root@k8s-node02 ~]# cat >> /etc/profile << EOF
# Set java environment
JAVA_HOME=/opt/environment/jdk-21.0.11
PATH=\$PATH:\$JAVA_HOME/bin
export JAVA_HOME PATH
EOF
# 刷新配置
[root@k8s-node02 ~]# source /etc/profile
# 查看Java版本
[root@k8s-node02 ~]# java -version
java version "21.0.11" 2026-04-21 LTS
Java(TM) SE Runtime Environment (build 21.0.11+9-LTS-211)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.11+9-LTS-211, mixed mode, sharing)
安装Maven
# 下载Maven安装包
[root@k8s-node02 ~]# wget https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz
# 解压
[root@k8s-node02 ~]# tar -zxvf apache-maven-3.9.16-bin.tar.gz -C /opt/environment/
# 配置jar包依赖的存放位置
[root@k8s-node02 ~]# vim /opt/environment/apache-maven-3.9.16/conf/settings.xml
# 把默认的注释添加这段
<!--mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*</mirrorOf>
<name>Pseudo repository to mirror external repositories initially using HTTP.</name>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>-->
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
# 配置环境变量
[root@k8s-node02 ~]# cat >> /etc/profile << EOF
# Set maven environment
MAVEN_HOME=/opt/environment/apache-maven-3.9.16
PATH=\$PATH:\$MAVEN_HOME/bin
export MAVEN_HOME PATH
EOF
# 生效
[root@k8s-node02 ~]# source /etc/profile
# 查看maven的版本
[root@k8s-node02 ~]# mvn -v
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5)
Maven home: /opt/environment/apache-maven-3.9.16
Java version: 21.0.11, vendor: Oracle Corporation, runtime: /opt/environment/jdk-21.0.11
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.4.226-1.el7.elrepo.x86_64", arch: "amd64", family: "unix"
安装docker
# 下载docker的安装包
[root@k8s-node02 ~]# wget https://download.docker.com/linux/static/stable/x86_64/docker-20.10.7.tgz
# 解压
[root@k8s-node02 ~]# tar -zxvf docker-20.10.7.tgz
# 添加到Path
[root@k8s-node02 ~]# mv docker/* /usr/bin
[root@k8s-node02 ~]# rm -rf docker-20.10.7.tgz docker
# system管理docker
[root@k8s-node02 ~]# cat > /usr/lib/systemd/system/docker.service << EOF
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
EOF
# 配置镜像加速和Harbor地址
[root@k8s-node02 ~]# mkdir -p /etc/docker
[root@k8s-node02 ~]# cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [
"https://docker.m.daocloud.io"
],
"insecure-registries": ["192.168.1.14:8899"]
}
EOF
# 设置开机自启动
[root@k8s-node02 ~]# systemctl daemon-reload
[root@k8s-node02 ~]# systemctl start docker && systemctl enable docker
# 测试访问Harbor
[root@k8s-node02 ~]# docker login 192.168.1.13:8899
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
# 推送测试
[root@k8s-node02 ~]# docker tag nginx:latest 192.168.1.13:8899/diagramhub/nginx:latest
[root@k8s-node02 ~]# docker push 192.168.1.13:8899/diagramhub/nginx:latest
The push refers to repository [192.168.1.13:8899/diagramhub/nginx]
271182c95b03: Pushed
58a87cd80f7d: Pushed
b77962fbff2d: Pushed
8e0111b626dd: Pushed
1f561b62ded0: Pushed
5f120dc722b0: Pushed
219a998c6050: Pushed
latest: digest: sha256:ae33218af50e1d8613f388276f0f29aacdbb815da94cf85f30b13df070022bac size: 1778
# 在集群任意节点拉群镜像测试
[root@k8s-master01 certs.d]# crictl pull --creds admin:Tcloud123@ 192.168.1.13:8899/diagramhub/nginx:latest
Image is up to date for sha256:7aaca76c508f7d121ff29cbe9dd071012486d00c21e17655eb1a1dfb711e9330
安装git
[root@k8s-node02 ~]# yum -y install git
[root@k8s-node02 ~]# git --version
git version 1.8.3.1
安装kubectl命令
[root@k8s-node02 ~]# curl -LO https://dl.k8s.io/release/v1.36.1/bin/linux/amd64/kubectl
[root@k8s-node02 ~]# chmod +x kubectl
[root@k8s-node02 ~]# mv kubectl /usr/local/bin/
安装jenkins
# 下载安装包
[root@k8s-node02 ~]# wget https://mirrors.jenkins.io/redhat-stable/jenkins-2.555.2-1.noarch.rpm
# 创建jenkins安装目录
[root@k8s-node02 ~]# mkdir -p /opt/server/jenkins
# 安装
[root@k8s-node02 ~]# mv jenkins-2.555.2-1.noarch.rpm /opt/server/jenkins/
[root@k8s-node02 ~]# cd /opt/server/jenkins/
[root@k8s-node02 jenkins]# rpm -ivh jenkins-2.555.2-1.noarch.rpm
# 修改配置文件
[root@k8s-node02 jenkins]# vim /usr/lib/systemd/system/jenkins.service
#Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64"
Environment="JAVA_HOME=/opt/environment/jdk-21.0.11" #添加我们的JAVA路径
Environment="JENKINS_PORT=8888" # 修改端口
User=root
Group=root
# 设置用户数组
[root@k8s-node02 jenkins]# chown -R root:root /var/lib/jenkins/ && chown -R root:root /var/cache/jenkins/
# 下载启动依赖 不然启动失败
[root@k8s-node02 jenkins]# yum install -y fontconfig
# 启动
[root@k8s-node02 jenkins]# systemctl daemon-reload
[root@k8s-node02 jenkins]# systemctl start jenkins && systemctl enable jenkins
访问jenkins并安装插件

选择插件来安装------安装

创建管理员用户

安装插件
右上角设置------插件管理------Available plugins

# 安装以下插件
Build Authorization Token Root
外部系统通过令牌安全触发 Jenkins 构建。
gitlab
联动 GitLab,代码提交自动触发构建,同步构建结果。
SonarQube Scanner
对接 SonarQube,完成代码质量、漏洞扫描。
Node and Label parameter
构建时选择代理节点 / 标签执行任务。
Kubernetes
对接 K8s,动态创建临时 Pod 作为构建代理,用完自动销毁。
maven integration
适配 Maven 项目,实现 Java 项目打包、解析测试报告。
Git Parameter
将 Git 分支、标签等作为构建参数,手动选择版本构建。
Config File Provider
统一管理配置文件,构建时自动加载下发。
Pipeline: Multibranch with defaults
增强多分支流水线,可设置默认流程,无 Jenkinsfile 也能构建。
docker
让 Jenkins 调用 Docker 能力,实现镜像构建、推送、运行等操作。
NodeJS
让Jenkins 调用本地的nodejs功能
安装完找到Download progress ------ 安装完重启jenkins(空闲)点击完自动重启
3、安装Gitlab
安装gitlab
# 下载Gitlab的RPM包 下载完成上传到服务器
地址:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-15.9.1-ce.0.el7.x86_64.rpm
# 安装依赖
[root@k8s-node03 ~]# yum -y install policycoreutils-python
# 安装Gitlab
[root@k8s-node03 ~]# rpm -i gitlab-ce-15.9.1-ce.0.el7.x86_64.rpm
# 编辑配置文件
[root@k8s-node03 ~]# vim /etc/gitlab/gitlab.rb
external_url 'http://192.168.1.15:9090' # 访问地址
gitlab_rails['time_zone'] = 'Asia/Shanghai' # 时区
puma['worker_processes'] = 2
sidekiq['max_concurrency'] = 8
postgresql['shared_buffers'] = "512MB"
postgresql['max_worker_processes'] = 4
prometheus_monitoring['enable'] = false
# 更新配置
[root@k8s-node03 ~]# gitlab-ctl reconfigure
# 启动
[root@k8s-node03 ~]# gitlab-ctl restart
# 获取密码 默认账号root
[root@k8s-node03 ~]# cat /etc/gitlab/initial_root_password
修改密码

右上角头像 > Perferences > Password

禁用注册功能
点击左上角三横 > Admin>Settings > General > Sign-up restrictions > 取消 Sign-up enabled > Save changes

取消头像
Settings > General > Account and limit > 取消 Gravatar enabled > Save changes

汉化Gitlab
Settings > Preferences > Localization > Default language > 选择简体中文 > Save changes

为当前用户设置中文 修改完记得刷新
右上角用户头像 > Preferences > Localization > Language > 选择简体中文 > Save changes

设置入站网络
设置>网络>出站请求>勾选>保存

不配置这个后面gitlab不能创建内网的webhook
创建群组 / 项目 / 添加人员

创建前后端项目
前端:DiagramHub-frontend
后端:DiagramHub-backend


创建开发人员添加到群组
左上角三条杠------管理员------用户------新用户



新用户的密码更改一下,用新用户登录Gitlab

4、准备后端项目用到Mysql数据库
创建mysql初始化用到的configmap
# 创建命名空间
[root@k8s-master01 DiagramHub]# kubectl create ns diagramhub
# 创建configmap
[root@k8s-master01 DiagramHub]# kubectl create configmap mysql-init-sql --from-file=init.sql -n diagramhub
创建Mysql
注意集群需要有StorageClass
[root@k8s-master01 DiagramHub]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage nfs-provisioner Delete Immediate false 4h20m
[root@k8s-master01 DiagramHub]# vim mysql.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-service
namespace: diagramhub
spec:
ports:
- port: 3306
clusterIP: None
selector:
app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: diagramhub
spec:
serviceName: mysql-service
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: docker.m.daocloud.io/library/mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
- name: init-sql
mountPath: /docker-entrypoint-initdb.d
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "300m"
livenessProbe:
exec:
command: ["mysqladmin", "-uroot", "-p123456", "ping", "-h", "localhost"]
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["mysql", "-uroot", "-p123456", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 15
periodSeconds: 5
volumes:
- name: init-sql
configMap:
name: mysql-init-sql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: managed-nfs-storage
resources:
requests:
storage: 1Gi
# 创建
[root@k8s-master01 DiagramHub]# kubectl create -f mysql.yaml
# 查看资源
[root@k8s-master01 DiagramHub]# kubectl get pod -n diagramhub
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 29s
[root@k8s-master01 DiagramHub]# kubectl get svc -n diagramhub
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql-service ClusterIP None <none> 3306/TCP 36s
创建Java后端程序用得数据库连接地址
[root@k8s-master01 DiagramHub]# vim secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: diagramhub-secrets
namespace: diagramhub
type: Opaque
stringData:
db-username: "root"
db-password: "123456"
jwt-secret: "YourVeryLongJWTSecretKeyHere_Minimum32Chars_2024!"
# 邮件配置(如不需要可留空)
mail-host: "smtp.example.com"
mail-port: "587"
mail-username: "your-email@example.com"
mail-password: "your-email-password"
# 创建
[root@k8s-master01 DiagramHub]# kubectl create -f secrets.yaml
5、创建前端项目使用的configmap
创建nginx.conf
[root@k8s-master01 DiagramHub]# vim nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 前端路由支持(SPA)
location / {
try_files $uri $uri/ /index.html;
}
# API代理到后端服务
location /api/ {
# 使用K8S内部服务名
proxy_pass http://backend-service:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Gzip压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
创建ConfigMap
# 创建
[root@k8s-master01 DiagramHub]# kubectl create configmap frontend-nginx-config -n diagramhub --from-file=default.conf=nginx.conf
# 查看
[root@k8s-master01 DiagramHub]# kubectl get configmaps -n diagramhub
NAME DATA AGE
frontend-nginx-config 1 42s
kube-root-ca.crt 1 27h
mysql-init-sql 1 27h
二、配置Devops
1、jenkins添加凭据
创建集群的凭据
clouds


集群地址:config文件中的地址

添加凭据

Secret file 把集群的config文件上传上去
ID:k8s-kubeconfig

测试连接------Save

创建Gitlab的凭据
系统管理------凭据管理------system(全局配置)------全局凭据------Add Credentials------Username with password
ID:gitlab-credentials
用户名:gwjcloud
密码:Tcloud123@

创建Harbor的凭据
系统管理------凭据管理------system(全局配置)------全局凭据------Add Credentials------Username with password
ID:harbor-credentials
用户名:admin
密码:Tcloud123@

添加全局工具配置


2、代码推送到Gitlab
本地生成公钥私钥
# 生成公钥私钥
PS D:\Java-code\DiagramHub> ssh-keygen -t rsa
把id_rsa.pub的内容复制到gitlab的个人信息中

推送后端代码

推送前端代码
3、配置Jenkins流水线
配置后端流水线

Create a job
任务名称:DiagramHub-Backend

描述:DiagramHub 后端 - Spring Boot 应用
常规设置
在General区域找到参数化构建过程 勾选
名称:GITLAB_SECRET_TOKEN
默认值:留空 等下生成Token替换
描述:GitLab Webhook 后端验证密钥
字符参数

配置触发事件Trigger
勾选 Build when a change is pushed to GitLab. GitLab webhook URL ......
勾选:Push Events
取消勾选:Approved Merge Requests (EE-only)
取消勾选:Comments
高级------生成Secret Token


把生成的Token替换上面常规设置的默认值

记住url和Token等下创建后端的webhook使用
url:http://192.168.1.14:8888/project/DiagramHub-Backend
Token:f319fcac95cec19284b57fba17c28790
配置流水线
前端代码地址:bhttp://192.168.1.15:9090/cloud/diagramhubbend.githttp://192.168.1.15:9090/cloud/diagramhub-backend.gitbhttp://192.168.1.15:9090/cloud/diagramhubbend.git
选择之前创建的凭据:gitlab-credentials
构建分支:*/main

点击Additional Behaviours(其他行为),选择检出到指定的本地分支------应用保存

配置后端的webhook
url:http://192.168.1.14:8888/project/DiagramHub-Backend
Token:f319fcac95cec19284b57fba17c28790
选择标签推送事件
关闭SSL验证------添加Webhook

配置前端流水线
Create a job
任务名称:DiagramHub-Frontend

任务描述:DiagramHub 前端 - Spring Boot 应用

常规设置
在General区域找到参数化构建过程 勾选
字符参数
名称:GITLAB_SECRET_TOKEN
默认值:留空 等下生成去替换
描述:GitLab Webhook 前端验证密钥

配置触发事件Trigger
勾选 Build when a change is pushed to GitLab. GitLab webhook URL ......
勾选:Push Events
取消勾选:Approved Merge Requests (EE-only)
取消勾选:Comments
高级------生成Secret Token


把生成的Token替换上面常规设置的默认值

记住地址和Token,创建前端的webhook使用
地址:http://192.168.1.14:8888/project/DiagramHub-Frontend
Token:8bd9bd86e88ea5e195bf9a4314a1b3
配置流水线
前端代码地址:http://192.168.1.15:9090/cloud/diagramhub-backend.githttp://192.168.1.15:9090/cloud/diagramhub-frontend.githttp://192.168.1.15:9090/cloud/diagramhub-backend.git
选择之前创建的凭据:gitlab-credentials
构建分支:*/main

点击Additional Behaviours(其他行为),选择检出到指定的本地分支------应用保存

创建前端的webhook
地址:http://192.168.1.14:8888/project/DiagramHub-Frontend
Token:8bd9bd86e88ea5e195bf9a4314a1b3
选择标签推送事件
关闭SSL验证------添加Webhook

三、使用Devops
1、后端项目创建相关配置文件

Jenkinsfile
在后端项目的根目录创建Jenkinsfile
pipeline {
agent any
tools {
// 使用 Jenkins 全局工具配置的 Maven
// 名称必须与 Jenkins → Manage Jenkins → Global Tool Configuration 中的 Name 一致
maven 'MAVEN_HOME'
}
environment {
DOCKER_REGISTRY = '192.168.1.13:8899'
IMAGE_NAME = 'diagramhub/diagramhub-backend'
K8S_NAMESPACE = 'diagramhub'
GIT_CREDENTIALS_ID = 'gitlab-credentials'
HARBOR_CREDENTIALS_ID = 'harbor-credentials'
KUBECONFIG_CREDENTIALS_ID = 'k8s-kubeconfig'
// 配置 Docker 允许 HTTP 私有仓库(Harbor 使用 HTTP)
DOCKER_OPTS = '--insecure-registry 192.168.1.13:8899'
}
// 注意:Tag 推送触发器应在 Jenkins UI 的构建触发器中配置
// 勾选 "Build when a change is pushed to GitLab" 并只选择 "Tag push events"
// 不要在此处使用 triggers 块
stages {
stage('获取Tag版本') {
steps {
script {
// 优先使用 GitLab Webhook 传递的 tag 名称
if (env.gitlabTargetBranch) {
// GitLab Tag Push 事件:tag名称在 gitlabTargetBranch 中(格式:refs/tags/v1.0.2)
env.TAG_NAME = env.gitlabTargetBranch.replace('refs/tags/', '')
echo "📌 从 GitLab Webhook 获取到 tag: ${env.TAG_NAME}"
} else if (env.GIT_BRANCH && env.GIT_BRANCH.startsWith('refs/tags/')) {
// Jenkins Git 插件:从 GIT_BRANCH 提取
env.TAG_NAME = env.GIT_BRANCH.replace('refs/tags/', '')
echo "📌 从 GIT_BRANCH 获取到 tag: ${env.TAG_NAME}"
} else {
// 备用方案:使用 git describe
env.TAG_NAME = sh(
script: 'git describe --tags --abbrev=0 2>/dev/null || echo "latest"',
returnStdout: true
).trim()
echo "📌 从 git describe 获取到 tag: ${env.TAG_NAME}"
}
// 移除 tag 的 'v' 前缀(如果有)
env.IMAGE_TAG = env.TAG_NAME.replaceFirst(/^v/, '')
echo "🏷️ 最终镜像版本: ${env.IMAGE_TAG}"
}
}
}
stage('代码检查') {
steps {
sh 'mvn checkstyle:check || true'
}
}
stage('单元测试') {
steps {
sh 'mvn clean package -DskipTests'
}
post {
always {
junit(testResults: '**/target/surefire-reports/*.xml', allowEmptyResults: true)
}
}
}
stage('构建镜像') {
steps {
script {
withCredentials([usernamePassword(credentialsId: "${HARBOR_CREDENTIALS_ID}", usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
sh """
# 1. 先使用Maven编译(在Jenkins宿主机上,不受Docker内存限制)
mvn clean package -DskipTests -Pprod
# 2. 复制jar到Docker构建目录
cp target/*.jar .
# 3. 登录Harbor
echo "${HARBOR_PASS}" | docker login http://${DOCKER_REGISTRY} \
-u ${HARBOR_USER} --password-stdin
# 4. 构建镜像(不再需要MAVEN_OPTS参数)
docker build --pull --no-cache \
-t ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.IMAGE_TAG} .
# 5. 推送镜像
docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.IMAGE_TAG}
"""
}
}
}
}
stage('部署到K8s') {
steps {
script {
withCredentials([file(credentialsId: "${KUBECONFIG_CREDENTIALS_ID}", variable: 'KUBECONFIG_FILE')]) {
sh """
# 设置KUBECONFIG环境变量
export KUBECONFIG=${KUBECONFIG_FILE}
# 使用sed替换YAML中的镜像版本为当前构建版本
sed -i "s|image:.*diagramhub-backend:.*|image: ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.IMAGE_TAG}|" k8s/backend-deployment.yaml
# 首次部署:应用完整的 K8s 配置(创建 Deployment、Service、PVC)
kubectl apply -f k8s/backend-deployment.yaml -n ${K8S_NAMESPACE}
# 等待滚动更新完成
kubectl rollout status deployment/diagramhub-backend \
-n ${K8S_NAMESPACE} --timeout=300s
"""
}
}
}
}
}
post {
success {
echo "✅ 后端部署成功!版本: ${env.IMAGE_TAG}"
}
failure {
echo "❌ 后端部署失败!"
}
}
}
Dockerfile
后端项目根目录创建Dockerfile
# 基础运行镜像(JRE17 轻量版)
FROM eclipse-temurin:17-jre-alpine
# 工作目录
WORKDIR /app
# 复制已编译好的 jar 包(需要Jenkins先mvn编译)
COPY *.jar app.jar
# 创建上传文件夹
RUN mkdir -p /app/uploads
# 设置时区(北京时间)
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# JVM 优化参数
ENV JAVA_OPTS="-Xms256m -Xmx512m -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1
# 暴露端口
EXPOSE 8080
# 启动项目(强制使用 prod 配置)
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar --spring.profiles.active=prod"]
后端Yaml文件
后端项目根目录创建k8s目录下创建backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: diagramhub-backend
namespace: diagramhub
labels:
app: diagramhub-backend
spec:
replicas: 1
selector:
matchLabels:
app: diagramhub-backend
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: diagramhub-backend
spec:
containers:
- name: backend
image: 192.168.1.14:8899/diagramhub/diagramhub-backend:latest
ports:
- containerPort: 8080
name: http
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: diagramhub-secrets
key: db-username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: diagramhub-secrets
key: db-password
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: diagramhub-secrets
key: jwt-secret
- name: MAIL_HOST
value: "smtp.example.com"
- name: MAIL_PORT
value: "25"
- name: MAIL_USERNAME
value: ""
- name: MAIL_PASSWORD
value: ""
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: uploads
mountPath: /app/uploads
volumes:
- name: uploads
persistentVolumeClaim:
claimName: backend-uploads-pvc
imagePullSecrets:
- name: harbor-secret
---
apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: diagramhub
spec:
selector:
app: diagramhub-backend
ports:
- protocol: TCP
port: 8080
targetPort: 8080
type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backend-uploads-pvc
namespace: diagramhub
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: managed-nfs-storage
推送代码 打Tag使用
# 1. 查看修改了哪些文件
git status
# 2. 添加所有修改的文件
git add .
# 3. 提交代码
git commit -m "fix: 你的提交说明"
# 4. 推送到远程 main 分支
git push origin main
# 5. 创建本地标签
git tag -a v1.0.0 -m "Release v1.0.0 - 你的版本说明"
# 6. 推送标签(触发 Jenkins 构建)
git push origin v1.0.0
Jinkens页面查看流水线

这里可以看到详细的构建历史,等待构建完成

查看集群是否部署
# 查看pod
[root@k8s-master01 ~]# kubectl get pod -n diagramhub
NAME READY STATUS RESTARTS AGE
diagramhub-backend-7bc8c7fd65-4gppf 1/1 Running 0 67s
mysql-0 1/1 Running 8 (60m ago) 6h34m
# 查看pvc
[root@k8s-master01 ~]# kubectl get pvc -n diagramhub
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
backend-uploads-pvc Bound pvc-278ef6da-9461-491e-9be0-86a826b9c0b8 1Gi RWO managed-nfs-storage <unset> 95s
mysql-data-mysql-0 Bound pvc-79a3bce3-17b1-4536-b43d-35c717a95737 1Gi RWO managed-nfs-storage <unset> 27h
# 查看svc
[root@k8s-master01 ~]# kubectl get svc -n diagramhub
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-service ClusterIP 10.96.86.74 <none> 8080/TCP 109s
mysql-service ClusterIP None <none> 3306/TCP 26h
# 查看日志
[root@k8s-master01 ~]# kubectl logs -n diagramhub diagramhub-backend-7bc8c7fd65-4gppf
Defaulted container "backend" out of: backend, k8tz (init)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)
2026-05-30T17:22:58.060+08:00 INFO 1 --- [diagramhub-library] [ main] com.diagramhub.DiagramHubApplication : Starting DiagramHubApplication v1.0.0 using Java 17.0.19 with PID 1 (/app/app.jar started by root in /app)
2026-05-30T17:22:58.067+08:00 INFO 1 --- [diagramhub-library] [ main] com.diagramhub.DiagramHubApplication : The following 1 profile is active: "prod"
2026-05-30T17:23:23.246+08:00 INFO 1 --- [diagramhub-library] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2026-05-30T17:23:24.547+08:00 INFO 1 --- [diagramhub-library] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 1198 ms. Found 14 JPA repository interfaces.
2026-05-30T17:23:38.252+08:00 INFO 1 --- [diagramhub-library] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2026-05-30T17:23:38.368+08:00 INFO 1 --- [diagramhub-library] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-05-30T17:23:38.370+08:00 INFO 1 --- [diagramhub-library] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.16]
2026-05-30T17:23:39.965+08:00 INFO 1 --- [diagramhub-library] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2026-05-30T17:23:40.084+08:00 INFO 1 --- [diagramhub-library] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 40804 ms
2026-05-30T17:23:53.365+08:00 INFO 1 --- [diagramhub-library] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2026-05-30T17:23:53.755+08:00 INFO 1 --- [diagramhub-library] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-05-30T17:23:56.152+08:00 INFO 1 --- [diagramhub-library] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@4abdd5e
2026-05-30T17:23:56.160+08:00 INFO 1 --- [diagramhub-library] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-05-30T17:23:57.262+08:00 WARN 1 --- [diagramhub-library] [ main] org.hibernate.orm.deprecation : HHH90000025: MySQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
2026-05-30T17:24:16.149+08:00 INFO 1 --- [diagramhub-library] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2026-05-30T17:24:23.152+08:00 INFO 1 --- [diagramhub-library] [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
2026-05-30T17:24:44.445+08:00 INFO 1 --- [diagramhub-library] [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path '/actuator'
2026-05-30T17:24:52.951+08:00 INFO 1 --- [diagramhub-library] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
2026-05-30T17:24:53.049+08:00 INFO 1 --- [diagramhub-library] [ main] com.diagramhub.DiagramHubApplication : Started DiagramHubApplication in 122.502 seconds (process running for 132.154)
2026-05-30T17:24:56.150+08:00 INFO 1 --- [diagramhub-library] [ main] c.d.config.AdminPasswordInitializer : 管理员密码正确,无需重置
发布新功能版本
# 这里比如我们发布了代码的新功能 要推送到代码仓库
PS D:\Java-code\DiagramHub\backend> cd D:\Java-code\DiagramHub\backend; git tag -a v1.0.3 -m "Release v1.0.3 - 新功能"; git push origin v1.0.3
等待查看流水线

# 查看pod使用的镜像
[root@k8s-master01 DiagramHub]# kubectl get deployments.apps -n diagramhub -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
diagramhub-backend 1/1 1 1 50m backend 192.168.1.13:8899/diagramhub/diagramhub-backend:1.0.3 app=diagramhub-backend
已经更新成功
2、前端项目创建相关配置文件
Jenkinsfile
在前端项目根目录创建Jenkinsfile
pipeline {
agent any
environment {
DOCKER_REGISTRY = '192.168.1.13:8899'
IMAGE_NAME = 'diagramhub/diagramhub-frontend'
K8S_NAMESPACE = 'diagramhub'
GIT_CREDENTIALS_ID = 'gitlab-credentials'
HARBOR_CREDENTIALS_ID = 'harbor-credentials'
KUBECONFIG_CREDENTIALS_ID = 'k8s-kubeconfig'
// 配置 Docker 允许 HTTP 私有仓库(Harbor 使用 HTTP)
DOCKER_OPTS = '--insecure-registry 192.168.1.13:8899'
}
// 注意:Tag 推送触发器应在 Jenkins UI 的构建触发器中配置
// 勾选 "Build when a change is pushed to GitLab" 并只选择 "Tag push events"
// 不要在此处使用 triggers 块
stages {
stage('获取Tag版本') {
steps {
script {
// 优先使用 GitLab Webhook 传递的 tag 名称
if (env.gitlabTargetBranch) {
// GitLab Tag Push 事件:tag名称在 gitlabTargetBranch 中(格式:refs/tags/v1.0.2)
env.TAG_NAME = env.gitlabTargetBranch.replace('refs/tags/', '')
echo "📌 从 GitLab Webhook 获取到 tag: ${env.TAG_NAME}"
} else if (env.GIT_BRANCH && env.GIT_BRANCH.startsWith('refs/tags/')) {
// Jenkins Git 插件:从 GIT_BRANCH 提取
env.TAG_NAME = env.GIT_BRANCH.replace('refs/tags/', '')
echo "📌 从 GIT_BRANCH 获取到 tag: ${env.TAG_NAME}"
} else {
// 备用方案:使用 git describe
env.TAG_NAME = sh(
script: 'git describe --tags --abbrev=0 2>/dev/null || echo "latest"',
returnStdout: true
).trim()
echo "📌 从 git describe 获取到 tag: ${env.TAG_NAME}"
}
// 移除 tag 的 'v' 前缀(如果有)
env.IMAGE_TAG = env.TAG_NAME.replaceFirst(/^v/, '')
echo "🏷️ 最终镜像版本: ${env.IMAGE_TAG}"
}
}
}
stage('构建镜像') {
steps {
script {
withCredentials([usernamePassword(credentialsId: "${HARBOR_CREDENTIALS_ID}", usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
sh """
# 1. 登录Harbor
echo "${HARBOR_PASS}" | docker login http://${DOCKER_REGISTRY} \\
-u ${HARBOR_USER} --password-stdin
# 2. 构建镜像(Docker多阶段构建,内部完成npm install和build)
docker build --pull --no-cache \\
-t ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.IMAGE_TAG} .
# 3. 推送镜像
docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.IMAGE_TAG}
"""
}
}
}
}
stage('部署到K8s') {
steps {
script {
withCredentials([file(credentialsId: "${KUBECONFIG_CREDENTIALS_ID}", variable: 'KUBECONFIG_FILE')]) {
sh """
# 设置KUBECONFIG环境变量
export KUBECONFIG=${KUBECONFIG_FILE}
# 使用sed替换YAML中的镜像版本为当前构建版本
sed -i "s|image:.*diagramhub-frontend:.*|image: ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.IMAGE_TAG}|" k8s/frontend-deployment.yaml
# 首次部署:应用完整的 K8s 配置(创建 Deployment、Service、PVC)
kubectl apply -f k8s/frontend-deployment.yaml -n ${K8S_NAMESPACE}
# 等待滚动更新完成
kubectl rollout status deployment/diagramhub-frontend \\
-n ${K8S_NAMESPACE} --timeout=300s
"""
}
}
}
}
}
post {
success {
echo "✅ 前端部署成功!版本: ${env.IMAGE_TAG}"
}
failure {
echo "❌ 前端部署失败!"
}
}
}
Dockerfile
在前端项目根目录创建Dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm install
# 复制源代码
COPY . .
# 构建生产版本
RUN npm run build
# 生产阶段
FROM nginx:alpine
# 复制构建产物到nginx
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制nginx配置(如果有)
# COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
前端Yaml文件
后端项目根目录创建k8s目录下创建frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: diagramhub-frontend
namespace: diagramhub
labels:
app: diagramhub-frontend
spec:
replicas: 1
selector:
matchLabels:
app: diagramhub-frontend
template:
metadata:
labels:
app: diagramhub-frontend
spec:
imagePullSecrets:
- name: harbor-secret
containers:
- name: diagramhub-frontend
image: 192.168.1.13:8899/diagramhub/diagramhub-frontend:latest
ports:
- containerPort: 80
name: http
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: nginx-config
configMap:
name: frontend-nginx-config
---
apiVersion: v1
kind: Service
metadata:
name: diagramhub-frontend-service
namespace: diagramhub
labels:
app: diagramhub-frontend
spec:
type: ClusterIP
selector:
app: diagramhub-frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http
推送代码 打Tag触发
# 1. 查看修改了哪些文件
git status
# 2. 添加所有修改的文件
git add .
# 3. 提交代码
git commit -m "fix: 你的提交说明"
# 4. 推送到远程 main 分支
git push origin main
# 5. 创建本地标签
git tag -a v1.0.0 -m "Release v1.0.0 - 你的版本说明"
# 6. 推送标签(触发 Jenkins 构建)
git push origin v1.0.0
Jenkins页面查看流水线

查看集群是否部署
# 查看pod
[root@k8s-master01 ~]# kubectl get pod -n diagramhub
NAME READY STATUS RESTARTS AGE
diagramhub-backend-55fb995596-r8jm7 1/1 Running 1 (76s ago) 14m
diagramhub-frontend-69d547cff8-d5j4n 1/1 Running 0 40s
mysql-0 1/1 Running 4 (47s ago) 95m
# 查看service
[root@k8s-master01 ~]# kubectl get svc -n diagramhub
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-service ClusterIP 10.96.86.74 <none> 8080/TCP 4h15m
diagramhub-frontend-service ClusterIP 10.96.68.23 <none> 80/TCP 23m
mysql-service ClusterIP None <none> 3306/TCP 31h
# 修改前端的service类型
[root@k8s-master01 ~]# kubectl edit svc -n diagramhub diagramhub-frontend-service
把 ClusterIP 修改为 NodePort

发布新功能版本
PS D:\Java-code\DiagramHub\frontend> cd D:\Java-code\DiagramHub\frontend; git tag -a v1.0.1 -m "Release v1.0.1 - 新版本"; git push origin v1.0.1
等待查看流水线

# 查看pod使用的镜像版本
[root@k8s-master01 ~]# kubectl get deployment -n diagramhub -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
diagramhub-backend 1/1 1 1 5h backend 192.168.1.13:8899/diagramhub/diagramhub-backend:1.0.3 app=diagramhub-backend
diagramhub-frontend 1/1 1 1 73m diagramhub-frontend 192.168.1.13:8899/diagramhub/diagramhub-frontend:1.0.1 app=diagramhub-frontend
镜像更新成功
3、使用参数化构建过程
如果新发布的版本有Bug,这里添加指定tag来运行业务pod
在参数化构建这里添加git参数

这流水线这里选择上一个版本构建就回退了

