前言
随着 AI 人工智能、云计算、Docker、K8S 等容器技术等的发展,DevOps 得到了广泛的应用和推广,CI / CD ( 持续集成 / 持续部署 )等概念不断深化和完善。CI/CD 是现代软件开发中的重要实践,CI(Continuous Integration,持续集成)强调开发人员频繁地将代码集成到共享仓库,并且每次集成都会通过自动化构建和测试来验证代码的质量。CD(Continuous Delivery/Deployment,持续交付 / 部署)是在持续集成的基础上,将经过验证的代码自动部署到生产环境或者类生产环境中。本文将以 GitLab+Jenkins+Harbor+Docker 的管理方案为例子,介绍如何通过合理的部署,实现项目全流程的自动化管理。
目录
[一、DevOps 的发展趋势](#一、DevOps 的发展趋势)
[三、GitLab 源代码管理](#三、GitLab 源代码管理)
[四、Docker 与 Harbor 私有仓库](#四、Docker 与 Harbor 私有仓库)
[五、Jenkins整合资源实现 CI/CD 自动化部署](#五、Jenkins整合资源实现 CI/CD 自动化部署)
一、DevOps 的发展趋势
随着 AI 人工智能、云计算、Docker、K8S 等容器技术等的发展,DevOps 得到了广泛的应用和推广,近10 年时间,它在 CI / CD ( 持续集成 / 持续部署 )等概念不断深化和完善。DevOps 的兴起,对于提高软件交付效率、质量和团队协作具有重要意义,具体表现为:
- 提高软件交付效率:通过实现持续集成(CI)和持续交付(CD)流程,开发人员能够迅速地集成代码变更,软件可以可靠地以自动化的方式发布到生产环境中,从而缩短交付周期,快速推出产品和功能更新。
- 提升软件质量:自动化测试包括单元测试、集成测试、功能测试等,确保了代码的质量和稳定性;持续监控提供了实时数据,帮助团队及时识别和解决问题。
- 加强团队协作:打破部门壁垒,强调开发、运维、测试、安全等各个职能部门之间的沟通与协作,摒弃 "竖井式" 的组织架构,形成跨职能的团队。团队成员共同为项目的成功交付负责,建立相互信任和尊重的文化氛围,鼓励积极分享知识和经验,共同解决问题。这种文化的转变有助于更快地发现问题,并以更高效率解决问题。
随着 DevOps 的持续发展,市场上也出现大量 Jenkins、GitLab、CircleCI、Azure Pipelines 等开发工具为 CI/CD 提供应用, 本文将以 GitLab+Jenkins+Harbor+Docker 的管理方案为例子,介绍如何通过合理的部署,实现项目全流程的自动化管理。
GitLab、Jenkins、Harbor、Docker 能分别满足我们在日常的代码管理、自动化部署、镜像管理、容器部署等不同方面的要求,由它们组成的总体部署流程图如下:

二、运行环境介绍
本次实施例子的运行环境共设三台服务器:
192.168.1.101 GitLab 服务器,用于存储源代码
192.168.1.102 Jenkins 服务器,管理 CI /CD 流程,安装 Docker 用于生成本地镜像
192.168.1.103 Harbor 服务器,私有仓库用于保存镜像
192.168.1.104 Docker 用于实现远程部署
关于GitLab、Jenkins、Harbor、Docker 的安装在网上已有大量的例子,此处只作简单介绍,不再详细讲解。
项目以 SpringBoot 为例子,利用 IDEA 的 GitLab Project 工具,直接把源代码链接到 GitLab 进行源代码存档
1 import org.springframework.boot.SpringApplication;
2 import org.springframework.boot.autoconfigure.SpringBootApplication;
3
4 import java.io.IOException;
5
6
7 @SpringBootApplication
8 public class Main {
9 public static void main(String[] args) throws InterruptedException {
10 SpringApplication.run(Main.class, args);
11 }
12 }
13
14 @RestController
15 @RequestMapping("/person")
16 public class PersonService {
17 @RequestMapping("/getAge")
18 public String getAge(String name){
19 return name+"'s age is 26";
20 }
21 }
三、GitLab 源代码管理
GitLab 是一个基于 Git 的开源仓库管理系统,相对于GitHub 公开性的开源仓库,它有私密性,可靠性上的优势。它支持私有化部署,可进行代码的提交、推送、拉取等基本操作,方便开发者对代码进行版本管理,也可创建多个分支,用于并行开发、功能测试和版本发布等。GitLab 本身内置 CI/CD 功能,通过配置文件如.gitlab-ci.yml 可以自动化构建、测试和部署过程。与 Jenkins 相比,它具有轻量化与集成化的优点,但由于可扩展性有一定限制,GitLab 的 CI/CD 更适用于中小型项目的管理或以开发性测试为目标的项目阶段,而 Jenkins 更适用于开发集成测试发布一体化的大型项目管理。
3.1 GitLab 安装
GitLab 的私人版是免费的,可在官网上下载,在国内更推荐使用极狐GitLab 中国发行版(JH) https://www.gitlab.cn/ 对国内的服务器支持更全面, 扫二维码后联系相关工作人员可获取最新版本。

本文把 gitlab 默认安装在 ubuntu 24 服务器 192.168.1.101 上
首先在官网下载Dev包
wget https://packages.gitlab.cn/repository/ubuntu-focal/pool/g/gitlab-jh/gitlab-jh_16.3.3-jh.0_arm64.deb
安装极狐
dpkg -i gitlab-jh_16.3.3-jh.0_amd64.deb
修改配置,加入IP 和商品 0.0.0.0:18081 ,使用本地时区,避免冲突建议不要使用默认端口 8080
1 # 设置外部url
2 external_url 'http://0.0.0.0:18081'
3
4 # 设置时区
5 gitlab_rails['time_zone'] = 'Asia/Shanghai'
运行 sudo gitlab-ctl reconfigure 重新初始化。
运行 sudo ufw allow 18081 开放端口 18081 给外网访问
运行 sudo gitlab-ctl start 启动服务
访问 http://localhost:18081 登录,初次登录,极狐 GitLab 将随机生成一个密码并存储在 /etc/gitlab/initial_root_password 文件中(出于安全原因,24 小时后会自动删除),登记后建议马上修改密码,使用此密码的用户名为root。
成功登录后,创建群组 project,建立项目 myproject2 ,在项目主页可以分别看到项目的 git 路径 git@0.0.0.0:project/myproject2.git 和 HTTP 路径 http://0.0.0.0:18081/project/myproject2.git

点击成员 -- 邀请成员,加入源项目代码开发人员名单

3.2 SSH 连接 GitLab 服务
在本机运行以下命令生成密钥
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): /root/.ssh/id_rsa_jh_gitlab
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa_jh_gitlab
Your public key has been saved in /root/.ssh/id_rsa_jh_gitlab.pub
The key fingerprint is:
SHA256:OuX13mP/EASg/KljuZXrWsq65WIvzFaU9T72tXisVPc jh-gitlab@devops.com
The key's randomart image is:
+---[RSA 3072]----+
| ... |
| . .. . |
| oo . . |
| o. ... |
| .S +. ...|
| +.+ o+ ..+|
| oo.B +..++.E|
| B* * o.o+= |
| oo*Boo .o+o+|
+----[SHA256]-----+
在 C:\Users\admin\.ssh 配置文件夹中可找到生成后的公钥和私钥

打开个人资料按键,点击SSH密钥页面,添加密钥

打开刚生成的 id_rsa.pub 文件,把生成的公钥复制粘贴到页面,最后保存

3.3 IntelliJ IDEA 代码发布
在 IDEA 点击设置-插件,搜索 Gitlab Projects 安装插件

在项目中打开 VCS 菜单,点击 "创建 Git 仓库" 按键

生成 Git 管理器后,点击 "管理远程" 按键

在URL上填入GitLab 上配置的项目HTTP 路径 http://192.168.1.101:18081/project/myproject2.git

在IDEA 点击提交并推送按键,测试一下项目是否能成功提交到GitLab

四、Docker 与 Harbor 私有仓库
4.1 Docker 运行环境
4.1.1 Docker 简介
Docker是一个开源的平台,用于开发、交付和运行应用程序。它能够在Windows,macOS,Linux计算机上运行,并将某一应用程序及其依赖项打包至一个容器中,这些容器可以在任何支持Docker的环境中运行。容器彼此隔离,但可以通过特定的通道相互传递信息。
Docker提供了一个轻量级的虚拟化解决方案。由于运行在同一计算机上的所有容器共享同一个操作系统内核,避免了启动与维护虚拟机的开销。因此相比于传统的虚拟机,Docker容器更加轻便、快速、容易部署。使用Docker,开发者可以避免在两台计算机上运行效果不一致的问题,容器提供了一致的行为,进而使得应用程序在不同环境的计算机上有相同的运行效果。
4.1.2 Docker 的结构组成
Docker平台由以下部分组成:
Docker守护进程(Docker daemon):Docker采用 C/S架构。Docker daemon 作为服务端接受来自客户端的请求,并进行处理(创建、运行、分发容器) 。
Docker客户端(Docker client):Docker 客户端则用于与 Docker 守护进程通信,发送命令以管理容器。Docker采用 C/S架构。客户端和服务端既可以运行在一个机器上,也可通过 socket 或者RESTful API 来进行通信。
Docker daemon一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon交互。
Docker镜像(Docker images):Docker 镜像是用于构建 Docker 容器的静态文件,它包含了应用程序运行所需的所有文件、依赖项和配置信息。Docker 镜像可以从 Docker Hub 或其他镜像仓库中获取,也可以通过 Dockerfile 自定义构建。
Docker容器(Docker container):Docker 容器是 Docker 镜像的运行实例,它包含了应用程序及其依赖项,并在隔离的环境中运行。每个容器都是一个独立的进程,拥有自己的文件系统、网络空间和进程空间。
Docker容器通过Docker镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。
有兴趣学习 Docker 基础操作的朋友可查看 《Docker 基本操作简介》

4.1.3 Docker 安装
目前 Docker 已被广泛应用于企业系统的部署,本次测试中默认把 Docker 部署在 ubuntu 服务器 192.168.1.104 中,
安装教程在官网https://docs.docker.com/engine/install/ubuntu/ 说明文档有详细介绍,其中 $VERSION_CODENAME 绑定所需要的版本号
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
最后执行安装 docker-ce、docker-ce-cli、containerd.io、docker-buildx-plugin、docker-compose-plugin 等数据包
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker 默认的镜像源来源于 DockerHub,但自 2024年5月起 DockerHub 官网的镜像源受到限制,所以完成安装后还需要修改配置文件 /etc/docker/daemon.json 。如果 daemon.json 不存在可以手动新建,然后加入国内可用的镜像源。如果内网的运行环境没加密,可注册一个免检测地址
"insecure-registries": [ "http://192.168.1.104:18084"]
1 {
2 "registry-mirrors": ["https://mirror.aliyuncs.com",
3 "https://mirror.baidubce.com",
4 "https://docker.m.daocloud.io",
5 "https://docker.nju.edu.cn",
6 "https://docker.mirrors.sjtug.sjtu.edu.cn",
7 "https://docker.mirrors.ustc.edu.cn",
8 "https://mirror.iscas.ac.cn",
9 "https://docker.rainbond.cc"
10 ],
11 "log-driver":"json-file",
12 "log-opts": {"max-size":"1g", "max-file":"3"},
13 "live-restore": true,
14 "insecure-registries": ["http://192.168.1.104:18084"]
15 }
完成后重启 docker 即可
1 sudo systemctl daemon-reload
2 sudo systemctl restart docker
输入命令docker -v 可检测 docker 是否已完成安装

4.2 Harbor 私有仓库
Docker 的常用镜像都来源于公开仓库,但在企业部署的系统,出于私密性与安全性考虑,一般都会部署私有仓库来保存常用的镜像。私有仓库是指企业或个人在本地或私有云环境中搭建的 Docker 镜像存储库,用于存储和管理自定义的Docker镜像。与Docker Hub等公共镜像仓库相比,私有仓库具有更高的安全性和可控性,适合存储敏感或内部使用的镜像。目前市面上常用的私有仓库有 Docker Registry、Harbor、Nexus Repository Manager、JFrog Artifactory 等。
下面以 Harbor 仓库为例,介绍一下私有仓库的应用。
4.2.1 Harbor 简介
Harbor 是 VMware 公司开源的企业级 Docker Registry 项目,其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务
Harbor 以 Docker 公司开源的 Registry 为基础,提供了图形管理 UI 、基于角色的访问控制(Role Based AccessControl) 、AD/LDAP 集成、以及审计日志(Auditlogging) 等企业用户需求的功能,同时还原生支持中文
Harbor 的每个组件都是以 Docker 容器的形式构建的,使用 docker-compose 来对它进行部署。用于部署 Harbor 的 docker-compose 模板位于 harbor/docker-compose.yml
4.2.2 Harbor 的功能特点
基于角色控制:用户和仓库都是基于项目进行组织的,而用户在项目中可以拥有不同的权限
基于镜像的复制策略:镜像可以在多个Harbor实例之间进行复制(同步)
支持 LDAP/AD:Harbor 可以集成企业内部已有的 AD/LDAP(类似数据库的一张表),用于对已经存在的用户认证和管理
镜像删除和垃圾回收:镜像可以被删除,也可以回收镜像占用的空间
图形化用户界面:用户可以通过浏览器来浏览,搜索镜像仓库以及对项目进行管理
审计管理:所有针对镜 像仓库的操作都可以被记录追溯,用于审计管理
支持 RESTful API:RESTful API 提供给管理员对于 Harbor 更多的操控, 使得与其它管理软件集成变得更容易
Harbor 和 docker registry的关系:Harbor 实质上是对 docker registry 做了封装,扩展了自己的业务模板
4.2.3 Harbor 的安装
首先在服务器上安装 Docker 和 Docker Compose,然后下载Harbor,测试中默认把 Habor 安装在服务器 192.168.1.103
在官网上点击链接 https://github.com/goharbor/harbor/releases 可下载 Harbor 的最新版 harbor-offline-installer-v2.12.1.tgz ( 官网安装教程: https://goharbor.io/docs/2.12.0/install-config/ )
解压安装包到文件夹/root/ tar -zxvf /root/harbor-offline-installer-v2.1.3.tgz -C /root/
进入 /root/harbor 文件夹可见到配置文件harbor.yml.tmpl,把文件名修改为 harbor.yml 默认配置文件
hostname 为本机IP,port 为使用商品,如果内部服务器没有加密可把 https 内部注释,要不然系统会报错,自行修改管理员密码 harbor_admin_password
1 hostname: 192.168.1.103
2
3 # http related config
4 http:
5 # port for http, default is 80. If https enabled, this port will redirect to https port
6 port: 18081
7 # https related config
8 #https:
9 # https port for harbor, default is 443
10 #port: 443
11 # The path of cert and key files for nginx
12 #certificate: /your/certificate/path
13 #private_key: /your/private/key/path
14 # enable strong ssl ciphers (default: false)
15 # strong_ssl_ciphers: false
16 ......
17 # The initial password of Harbor admin
18 # It only works in first time to install harbor
19 # Remember Change the admin password from UI after launching Harbor.
20 harbor_admin_password: Harbor@12345 ---修改harbor登录密码
21
22 # Harbor DB configuration
23 database:
24 # The password for the root user of Harbor DB. Change this before any production use.
25 password: root@123 ---修改harbor数据库密码
26
27 ......
28 # The default data volume
29 data_volume: /data ---修改harbor仓库数据目录(安装Harbor时会自动创建)
30
31 ......
32 # The directory on your host that store log
33 location: /var/log/harbor
完成配置修改后执行安装
sudo ./install.sh
成功安装后,系统将出现以下页面

成功安装,进入 /root/harbor 文件夹,启动服务
sudo docker-compose start
若 harbor 成功启动可见以下页面

打开 http://192.168.1.103:18081 即可登录 harbor 系统

新建项目 projects 及镜像仓库 myproject2 , 加入常用的用户后,Harbor 的基本配置已完成

五、Jenkins 整合资源实现 CI/CD 自动化部署
Jenkins 的前身是 Hudson,一个采用 Java 编写的开源持续集成工具,由 Sun 公司在 2004 年启动,2005 年发布了第一个版本,并逐渐在持续集成工具领域占据主导地位。Oracle 收购 Sun 后,随着时间的推移,Jenkins 社区迅速壮大,到 2013 年 12 月,Jenkins 受广大开发企业的欢迎,项目成员远超 Hudson,成为 CI/CD 自动化部署领域中使用量占比最高的平台。如今,虽然市面上已有很多工具能实现自动化部署,但 Jenkins 依然是持续集成工具领域的领导者。
5.1 Jenkins 的特点
Jenkins 是一个开源的自动化服务器,可以通过丰富的插件支持构建、部署和自动化的各个方面,它的主要功能包括:
持续集成/持续交付(CI/CD):自动化的软件版本发布和测试项目,确保每次代码变更都能快速且可靠地集成和交付。
构建自动化:支持各种构建工具和语言,如 Maven、Gradle、Ant、以及 shell 脚本等。
测试自动化:能够集成各种测试工具和框架,如 JUnit、TestNG 等,自动化执行测试并生成报告。
部署自动化:自动化应用程序的部署过程,可以集成 Docker、Kubernetes 等工具。
监控和报告:监控外部调用的执行工作,生成详细的构建和测试报告,帮助快速定位和处理问题。
5.2 Jenkins 安装部署
Jenkins 可通过官网下载 https://www.jenkins.io/download/ 相应的版本,通过 java 执行命运直接运行,本次测试默认把 Jenkins 安装在服务器 192.168.1.102 ,并使用端口 18080 运行
java -jar jenkins.war --httpPort=18081
若以 root 身份运行,初始化密码在 /root/.jenkins/secrets/initialAdminPassword 文件中。

第一次进入系统后可默认安装推荐的插件成功,登录后建议修改用户信息和密码。

完成插件安装后,点击 Manage Jenkins - Tools 进入工具页面,绑定 Maven 和 JDK 路径

5.3 Jenkins 链接 GitLab
5.3.1 工具安装
进入Manage Jenkins-Tools 页面,点激 Availale plugins 按键,分别安装 GitLab Plugin ( 连接GitLab ) 和 Generic Webhook Trigger Plugin (用于检测 GitLab 修改的触发器)两个工具,完成安装后,可在 Installed plugins 页面下查看。

5.3.2 建立身份认证凭据
一般的项目需要在 Jenkins 系统中建立2个身份谁凭据,一个是连接 Git 服务器的 SSH 认证,另一个是连接 GitLab 的令牌
首先建立一个 SSH 认证,在 Jenkins 服务器运行 ssh-keygen -t rsa -b 4096 -C "your_email@example.com" 命令生成密钥
1 ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
2 Generating public/private rsa key pair.
3 Enter file in which to save the key (/root/.ssh/id_rsa): /root/.ssh/id_rsa_jh_gitlab
4 Enter passphrase (empty for no passphrase):
5 Enter same passphrase again:
6 Your identification has been saved in /root/.ssh/id_rsa_jh_gitlab
7 Your public key has been saved in /root/.ssh/id_rsa_jh_gitlab.pub
8 The key fingerprint is:
9 SHA256:OuX13mP/EASg/KljuZXrWsq65WIvzFaU9T72tXisVPc jh-gitlab@devops.com
10 The key's randomart image is:
11 +---[RSA 3072]----+
12 | ... |
13 | . .. . |
14 | oo . . |
15 | o. ... |
16 | .S +. ...|
17 | +.+ o+ ..+|
18 | oo.B +..++.E|
19 | B* * o.o+= |
20 | oo*Boo .o+o+|
21 +----[SHA256]-----+
打开 /root/.ssh/id _rsa.pub 复制公钥,然后进入 GitLab。
打开个人资料按键,点击SSH密钥页面,添加公钥。

把生成的 id_rsa.pub 公钥复制粘贴到页面,然后保存

点击 GitLab 的访问令牌页面,添加一个新令牌,设置到期时间,钩选相关的权限

保存令牌后跳转到以下页面,注意令牌只能在第一次创建时可以看到,所以此时必须复制保存令牌

进入 Jenkins 打开 Manager Jenkins - Credentials-System-Global credentials 页面,添加GitLab API token 凭据

选择 GitLab API token,把 GitLab 中复制的令牌填入 API token 后,再保存

成功保存 GitLab API token 凭据后,再新建另一个 SSH 凭据,选择类型为 SSH Username with private key , 打开刚生成的 /root/.ssh/id _rsa 私钥,复制后填入 Private Key 中

保存后可在Manage Jenkins-Credentials 页面看到刚生成的2个凭据

5.3.3 测试 GitLab连接
打开Manage Jenkins - System 页面,找到 GitLab connection 选项,在Connection name 填入自定的连接名称 GitLab_Connection, 在 GitLab host URL 中填入 GitLab 服务器的 IP 地址 http:// 192.168.1.101: 18081,在 Gredentials 中选择刚刚建立的 GitLab API token 身份认证,按下 Test Connection 测试一下。

若连接不成功,不用慌,只因为是第一次连接,所以需要先手动进行 ssh 登录。
打开命令窗口,输入 ssh root@192.168.1.101,输入密码登录成功后,连接会自动保存,后续操作无需再次登录。
此时再点击 Test Connection 测试,系统会显示链接成功

5.4 Jenkins 常用的项目类型 Pipeline 流水线与 Freestyle 自由风格
5.4.1 Pipeline 与 Freestyle 的区别
目前 Jenkins 项目主要有两种类型,流水线 Pipeline 与 自由风格 Freestyle :
- 流水线 Pipeline
Pipeline 流水线是在Jenkins 2.0 版本开始得到重视,灵活可变性的优点让 Pipeline 逐渐成为构建 Jenkins 部署项目的主流方式,它使用 Jenkinsfile 文件来定义整个构建流程。
Jenkinsfile是一个文本文件,使用 Groovy 语言的DSL(领域特定语言),可以定义构建的各个阶段和步骤,其特点是可以通过简单的脚本灵活地控制项目构建的整个生命周期。
Pipeline 任务支持复杂的构建流程,包括多个阶段和步骤,以及并行执行等高级功能。
Pipeline 任务可以很容易地与源代码管理系统集成,支持持续集成(CI)和持续交付(CD)。
- 自由风格 Freestyle
自由风格项目是Jenkins早期版本中的主要任务类型,允许用户通过Jenkins的Web界面配置构建步骤。
这种类型的项目没有固定的构建流程定义方式,用户可以手动添加构建步骤、配置插件和参数等。
自由风格项目适合快速设置简单的构建任务,比较适合新手使用,但对于复杂的构建流程,其灵活性就不如 Pipeline 任务灵活和强大。
两者的主要区别包括:
定义方式:Pipeline任务通过Jenkinsfile定义,而自由风格项目通过Jenkins的Web界面配置。
灵活性和扩展性:Pipeline任务更加灵活,支持复杂的构建流程和高级功能,如并行执行和模板化。
集成度:Pipeline任务更容易与源代码管理系统集成,实现自动化的CI/CD流程。
学习和使用难度:Pipeline任务可能需要用户学习 Jenkinsfile 的语法和DSL,而自由风格项目则更直观,易于上手。
本文将使用 Pipeline 方式来介绍一下项目的部署过程。
5.4.2 Pipeline 脚本的结构
pipeline 使用的是 Groovy 脚本,它有两种编写方式:脚本式和声名式

Jenkins Pipeline 脚本结构主要由以下几个部分组成:
- Node:定义了执行Pipeline阶段和步骤的代理标签或者是节点。
- Pipeline:包含了一系列的阶段(Stages)。
- Stages:包含了一系列的步骤(Steps)。
- Steps:定义了实际执行的命令或操作。
下面是一个简单的Jenkins Pipeline 脚本结构示例:
1 #声明式语法的pipeline脚本
2 pipeline{ # 表示声明它是一个pipeline脚本,最顶级的节点
3 agent any # 固定写法 - 表示在哪个节点上面去构建;any表示由jenkins自动分配节点
4 stages{ # 阶段性任务 - 所有的步骤都在这里面
5 stage("拉取脚本"){ # 任务
6 steps{ } # 步骤
7 }
8 stage("执行用例"){ # 任务
9 steps{ } # 步骤
10 }
11
12 }
13 post{ } # 表示在任务完成之后做的操作 - 如:发送邮件
14
15 }
常用变量:
- parameters 参数配置
功能:定义构建时用户可以输入的参数。
常见配置:
string(name: 'ENV', defaultValue: 'dev', description: 'Environment to deploy'):文本输入。
booleanParam(name: 'DEPLOY', defaultValue: true, description: '是否部署'):布尔值输入。
choice(name: 'BRANCH', choices: ['dev', 'qa', 'prod'], description: '分支选择'):选择输入。
1 pipeline{
2 agent any
3 parameters {
4 string (name: 'namespace' , defaultValue: 'projects')
5 string (name: 'projectName', defaultValue: 'myproject2')
6 string (name: 'version', defaultValue: 'v1.0')
7 string (name: 'registryIP', defaultValue: '192.168.1.102:18081')
8 }
9 .........
10 }
- environment 环境变量
功能:设置环境变量,可以在整个Pipeline中使用。
1 environment {
2 ENV_VAR = 'value'
3 PATH = "/usr/bin:/usr/local/bin:${PATH}"
4 }
- options
功能:配置Pipeline的高级选项,比如超时设置、丢弃旧的构建等。
常见配置:
timeout(time: 1, unit: 'HOURS'):将Pipeline的执行限制在1小时内。
disableConcurrentBuilds():防止并发构建。
buildDiscarder(logRotator(numToKeepStr: '5')):只保留5个构建记录。
1 options {
2 timestamps()
3 timeout(time: 4, unit: 'HOURS')
4 buildDiscarder(logRotator(artifactDaysToKeepStr: '100', artifactNumToKeepStr: '100', daysToKeepStr: '200', numToKeepStr: '200'))
5 copyArtifactPermission('/DS_BFFD/FFGFGGDY/ssffgd_ddfddf-MT');
6 }
5.4.3 部署 一个 Jenkins 项目进行 Generic Webhook Trigger Plugin 触发器测试
新建一个 Pipeline 项目,在 GitLab Connection 选项中选择刚刚在 Manage Jenkins-System 中建好的 GitLab_Connection 连接。

在触发器中勾选 " Build when a change is pushed to GitLab. GitLab webhook URL: http://192.168.1.102:18080/project/myproject2 " 选择

在高级选择中选择 Secret token 选项,点击 Generate 选择生成随机数

复制随机数后打开 GitLab 上的对应项目,新建一个Webhooks

在 URL 中填入 Jenkins 项目的地址,在 Secret 令牌中填入刚刚在 Jenkins 中复制的令牌

下面触发来源代表触发把信息从 GitLab 推送 Jenkins 的事件,可按需求自行勾选。

保存后回到 Jenkins, 在流水线中选择 Pipeline script 脚本方式,在选择框里选择一个 Hello World 作为测试。
后面如果对编写脚本不熟悉,可以点击下面的 "流水线语法" 进行查找。

万事俱备,保存后尝试在 IDEA 提交代码到 Git 或在 GitLab 按下测试按钮。

此时 Jenkins 会被事件触发运行脚本,可以看到以下的结果。

项目触发成功,代表 Jenkins 已成功与 Gitlab 连接。此时在 Jenkins 服务器的安装目录下 (本次测试项目是在 /root/.jenkins/workspace)找到项目源代码。
5.5 编写 Groovy 脚本把镜像发送到 Harbor 私有库
小试牛刀后,正式开始编写 Jenkinsfile 脚本,Jenkins 通过 GitLab 拉取源代码,然后生成 Docker 镜像,并把镜像推送到 Harbor 私有仓库 。
首先在 Jenkins 项目流水线定中选择 Pipeline script from SCM 类型, SCM 选项中选择 Git , 在 Repository URL 中填入项目的 git 克隆路径 git@19.168.1.101:project/myproject2.git,在 Credentials 中选择刚建好的 SSH 身份认证。

最后选择好 Git 的分支和 Jenkinsfile 脚本在项目中的相对路径(一般在项目的根目录下)。

完成配置后开始编写项目的 Jenkinsfile 脚本,编写好 parameters 后可以 jenkins 项目中看到参数的默认值,每次发布时可以根据需求更改。其中 namespace 是 harbor 的空间名,projectName 是 harbor 的项目名称,version 是当前发布的版本号, registryIP 是 harbor 服务器的IP地址。

首先在 Jenkins 运行 mvn 重新生成源代码,根据 Jenkins 的环境变量获取所生成 jar 的绝对路径,然后在 Jenkins 服务器中生成 Docker 镜像,并把镜像推向 Harbor 私有仓库。
最后的 publisher 函数是在 Docker 远程服务器中从 Harbor 拉下镜像,生成容器,此函数将在下一节详细介绍。
1 pipeline{
2 agent any
3 parameters {
4 string (name: 'namespace' , defaultValue: 'projects') // harbor 空间名
5 string (name: 'projectName', defaultValue: 'myproject2') //harbor 项目名称
6 string (name: 'version', defaultValue: 'v1.0') //默认版本号
7 string (name: 'registryIP', defaultValue: '192.168.1.103:18081') //harbor 服务器 IP
8 string (name: 'dockerUser', defaultValue: 'admin') //docker 远程登录名
9 string (name: 'dockerPass', defaultValue: '12345678') //docker 登录密码
10 string (name: 'dockerServer', defaultValue: 'http://192.168.1.104:18081') //docker 服务器IP
11 }
12
13 stages {
14 //在 jenkins 服务器重要生成 jar
15 stage('build_jar') {
16 steps {
17 sh 'mvn clean'
18 sh 'mvn install'
19 }
20 }
21
22 stage('build_image') {
23 steps {
24 script {
25 //获取 Jenkins 的安装路径
26 jenkinsHome = env.JENKINS_HOME
27 //获取 Jenkins 的当前工程名称
28 jobName = env.JOB_NAME
29 //获取 Jenkins 的项目URL
30 jenkinsUrl = env.JENKINS_URL
31 //组成 Jenkinsfile 的路径
32 jenkinsfilePath = "${jenkinsHome}/workspace/${jobName}/"
33
34 //测试性输出
35 echo '------------jenkinsfile running------------'
36 echo "当前项目的标准文件夹是: ${jenkinsfilePath}"
37 echo "$jenkinsUrl"
38 echo "Workspace:${WORKSPACE}"
39 echo "PWD:${pwd()}"
40
41 //在Jenkins服务器测试项目包是否能正常运行
42 //sh "java -jar target/MyProject2-v1.0.jar"
43
44 //生成镜像
45 buildImage()
46 }
47 }
48 }
49
50 stage('push') {
51 steps {
52 script {
53 //调用函数把镜像推送到远程私有库Harbor
54 pushImage()
55 //链接Harbor,下拉镜像,生成容器
56 publisher()
57 }
58 }
59 }
60 }
61 }
62
63 // harbor 私有库的镜像名
64 def registryImage=null
65 // jenkins服务器的镜像名
66 def image=null
67
68 // 生成镜像
69 def buildImage(){
70 //避免镜像名重复生成随机数
71 randomInt = new Random().nextInt(9999-1000)+1000
72 //根据参数生成镜像名
73 imageName="${params.namespace}/${params.projectName}:${params.version}.$randomInt"
74 //生成镜像
75 sh "docker build -t $imageName . ;"
76 //保存本地镜像名
77 this.image=imageName
78 }
79
80 // 把镜像推送到 harbor
81 def pushImage(){
82 //harbor私有库镜像地址
83 this.registryImage="${params.registryIP}/${this.image}"
84 //打印 harbor 镜像地址
85 println(this.registryImage)
86 //标记私有仓库镜像
87 sh "docker tag ${this.image} $registryImage ;"
88 //推送镜像到 harbor
89 sh "docker push $registryImage; "
90 //推送镜像后清除本地镜像
91 sh "docker rmi $registryImage"
92 }
93
94 def publisher(){
95 //execCommand 请注意:执行*.sh文件前,先使用 chmod 开放权限,否则第一次执行jenkins会报权限不足
96 sshPublisher(publishers: [
97 sshPublisherDesc(configName: 'Docker_Publish_Server', transfers: [
98 sshTransfer(cleanRemote: false,
99 excludes: '',
100 execCommand: "chmod +x /root/remote/command.sh; /root/remote/command.sh $registryImage ${params.projectName} ${params.dockerUser} ${params.dockerPass} ${params.dockerServer}",
101 execTimeout: 120000,
102 flatten: false,
103 makeEmptyDirs: false,
104 noDefaultExcludes: false,
105 patternSeparator: '[, ]+',
106 remoteDirectory: '',
107 remoteDirectorySDF: false,
108 removePrefix: '',
109 sourceFiles: 'command.sh')
110 ], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)
111 ])
112 }
成功运行后可以在 Jenkins 服务器中看到生成的 Docker 镜像记录

成功运行后可在 Harbor 服务器中可以看到上传的镜像

5.6 在 Docker 服务器下拉镜像部署容器
5.6.1 Publish Over SSH 链接部署
在上面的代码中可以看 sshPublisher 函数,它是 Publilsh Over SSH 插件里的一个函数,作用是让 Jenkins 远程调用 docker 服务器 192.168.1.104 ,在 Harbor 私有仓库中下拉镜像,生成容器。
首先需要在 Jenkins 下载 Publilsh Over SSH 插件

然后进入 Manage Jenkins - System 页面进行配置,注意 SSH Severs 配置中 Name 名称,后面将在sshPublisher 函数中用到, Hostname 是 Docker 服务器IP ,Username 是服务器登录名,Remote Directory 为文件的默认目录。

完成后还需在docker 服务器中运行 ssh-keygen -t rsa -b 4096 -C "your_email@example.com" 命令生成密钥,然后点击"高级"按键,勾选身份认识方式,在 Key 中填入 Docker 服务器的私钥。
注意运行前先在 Jenkins 服务器运行命令 ssh root@192.168.1.104, 手动链接一下 Docker 服务器,避免运行函数时链接失败。

最后点击 Test Configuration 按键,只要密钥输入正确,链接测试将显示 Success

5.6.2 sshPublisher 函数说明
Publish Over SSH 插件允许在构建过程中通过 SSH 将文件或目录发布到远程服务器上,它支持 SCP 和 SFTP 协议,并可以在传输完成后执行远程命令。而 sshPublisher 函数是 Publish Over SSH 插件里的一个函数,它可以根据 Jenkins 中的配置连接远程的 Docker 服务器,并在服务器中执行脚本命令,远程操作控制 Docker。
sshPulisher 函数:
1 sshPublisher(publishers: [
2 sshPublisherDesc(configName: 'myRemoteServer', transfers: [
3 sshTransfer(
4 cleanRemote: false,
5 excludes: '',
6 execCommand: '',
7 execTimeout: 120000,
8 flatten: false,
9 makeEmptyDirs: false,
10 noDefaultExcludes: false,
11 patternSeparator: '[, ]+',
12 remoteDirectory: '/var/www/html',
13 remoteDirectorySDF: false,
14 removePrefix: '',
15 sourceFiles: '**/*'
16 )
17 ])
18 ])
参数说明:
- configName
说明:配置名称,注意此处填入的是上一节介绍的 Publilsh Over SSH 插件配置中 SSH Servers 的 Name 值,填错则无法链接
- remoteDirectory
说明:这是远程服务器上的目标目录,构建产物将会被上传到这个目录下,它指定了文件在远程服务器上的存储位置。如果为空,在上一节 Remote Directory 的路径就是它的默认值。
示例:remoteDirectory: '/var/www/app'意味着文件将被发布到远程服务器的/var/www/app目录中。
- sourceFiles
说明:用于指定要上传的本地文件或目录的模式。可以使用通配符来匹配多个文件,例如**/*.jar会匹配所有子目录下的.jar文件。
示例:sourceFiles: 'dist/*'将上传本地dist目录下的所有文件。
- execCommand
说明:在文件传输完成后,可以在远程服务器上执行一个命令。这对于解压文件、重启服务等操作非常有用。例如,在上传了一个压缩包后,可以使用这个命令在远程服务器上解压它。
示例:execCommand: 'unzip myapp.zip && chmod +x myapp.sh &&./myapp.sh'将在远程服务器上解压myapp.zip文件,然后赋予myapp.sh可执行权限,并运行这个脚本。
- execTimeout
说明:执行命令的过期时间,默认为 120000 毫秒
- flatten
说明:布尔值参数,用于决定是否将上传的文件结构扁平化。如果设置为true,所有文件将直接上传到remoteDirectory下,而不会保留本地的目录结构。
示例:flatten: true,假设本地有src/main/java/MyApp.class文件,上传后将直接位于remoteDirectory下,而不是remoteDirectory/src/main/java/MyApp.class。
- cleanRemote
说明:布尔值参数,用于指定是否在上传文件之前清空远程目录。如果设置为true,在上传之前,remoteDirectory中的所有现有文件将被删除。这在确保远程服务器上的文件是最新版本,并且没有旧文件残留时非常有用,但要谨慎使用,以免误删重要文件。
示例:cleanRemote: true将在每次上传之前清空远程目录。
- excludes
说明:用于指定在上传过程中要排除的文件或目录。使用模式匹配的方式来排除文件,类似于sourceFiles的反向操作。
execCommand 代表要在 Docker 服务器中要执行的命令,一般命令短可以直接在此编写,如果命令过长可以放在 *.sh 文件中执行,在 sourceFiles 选择中填入 command.sh 后,运行 execCommand 时就会执行 command.sh 脚本。
在代码中可以看到 execCommand 内容表示在 Docker 服务器执行 chmod 命令,获取文件的所有权(注意:如果没有此输入,初次运行时系统可能显示"无此权限执行 sh 命令")。
1 execCommand: "chmod +x /root/remote/command.sh; /root/remote/command.sh $registryImage ${params.projectName} ${params.dockerUser} ${params.dockerPass} ${params.dockerServer}"
在运行command.sh 脚本时绑定输入4个参数,此参数正在 pipeline 中的 parameters 参数值。注意 commnad.sh 脚本文件的默认路径是 Remote Directory 中所配置的路径。
command.sh 脚本
#! /bin/sh
#私有仓库镜像名
registryImage=$1
#容器默认名称
projectName=$2
#docker 用户名
username=$3
#docker 密码
password=$4
#docker服务器IP
server=$5
echo "--------------------docker run------------------------------"
#链接部署服务器
sudo docker login -n ${username} -p ${password} ${server}
#拉取私有库镜像
sudo docker pull ${registryImage}
#停止本地已在运行的容器
sudo docker stop ${projectName}
#删除本地容器
sudo docker rm ${projectName}
#重新生成容器
sudo docker run -d -p 0.0.0.0:18083:8083 --network=bridge --name ${projectName} ${registryImage}
根据输入的参数值,docker 服务器可以获取 harbor 服务器用户名密码,经过 docker 登录后下拉镜像,最后生成容器。
万事俱备,只欠东风。此时,只需要在 IDEA 提交代码,系统就会触发 Jenkins ,实现 GitLab -> Jenkins -> Harbor -> Docker 的全流程自动部署, 通过 build 窗口的记录中查看到 Console Output 的输出结果。

本章总结
本章分别介绍了 GitLab 、 Jenkins 、 Harbor 、 Docker 服务器部署的详细流程及注意事项,通过整合 GitLab + Jenkins + Harbor + Docker 可以构建一个完整的、高效的 CI/CD 流水线,实现从代码提交到应用部署的全自动化流程,有效提高软件开发和交付的效率和质量。 它涵盖了从开发、测试到部署的整个软件生命周期,并且促进了团队协作和资源的有效利用。
作者:风尘浪子
https://www.cnblogs.com/leslies2/p/18632905
原创作品,转载时请注明作者及出处