DevOps 入门到实战:从敏捷开发、持续集成到 GitLab + Jenkins + Docker 自动化部署
摘要:DevOps 到底在解决什么问题?持续集成(CI)为什么是敏捷开发的基石?本文先讲透软件开发生命周期、瀑布模型、敏捷开发与持续集成的概念,再以一套完整的实战环境(IDEA + GitLab + Jenkins + Docker)带你走通"代码提交 → 自动拉取 → 编译打包 → 推送目标服务器 → 容器化运行 → 参数化发布"的全流程,让零基础读者也能搭建出自己的第一条 CI/CD 流水线。
第一部分:DevOps 概念
一、软件开发生命周期(SDLC)
软件开发生命周期又叫 SDLC(Software Development Life Cycle),是集合了计划、开发、测试和部署过程的集合。经典 SDLC 包含五个阶段:
| 阶段 | 说明 |
|---|---|
| 需求分析 | 第一阶段。根据项目需求做可行性分析,信息可以来自公司内部或客户,也可能是对现有项目的改善;同时要分析预算、收益与布局 |
| 设计 | 第二阶段。确定系统架构与"满意状态"(做成什么样子、有哪些功能),并用图表、布局或文档形式输出项目计划 |
| 实现 | 第三阶段。项目经理创建并分配任务,开发者按设计阶段的目标编写代码,周期可能长达数月 |
| 测试 | 第四阶段。测试人员进行功能测试、代码测试、压力测试等 |
| 进化 | 最后阶段。根据用户反馈持续改进与维护,包括功能修改、Bug 修复、功能增加 |
二、软件开发瀑布模型:经典但已过时
瀑布模型(Waterfall Model)是最著名、最常使用的软件开发模型,由制造业流程衍生而来——一系列开发过程像生产线一样在单一方向上流动。在模型创建之初,很多内容全靠开发人员猜测,因此只适用于简单软件开发,已不适合现代开发。
瀑布模型的优缺点:
| 优势 | 劣势 |
|---|---|
| 简单易用、容易理解 | 阶段划分完全固定,阶段之间产生大量文档,极大增加工作量 |
| 前一阶段完成,只需关注后续阶段 | 模型线性推进,用户到项目末期才能看到成果,开发风险高 |
| 提供按阶段划分的检查节点 | 不适用用户需求的变化 |
一句话:瀑布模型把软件开发当成"一次性的单向流水线",需求一旦变化,代价巨大。
三、软件的敏捷开发
3.1 什么是敏捷开发?
敏捷开发(Agile Development)的核心是迭代开发(Iterative Development)与增量开发(Incremental Development)。
3.2 何为迭代开发?
传统开发用一个"大周期"(比如一年)完成一次"大开发";迭代开发把过程拆成多个"小周期",每次小开发都走同样的流程,看上去像在重复相同的步骤。
经典案例——SpaceX:想造大推力火箭送人类上火星,但没有一开始就造大火箭,而是先造最简陋的 Falcon 1,前三次发射接连爆炸,第四次才成功入轨;随后开发中型火箭 Falcon 9,九年发射 70 次;最后才开发 Falcon 重型火箭。如果 SpaceX 不做迭代开发,可能至今还无法上天。
3.3 何为增量开发?
软件的每个版本都新增一个用户可以感知的完整功能,即按照新增功能来划分迭代。
类比:房产公司开发一个 10 栋楼的小区。增量开发是第一个迭代交付 1 号楼、第二个迭代交付 2 号楼——每个迭代交付一栋完整可用的楼,而不是第一个迭代挖好 10 栋楼的地基、第二个迭代盖骨架、第三个迭代架屋顶。
3.4 敏捷开发如何迭代?
虽然敏捷把开发拆成多个迭代,但每次迭代仍然是一个完整的软件开发周期,必须按照软件工程的方法论走正规的流程管理:需求 → 设计 → 开发 → 测试 → 交付,如此循环。
3.5 敏捷开发的好处
好处一:早期交付,降低成本。
沿用房产公司的例子:瀑布模式两年后一次性交付 10 栋楼,意味着两年后才能回款;敏捷模式六个月交付 1 号楼,之后每两个月交付一栋,半年回款 10%,后续每月都有现金流,资金压力大大减轻。
好处二:及时了解市场,降低产品不适用的风险。
想一想哪种损失更小:10 栋楼都盖好才发现卖不出去,还是盖好第 1 栋楼就发现卖不出去,从而改进甚至停建后面 9 栋?
四、什么是持续集成(CI)?
持续集成(Continuous Integration,CI)指的是频繁地(一天多次)将代码集成到主干。
持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。
核心措施:代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。通过持续集成,团队可以快速从一个功能切换到另一个功能——敏捷开发很大一部分都要归功于持续集成。
4.1 CI 的完整流程
根据持续集成的设计,代码从提交到生产通常经历以下几步:
- 提交(Commit):开发者向代码仓库提交代码,所有后续步骤都始于本地代码的一次 commit;
- 测试(第一轮):代码仓库为 commit 配置钩子(hook),提交代码或合并进主干即自动跑测试;
- 构建(Build):通过第一轮测试后代码可合并进主干。构建指把源码转换为可运行的实际代码,例如安装依赖、配置样式表/JS 脚本/图片等资源;
- 测试(第二轮):构建完成后进行第二轮测试。如果第一轮已覆盖全部测试,第二轮可省略(此时构建步骤要移到第一轮测试之前);
- 部署(Deploy):通过测试的代码成为可直接部署的版本(artifact),打包存档并发布到生产服务器;
- 回滚(Rollback):当前版本出问题时,回滚到上一版本的构建结果,最简单做法是修改符号链接指向上一个版本的目录。
4.2 持续集成的组成要素
- 一个自动构建过程:从检出代码、编译构建、运行测试、结果记录到测试统计全部自动完成,无需人工干预;
- 一个代码存储库:用版本控制软件(SVN 或 Git)保障代码可维护性,同时作为构建素材库;
- 一个持续集成服务器:Jenkins 就是配置简单、使用方便的持续集成服务器。
4.3 持续集成的好处
- 降低风险:持续构建、编译和测试,能很早期发现问题,修复代价小;
- 对系统健康持续检查,减少发布风险带来的问题;
- 减少重复性工作,解放人工;
- 持续部署:随时提供可部署单元包;
- 持续交付可供使用的版本;
- 增强团队信心。
第二部分:CI/CD 实战
五、实战环境总览
概念理解了,接下来动手搭建一套完整的 CI/CD 环境。环境由 4 台机器组成:
| 配置 | 主机名 | IP | 软件 |
|---|---|---|---|
| 2核 2G 200G | gitlab | 192.168.100.153 | GitLab |
| 2核 4G 200G | Jenkins | 192.168.100.154 | JDK 1.8、Maven、Docker、Jenkins |
| 2核 4G 200G | web | 192.168.100.155 | Docker |
| 2核 8G 400G | Windows | 192.168.100.132 | JDK 1.8、Maven 3.8.4、IDEA 2019.3.1、Git |
整套流水线的数据流:
开发者(Windows + IDEA 写代码)
│ git push
▼
GitLab(代码仓库,192.168.100.153)
│ Jenkins 定时/手动拉取
▼
Jenkins(CI 服务器,192.168.100.154:编译打包)
│ Publish over SSH 推送 jar
▼
Web 目标服务器(192.168.100.155:Docker 运行容器)
│ 浏览器访问
▼
http://192.168.100.155:8080/test
六、Windows 开发环境搭建
6.1 安装 JDK
JDK(Java Development Kit) 是提供给 Java 开发者的一套完整软件开发环境。先理清三个概念的关系:
JDK = JRE + 开发工具 = JVM + 核心类库 + 开发工具
- JVM(Java Virtual Machine):执行字节码的引擎,提供内存管理、垃圾回收、安全机制等运行时环境,是 JRE 的核心组件;
- JRE(Java Runtime Environment):Java 程序的运行环境,包含 JVM + Java 核心类库;只有 JRE 能运行 Java 程序,但不能开发(编译)新程序;
- JDK:Java 程序开发套件,包含 JRE + 开发工具(主要是 javac 等),开发 Java 程序必须安装 JDK。
安装过程很简单:下载 JDK 1.8 安装包,保持默认路径,一路"下一步"即可完成。
6.2 安装并配置 Maven
Apache Maven 是 Java 生态中最核心、应用最广泛的项目构建和依赖管理工具。它基于 POM(Project Object Model,项目对象模型),通过核心配置文件 pom.xml 标准化项目的构建过程、报告和文档信息,并管理项目依赖。
核心概念:
- pom.xml:Maven 项目的"心脏和灵魂",所有配置(项目信息、依赖、构建配置、插件配置等)都在这里;
- 依赖(Dependency):项目所依赖的外部库,通过坐标声明;
- 仓库(Repository):
- 本地仓库:开发者机器上的目录(默认
~/.m2/repository),缓存从远程下载的依赖; - 中央仓库:Maven 社区维护的默认公共仓库;
- 私服(如 Nexus、Artifactory):公司内部仓库,用于缓存中央仓库构件加速构建、部署公司私有构件、控制对外部仓库的访问;
- 本地仓库:开发者机器上的目录(默认
- 构建生命周期(Lifecycle):一组有序的构建阶段;
- 阶段(Phase):生命周期中的具体步骤;
- 插件(Plugin)与目标(Goal):插件提供具体构建功能,一个插件可有多个目标;执行命令本质是调用插件目标。
配置阿里云镜像:
把 Maven 压缩包解压到 E 盘后,编辑 conf/settings.xml:
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
注意:镜像配置必须放在
<!-- -->注释符号外面,否则不生效。
6.3 安装 IDEA 并创建 Spring Boot 项目
IntelliJ IDEA 是 JetBrains 开发的智能 Java IDE,分付费旗舰版(全功能)和免费社区版(基础 Java/Kotlin 开发)。安装路径可选 E 盘,按向导完成安装。
新建项目:
- 选择 Spring Initializr 创建 Web 项目;
- 修改 Group,Type 设置为 Maven;
- 选择 Web 中的 Spring Web 模板;
- 设置项目名称
demo_web与路径; - 创建完成后删除多余配置项。
编写测试接口:
在 com.chenyan.demo.controller 包下新建 TestController:
package com.chenyan.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "跟陈岩学云原生";
}
}
修改 pom.xml:
注意依赖名称是 spring-boot-starter-web(没有 mvc):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
等待依赖下载完成后,启动应用,浏览器访问验证:
http://localhost:8080/test
常见问题:Error:java: 无效的源发行版: 13
原因是 IDEA 的 Java 编译级别与本地 JDK 版本不一致。解决方法:
File → Settings → Build, Execution, Deployment → Compiler → Java Compiler,将字节码版本改为 1.8 并 Apply;File → Project Structure,检查 Project SDK、Project language level、Modules 的 language level 是否都设置为 8。
修改完成后即可正常启动。
七、GitLab 私有代码仓库部署
7.1 为什么用 GitLab?
GitLab 是开源(MIT 协议)的仓库管理系统,基于 Git 搭建 Web 服务。与 GitHub 类似,可以注册用户、提交代码、添加 SSH Key;区别在于 GitLab 可以部署到自己的服务器上,数据库等一切信息都掌握在自己手里,适合团队内部协作开发。简单说:GitLab 是"私有化的 GitHub"。
7.2 CentOS 7 部署 GitLab(gitlab-ce 12.4.2)
第一步:安装依赖并启动服务
[root@gitlab ~]# yum -y install policycoreutils openssh-server openssh-clients postfix
# 启动 ssh
[root@gitlab ~]# systemctl enable sshd --now
# postfix 支持 GitLab 发信功能
[root@gitlab ~]# systemctl enable postfix --now
# 开放 ssh 与 http
[root@gitlab ~]# firewall-cmd --add-service=ssh --permanent
[root@gitlab ~]# firewall-cmd --add-service=http --permanent
[root@gitlab ~]# firewall-cmd --reload
第二步:下载安装 GitLab
# 使用清华镜像下载
[root@gitlab ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el6/gitlab-ce-12.4.2-ce.0.el6.x86_64.rpm
[root@gitlab ~]# rpm -ivh gitlab-ce-12.4.2-ce.0.el6.x86_64.rpm
第三步:修改配置并启动
[root@gitlab ~]# vim /etc/gitlab/gitlab.rb
# 第 23 行:修改外部访问地址
external_url 'http://192.168.100.153:82'
# 第 1112 行:修改 nginx 监听端口
nginx['listen_port'] = 82
# 重载配置并重启
[root@gitlab ~]# gitlab-ctl reconfigure
[root@gitlab ~]# gitlab-ctl restart
# 放行 82 端口
[root@gitlab ~]# firewall-cmd --zone=public --add-port=82/tcp --permanent
[root@gitlab ~]# firewall-cmd --reload
启动成功后,浏览器访问
http://192.168.100.153:82,首次会进入修改管理员 root 密码页面(示例密码:abcd1234),改完后即可登录。
7.3 GitLab 基础管理:组、用户与项目
创建组:
在 GitLab 中创建组(Group),可见性类型设置为 私有(Private)。
创建用户:
为保证账户安全,创建一个新用户并设置角色:
- Regular(普通用户):可以访问属于他的项目;
- Admin(管理员):可以访问所有项目。
创建完用户后立即修改密码,再把用户加入组(Group → Members)并赋予角色。
组内 5 种角色权限速查:
| 角色 | 权限 |
|---|---|
| Guest | 可创建 issue、发表评论,不能读写版本库 |
| Reporter | 可克隆代码,不能提交(适合 QA、PM) |
| Developer | 可克隆、开发、提交、push(普通开发) |
| Maintainer | 可创建项目、添加 tag、保护分支、添加成员、编辑项目(核心开发) |
| Owner | 可设置项目可见性、删除/迁移项目、管理组成员(开发组组长) |
创建项目:
以新用户登录 GitLab,在所属组内创建项目,命名项目并将类型设为私有。
八、把源码推送到 GitLab 仓库
第一步:Windows 安装 Git
安装 Git for Windows(保持默认路径),注意安装过程中取消勾选多余选项(示例为全部去掉)。这样右键菜单与 IDEA 中才会干净地出现 Git 操作。
第二步:IDEA 集成 Git
在 IDEA 的 Settings → Version Control → Git 中指定 Git 可执行文件路径,左下角出现 Git 集成成功提示。
第三步:本地提交(Add → Commit)
选择整个项目,右键出现 Git 菜单:
- 先
+ Add将文件加入缓存; - 再
Commit Directory提交到本地 Git,填写版本说明与邮箱; - 左下角显示提交成功信息。
第四步:推送到远程 GitLab
- 在 GitLab 对应项目页面复制 HTTP 仓库地址;
- IDEA 中选择 Push,把 URL 粘贴进去;
- 输入 GitLab 用户身份验证(私有项目需要);
- 验证成功后直接 Push,右下角出现成功提示;
- 回到 GitLab 服务器,在项目页面即可看到提交内容。
九、Jenkins CI 服务器部署
9.1 认识 Jenkins
Jenkins 是开源的、基于 Java 的持续集成和持续交付/部署工具,用于自动化构建、测试和部署代码。
核心目标:自动化软件开发流程
- 持续集成(CI):频繁将开发人员的代码变更合并到共享主干(如 Git 仓库),并立即自动构建和测试,尽早发现集成错误;
- 持续交付(CD):确保每次代码变更都能通过自动化流程(构建、测试、部署到类生产环境)被安全、快速、可靠地发布;
- 持续部署:在持续交付基础上更进一步,自动把验证通过的变更部署到生产环境(通常需要严格的自动化测试和监控)。
核心工作原理:Pipeline(流水线)
- Jenkins 将软件交付建模为一系列阶段(Stage),每个阶段包含多个步骤(Step);
- 典型阶段:检出代码 → 编译构建 → 单元测试 → 集成测试 → 代码质量分析 → 构建部署包 → 部署测试环境 → 部署生产环境;
- Pipeline 通常用 Jenkinsfile(文本文件)编写并存在源码仓库中,实现"Pipeline as Code"。
主要组件:
- Jenkins Server/Controller(主节点):提供 Web UI、管理配置、调度构建作业;
- Jenkins Agent/Node(执行器):实际执行构建任务的计算资源,主节点将任务分发给它;
- Project/Job:定义构建任务的具体内容和步骤;
- Pipeline:声明式或脚本式语法定义的多阶段构建过程;
- Artifact:构建产物(JAR、WAR、Docker 镜像等);
- Plugin:扩展 Jenkins 功能的附加模块(插件生态超过 1800 个)。
9.2 Jenkins 预备环境
# 关闭防火墙
[root@jenkins ~]# systemctl disable firewalld.service --now
# 关闭 SELinux(临时 + 永久)
[root@jenkins ~]# setenforce 0
[root@jenkins ~]# vim /etc/selinux/config
SELINUX=disabled
9.3 部署 JDK 与 Maven
# 解压到 /usr/local
[root@jenkins ~]# tar zxvf jdk-8u171-linux-x64.tar.gz -C /usr/local/
[root@jenkins ~]# tar zxvf apache-maven-3.6.2-bin.tar.gz -C /usr/local/
# 重命名
[root@jenkins ~]# cd /usr/local/
[root@jenkins local]# mv apache-maven-3.6.2/ maven
[root@jenkins local]# mv jdk1.8.0_171/ jdk
配置 Maven 阿里云镜像(settings.xml 第 159 行附近,注释外):
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
配置 Maven 默认使用 JDK 8(第 253 行注释外 + 第 275 行下面):
<profile>
<id>jdk8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
<activeProfiles>
<activeProfile>jdk8</activeProfile>
</activeProfiles>
9.4 部署 Docker
Docker 是开源的容器化平台,把应用及其依赖打包成轻量级、可移植的容器,实现快速部署和跨环境一致运行。
# 卸载旧版并安装依赖
[root@jenkins ~]# sudo yum remove docker docker-common docker-selinux docker-engine
[root@jenkins ~]# sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 使用华为云镜像源
[root@jenkins ~]# wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.huaweicloud.com/docker-ce/linux/centos/docker-ce.repo
[root@jenkins ~]# sudo sed -i 's+download.docker.com+mirrors.huaweicloud.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# 安装并启动
[root@jenkins ~]# sudo yum makecache fast
[root@jenkins ~]# sudo yum install docker-ce
[root@jenkins local]# systemctl enable docker --now
配置镜像加速器(daemon.json):
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn",
"https://hub-mirror.c.163.com"
]
}
EOF
[root@jenkins local]# systemctl daemon-reload
[root@jenkins local]# systemctl restart docker
说明:镜像加速地址会随时间失效,建议配置多个可用地址;构建时若出现下载超时,可重试或更换列表。
9.5 Docker 方式部署 Jenkins
第一步:拉取镜像
[root@jenkins local]# docker pull jenkins/jenkins:2.346.3-lts-jdk8
第二步:准备 docker-compose 编排文件
[root@jenkins ~]# mkdir -p /usr/local/docker/jenkins_docker
[root@jenkins ~]# cd /usr/local/docker/jenkins_docker/
[root@jenkins jenkins_docker]# vim docker-compose.yml
version: "3.1"
services:
jenkins:
image: jenkins/jenkins:2.346.3-lts-jdk8
container_name: jenkins
ports:
- 8080:8080
- 50000:50000
volumes:
- ./data/:/var/jenkins_home/
第三步:启动并解决权限问题
[root@jenkins jenkins_docker]# docker-compose up -d
# 查看日志发现 Permission denied
[root@jenkins jenkins_docker]# docker logs -f jenkins
touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
# 原因:宿主机 data 目录权限不足,容器内 jenkins 用户无法写入
[root@jenkins jenkins_docker]# chmod 777 data/
[root@jenkins jenkins_docker]# docker-compose restart
再次查看日志,看到初始化密码即部署成功:
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
f6eb6f8c4af94a048d433c7bae5384c9
This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
Jenkins is fully up and running
第四步:本地化插件与国内更新源
国外网络拉取插件很慢,先把插件包(plugins)通过 XFTP 覆盖到 data/plugins 目录,再修改属主:
[root@jenkins data]# chown -R zhangsan:zhangsan plugins/
替换更新源为清华源:
[root@jenkins data]# mv hudson.model.UpdateCenter.xml hudson.model.UpdateCenter.xml.bk
[root@jenkins data]# cp -p hudson.model.UpdateCenter.xml.bk hudson.model.UpdateCenter.xml
[root@jenkins data]# vim hudson.model.UpdateCenter.xml
<?xml version='1.1' encoding='UTF-8'?>
<sites>
<site>
<id>default</id>
<url>https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json</url>
</site>
</sites>
重启后访问 http://192.168.100.154:8080/。
第五步:常见问题——其他主机无法访问 Jenkins
本机可访问、其他主机访问不了,通常是宿主机未开启 IP 转发:
[root@jenkins jenkins_docker]# vim /etc/sysctl.conf
# 末尾添加
net.ipv4.ip_forward=1
[root@jenkins jenkins_docker]# sysctl -p
第六步:Jenkins 初始化
- 把日志中的序列密码粘贴进文本框;
- 选择不安装插件(插件已本地化上传);
- 创建管理员用户,实例配置保持默认;
- 进入首页后,在"系统管理 → 插件管理"中确认本地插件已被识别。
十、Jenkins 核心配置
10.1 安装 git-parameter 插件
git-parameter 插件用于从 Git 拉取项目到 Jenkins,并支持参数化选择分支/Tag。
在"系统管理 → 插件管理 → 高级"页面,找到 Deploy Plugin 上传入口,依次上传并安装:
- Deploy Plugin(部署相关);
- git-parameter(Git 参数)。
安装成功后,在"已安装"列表中可查看到插件。
10.2 全局工具配置:JDK 与 Maven
先把 JDK 和 Maven 移动到 Jenkins 工作目录(容器挂载目录),让容器内能直接使用:
[root@jenkins data]# cd /usr/local/docker/jenkins_docker/data
[root@jenkins data]# mv /usr/local/jdk/ ./
[root@jenkins data]# mv /usr/local/maven/ ./
然后在 Jenkins 页面:系统管理 → Global Tool Configuration(全局工具配置):
- JDK:取消自动安装,指定容器内路径
/var/jenkins_home/jdk; - Maven:取消自动安装,指定容器内路径
/var/jenkins_home/maven(与 maven 目录对应)。
配置完成后"应用并保存"。
10.3 配置 Publish over SSH 插件
Publish over SSH 插件用于把构建产物推送到目标 web 服务器,一般默认已安装。
进入系统管理 → Configure System(系统配置),找到 Publish over SSH 区域:
- 配置 SSH 目标服务器信息(Jenkins 服务器 IP/端口、目标主机 root 用户的用户名与密码);
- "应用并保存"后进行连接测试。
常见问题:缺少目标目录
测试报错缺少 /user/local/test 目录(示例路径),到目标 web 服务器上创建即可:
[root@web ~]# mkdir /usr/local/test
再次测试,显示连接成功。
10.4 创建 Jenkins 任务
进入 Jenkins 首页,新建任务:
- 定义任务名称(如 mytest),选择"构建一个自由风格的软件项目";
- 在"源码管理"中选择 Git,粘贴 GitLab 项目的 HTTP URL(私有项目需添加身份验证);
- 添加 GitLab 用户 chen 的凭据(用户名/密码);
- 应用并保存。
十一、五连构建:从拉代码到容器化上线
整个实战通过 5 次构建逐步打通流水线,理解每一步非常关键。
11.1 第 1 次构建:拉取 GitLab 项目
直接点击"立即构建"。构建成功后,进入 Jenkins 容器确认项目已拉取:
[root@jenkins jenkins_docker]# docker exec -it jenkins bash
jenkins@9b0e9093a29f:~/workspace/mytest$ ls
HELP.md demo_web.iml pom.xml src target
源码中已经有我们的测试接口:
jenkins@...:~/workspace/mytest/src/main/java/com/chenyan/demo/controller$ cat TestController.java
package com.chenyan.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "跟陈岩学云原生";
}
}
11.2 第 2 次构建:编译打包 jar
进入任务"配置 → 构建",添加构建步骤"调用顶层 Maven 目标",输入:
clean package -DskipTests
参数说明:
clean:清除之前构建生成的文件(如 target 目录),确保全新构建;package:编译源码并打包生成可执行文件;-DskipTests:跳过单元测试执行(但仍编译测试代码)。
构建成功后,到 Jenkins 服务器确认 jar 已生成:
[root@jenkins target]# cd /usr/local/docker/jenkins_docker/data/workspace/mytest/target
[root@jenkins target]# ls
classes demo-0.0.1-SNAPSHOT.jar demo-0.0.1-SNAPSHOT.jar.original generated-sources ...
11.3 第 3 次构建:推送 jar 至目标服务器
进入任务"配置 → 构建后操作",选择"Send build artifacts over SSH"(通过 SSH 发送构建产物):
- 选择之前配置好的 SSH Server;
- 设置要发送的源文件(如
target/*.jar)与远程目录; - 构建后查看控制台输出,确认已发送成功。
到目标 web 服务器验证:
[root@web ~]# cd /usr/local/test/
[root@web test]# ls
target
[root@web target]# ls
demo-0.0.1-SNAPSHOT.jar
11.4 第 4 次构建:拉取 Docker 文件
应用要容器化,需要 Dockerfile 与 docker-compose.yml。在 Windows 的 IDEA 项目中创建 docker 文件夹:
Dockerfile:
FROM openjdk:8
COPY demo-0.0.1-SNAPSHOT.jar /usr/local/
WORKDIR /usr/local
CMD java -jar demo-0.0.1-SNAPSHOT.jar
docker-compose.yml(与 Dockerfile 同目录):
version: '3.1'
services:
mytest:
build:
context: ./
dockerfile: Dockerfile
image: mytest:v1.0
container_name: mytest
ports:
- 8080:8080
将整个项目(含 docker 目录)提交到本地 Git 并 push 到 GitLab,然后在 Jenkins 中再次"立即构建"。构建成功后进入容器查看:
[root@jenkins mytest]# cd /usr/local/docker/jenkins_docker/data/workspace/mytest
[root@jenkins mytest]# ls
demo_web.iml docker HELP.md pom.xml src target
[root@jenkins mytest]# ls docker/
docker-compose.yml Dockerfile
11.5 第 5 次构建:目标服务器容器化运行
第一步:web 目标服务器部署 Docker
[root@web test]# yum install -y yum-utils device-mapper-persistent-data lvm2
[root@web test]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@web test]# systemctl disable firewalld.service --now
[root@web test]# setenforce 0
[root@web test]# yum install docker-ce -y
[root@web test]# systemctl enable docker --now
同样配置镜像加速与 IP 转发:
tee /etc/docker/daemon.json <<-'EOF'
{ "registry-mirrors": ["https://docker.m.daocloud.io", "..."] }
EOF
[root@web test]# systemctl daemon-reload
[root@web test]# systemctl restart docker
[root@web test]# vim /etc/sysctl.conf
net.ipv4.ip_forward=1
[root@web test]# sysctl -p
第二步:修改 Jenkins 构建后操作
回到任务"配置 → 构建后操作",在 SSH 发送配置的 Exec command 中填写:
cd /usr/local/test/docker
mv ../target/*jar ./
docker-compose down
docker-compose up -d --build
流程说明:进入 docker 目录 → 把上级推送来的 jar 移动到当前目录 → 停掉旧容器 → 重新构建镜像并启动容器。
第三步:构建并验证
点击"立即构建"。首次构建若出现 timeout 信息,通常是拉取 openjdk:8 基础镜像慢,多构建几次即可。
浏览器访问目标服务器:
http://192.168.100.155:8080/test
看到"跟陈岩学云原生"即表示部署成功!在 web 服务器上确认镜像与容器:
[root@web docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mytest v1.0 549778e2ed21 4 minutes ago 543MB
openjdk 8 b273004037cc 2 years ago 526MB
[root@web docker]# docker ps -a
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
25d33d9e84bd mytest:v1.0 "/bin/sh -c 'java -j…" Up 6 minutes 0.0.0.0:8080->8080/tcp mytest
到这里,一条完整的 CI/CD 流水线就通了:
IDEA 提交代码 → GitLab 仓库 → Jenkins 拉取 → Maven 编译 jar → SSH 推送 → Docker 构建运行
11.6 清理虚悬镜像
多次构建会在目标服务器产生 <none> 虚悬镜像,可以一键清理:
[root@web ~]# docker image prune -f
也可以把这条命令直接放进 Jenkins 的构建后操作中,每次部署后自动清理。
十二、进阶:参数化构建(按 Tag 发布版本)
手动"立即构建"每次都构建最新代码;生产环境更常见的是选择一个版本(Tag)发布,这就是参数化构建。
12.1 配置 Git 参数
进入任务"配置 → General",勾选"参数化构建过程",添加Git 参数:
- 名称:如
Tag; - 参数类型:Branch or Tag;
- 默认值:origin/master。
12.2 添加版本切换步骤
在"构建"中添加"执行 shell"步骤,拖拽到构建选项的置顶位置(先切版本,再编译打包):
git checkout $Tag
这样每次构建会先切到选中的 Tag,再执行 mvn clean package -DskipTests。
12.3 在 GitLab 打 Tag
回到 GitLab 项目页面,选择"标签(Tags)":
- 创建新标签,填写版本号(如 v1.0)并创建;
- 在 IDEA 中修改代码(例如把返回内容改为版本 2.0 文案);
- 提交并 push,再创建 v2.0 标签。
此时项目已有两个版本:
v1.0(初版)
v2.0(更新版)
12.4 选择版本构建验证
回到 Jenkins 任务页面,"Build with Parameters"现在可以选择 Tag:
- 选择 v1.0 构建,访问目标网站看到 1.0 版本内容;
- 再选择 v2.0 构建,刷新网页即可看到内容更新为 2.0。
这就是"参数化发布 + 快速回滚"的雏形:想回到旧版本,只需重新选择旧 Tag 构建一次。配合 docker-compose 每次重建,版本切换非常干净。
十三、常见问题速查
| 问题 | 原因 | 解决 |
|---|---|---|
| Jenkins 容器启动报 Permission denied | 宿主机 data 目录权限不足 | chmod 777 data/ 后重启容器 |
| IDEA 报"无效的源发行版: 13" | 编译级别与 JDK 版本不一致 | Settings → Java Compiler 与 Project Structure 中统一改为 1.8 |
| 其他主机访问不了 Jenkins | 未开启 IP 转发 | /etc/sysctl.conf 添加 net.ipv4.ip_forward=1 并 sysctl -p |
| 插件/镜像下载超时 | 国外源慢 | 插件换清华源;镜像配多个国内加速器 |
| Publish over SSH 测试失败 | 目标目录不存在 | 在目标服务器先 mkdir 对应目录 |
| 私有项目拉取失败 | 未添加凭据 | 任务中配置 GitLab 用户凭据 |
目标服务器出现大量 <none> 镜像 |
多次构建残留 | docker image prune -f 或写入构建后操作 |
十四、总结
回顾整篇内容,我们做了两件事:
第一件事:建立 DevOps 心智模型。
软件交付正在从瀑布模型(一次大开发、末期交付、惧怕需求变化)走向敏捷开发(迭代 + 增量、早期交付、持续降低风险),而敏捷的工程基石就是持续集成:频繁提交、自动测试、快速构建,让"可发布的版本"随时存在。
第二件事:亲手打通一条 CI/CD 流水线。
从 Windows 开发环境的 JDK/Maven/IDEA 准备,到 GitLab 私有仓库搭建,再到 Jenkins 的 Docker 化部署与任务配置,我们用 5 次构建实现了:
代码提交(GitLab)→ 自动拉取(Jenkins)→ 编译打包(Maven)
→ 产物推送(SSH)→ 容器运行(Docker Compose)
最后通过 git-parameter 实现按 Tag 的参数化发布,让"选版本、点构建、看效果"成为日常操作。
有了这条流水线,代码提交后的构建、测试、打包、部署都不再依赖人工,DevOps 的价值——更快交付、更高质量、更少重复劳动——也就真正落地了。下一篇可以继续深入 Jenkins Pipeline as Code(Jenkinsfile)与 Kubernetes 部署,欢迎关注!