DevOps 入门到实战:从敏捷开发、持续集成到 GitLab + Jenkins + Docker 自动化部署

Source

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 的完整流程

根据持续集成的设计,代码从提交到生产通常经历以下几步:

  1. 提交(Commit):开发者向代码仓库提交代码,所有后续步骤都始于本地代码的一次 commit;
  2. 测试(第一轮):代码仓库为 commit 配置钩子(hook),提交代码或合并进主干即自动跑测试;
  3. 构建(Build):通过第一轮测试后代码可合并进主干。构建指把源码转换为可运行的实际代码,例如安装依赖、配置样式表/JS 脚本/图片等资源;
  4. 测试(第二轮):构建完成后进行第二轮测试。如果第一轮已覆盖全部测试,第二轮可省略(此时构建步骤要移到第一轮测试之前);
  5. 部署(Deploy):通过测试的代码成为可直接部署的版本(artifact),打包存档并发布到生产服务器;
  6. 回滚(Rollback):当前版本出问题时,回滚到上一版本的构建结果,最简单做法是修改符号链接指向上一个版本的目录。

4.2 持续集成的组成要素

  • 一个自动构建过程:从检出代码、编译构建、运行测试、结果记录到测试统计全部自动完成,无需人工干预;
  • 一个代码存储库:用版本控制软件(SVN 或 Git)保障代码可维护性,同时作为构建素材库;
  • 一个持续集成服务器:Jenkins 就是配置简单、使用方便的持续集成服务器。

4.3 持续集成的好处

  1. 降低风险:持续构建、编译和测试,能很早期发现问题,修复代价小;
  2. 对系统健康持续检查,减少发布风险带来的问题;
  3. 减少重复性工作,解放人工;
  4. 持续部署:随时提供可部署单元包;
  5. 持续交付可供使用的版本;
  6. 增强团队信心

第二部分: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 盘,按向导完成安装。

新建项目:

  1. 选择 Spring Initializr 创建 Web 项目;
  2. 修改 Group,Type 设置为 Maven
  3. 选择 Web 中的 Spring Web 模板;
  4. 设置项目名称 demo_web 与路径;
  5. 创建完成后删除多余配置项。

编写测试接口:

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 版本不一致。解决方法:

  1. File → Settings → Build, Execution, Deployment → Compiler → Java Compiler,将字节码版本改为 1.8 并 Apply;
  2. 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 菜单:

  1. + Add 将文件加入缓存;
  2. Commit Directory 提交到本地 Git,填写版本说明与邮箱;
  3. 左下角显示提交成功信息。

第四步:推送到远程 GitLab

  1. 在 GitLab 对应项目页面复制 HTTP 仓库地址;
  2. IDEA 中选择 Push,把 URL 粘贴进去;
  3. 输入 GitLab 用户身份验证(私有项目需要);
  4. 验证成功后直接 Push,右下角出现成功提示;
  5. 回到 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 初始化

  1. 把日志中的序列密码粘贴进文本框;
  2. 选择不安装插件(插件已本地化上传);
  3. 创建管理员用户,实例配置保持默认;
  4. 进入首页后,在"系统管理 → 插件管理"中确认本地插件已被识别。

十、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 区域:

  1. 配置 SSH 目标服务器信息(Jenkins 服务器 IP/端口、目标主机 root 用户的用户名与密码);
  2. "应用并保存"后进行连接测试。

常见问题:缺少目标目录

测试报错缺少 /user/local/test 目录(示例路径),到目标 web 服务器上创建即可:

[root@web ~]# mkdir /usr/local/test

再次测试,显示连接成功。

10.4 创建 Jenkins 任务

进入 Jenkins 首页,新建任务:

  1. 定义任务名称(如 mytest),选择"构建一个自由风格的软件项目";
  2. 在"源码管理"中选择 Git,粘贴 GitLab 项目的 HTTP URL(私有项目需添加身份验证);
  3. 添加 GitLab 用户 chen 的凭据(用户名/密码);
  4. 应用并保存。

十一、五连构建:从拉代码到容器化上线

整个实战通过 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 发送构建产物):

  1. 选择之前配置好的 SSH Server;
  2. 设置要发送的源文件(如 target/*.jar)与远程目录;
  3. 构建后查看控制台输出,确认已发送成功。

到目标 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)":

  1. 创建新标签,填写版本号(如 v1.0)并创建;
  2. 在 IDEA 中修改代码(例如把返回内容改为版本 2.0 文案);
  3. 提交并 push,再创建 v2.0 标签。

此时项目已有两个版本:

v1.0(初版)
v2.0(更新版)

12.4 选择版本构建验证

回到 Jenkins 任务页面,"Build with Parameters"现在可以选择 Tag:

  1. 选择 v1.0 构建,访问目标网站看到 1.0 版本内容;
  2. 再选择 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=1sysctl -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 部署,欢迎关注!