DevOps 入门系列 :CI_CD流水线
CI/CD 零基础教程(基于GitLab平台)
Part 1:认识CI/CD
1.1 手动部署的痛苦
假设你已经写好了一个 Python Flask 应用,用 Docker 打包好了。现在每次修改代码,你需要手动执行一系列重复操作,代码如下:
# 1. 本地重新构建镜像
docker build -t myapp:v2 .
# 2. 登录远程服务器
ssh user@你的服务器
# 3. 在服务器上拉取新镜像(如果是本地推送到仓库)
docker pull myapp:v2
# 4. 停止旧容器,启动新容器
docker stop myapp
docker rm myapp
docker run -d --name myapp -p 80:5000 myapp:v2
如果一天改 10 次代码,就要重复 10 次这些操作。不仅浪费时间、效率极低,还容易出现人为失误,比如忘记停止旧容器导致端口冲突、部署版本不一致等问题。
CI/CD 就是为了解决该问题:只需执行 git push 提交代码,测试、打包、部署等所有流程均可自动完成。
1.2 CI/CD 是什么?
CI(Continuous Integration,持续集成)
代码推送到仓库后,系统立即自动运行代码测试、项目构建流程,全程校验新提交代码的兼容性与可用性,确保新代码不会破坏项目原有功能。
核心解决痛点:本地好好的,部署到服务器就报错的环境适配难题。
CD(Continuous Delivery / Deployment,持续交付/部署)
-
持续交付:自动化完成构建、测试流程后,提前准备好 Docker 镜像等项目产物,仅需手动点击按钮即可完成上线。
-
持续部署:无需任何手动操作,流程全部自动化,代码校验通过后直接部署到生产环境。
一句话总结:CI 保证「代码可正常运行」,CD 保证「代码自动部署到服务器」。
1.3 CI/CD 核心核心组件(以 GitLab CI 为例)
GitLab 内置完整的 CI/CD 系统,无需额外搭建 Jenkins 等第三方工具,仅需在项目仓库根目录配置 .gitlab-ci.yml 文件,即可自动执行流水线任务。
完整工作流程
你执行 git push 提交代码
↓
GitLab 检测到仓库根目录的 .gitlab-ci.yml 配置文件
↓
GitLab 分配 Runner(任务执行者)承接任务
↓
Runner 启动纯净的 Docker 容器/虚拟机运行环境
↓
Runner 按配置文件顺序执行自定义命令(测试、构建、打包等)
↓
Runner 将任务结果(成功/失败、运行日志、产物)回传 GitLab
↓
用户在 GitLab 页面查看流水线状态
三大核心概念
关键概念1:Runner(执行者)
Runner 是真正执行 CI/CD 任务的「工人」,负责运行配置文件中的所有命令。学习阶段可直接使用 GitLab 提供的公共免费 Runner,无需本地搭建、无需额外安装部署。
关键概念2:.gitlab-ci.yml(配置剧本)
CI/CD 流水线的核心配置文件,所有自动化流程规则、任务、命令均在该文件中定义,GitLab 会严格按照文件配置执行任务。文件必须放置在仓库根目录,且文件名必须以点开头。
关键概念3:Pipeline(流水线)
一次完整的代码触发、任务执行、结果反馈流程,即为一条流水线。一条流水线可拆分多个串行阶段,典型流程:构建(build)→ 测试(test)→ 部署(deploy)。
1.4 补充知识:Webhook 触发机制
很多人会疑惑:GitLab 如何实现「代码 push 后自动触发流水线」?核心依赖 Webhook 机制。
定义
Webhook 是一种事件触发机制:当指定事件发生时,系统自动向预设 URL 发送 HTTP 请求,触发对应后续动作。
GitLab CI 中的应用
-
触发事件:开发者执行 git push 提交代码
-
Webhook 地址:GitLab 内置触发地址(系统自动配置,无需手动修改)
-
执行动作:唤醒 Runner 读取 .gitlab-ci.yml 配置,运行流水线任务
无需手动编写 Webhook 代码,理解该机制可快速排查「代码 push 后流水线未启动」的异常问题。
1.5 5分钟实战:跑通第一条 CI 流水线
步骤0:准备 GitLab 项目
-
登录 GitLab 官网(gitlab.com)或国内极狐 GitLab(jihulab.com),两者功能用法完全一致
-
新建空白项目,命名为
my-first-ci -
本地终端克隆空仓库,执行命令:
git clone https://gitlab.com/你的用户名/my-first-ci.git
cd my-first-ci
步骤1:创建流水线配置文件
在项目根目录新建文件 .gitlab-ci.yml(必须带开头的点),写入以下基础配置:
# 定义流水线执行阶段
stages:
- hello
# 定义流水线任务
say_hello:
stage: hello # 绑定所属阶段
script: # 任务执行命令(必填字段)
- echo "Hello from CI/CD"
- echo "当前时间:$(date)"
步骤2:提交代码触发流水线
git add .gitlab-ci.yml
git commit -m "添加第一条流水线"
git push origin main
步骤3:查看流水线运行结果
-
浏览器打开 GitLab 项目主页,点击左侧菜单「CI/CD → Pipelines」
-
页面会显示新建流水线,状态依次为 pending(等待)→ running(运行中)
-
几秒后刷新页面,状态变为绿色 ✅ passed(运行成功)
-
点击流水线编号,进入后打开 say_hello 任务,可查看实时输出日志
成功日志示例:
Hello from CI/CD!
当前时间: 2025-01-xx xx:xx:xx
至此,你的第一条 GitLab CI 流水线成功运行!
1.6 YAML 配置逐行详解
-
stages:声明流水线包含的所有执行阶段,示例中仅定义 hello 阶段。阶段名称可自定义(build、test、deploy 等),阶段按列表顺序串行执行,上一阶段所有任务成功后,才会执行下一阶段。
-
say_hello:自定义任务(Job)名称,需保证项目内唯一,代表一个独立的执行任务单元。
-
stage: hello:将当前任务绑定到指定阶段,名称必须与 stages 定义的阶段名完全一致。
-
script:核心必填字段,数组格式,按顺序执行每行 shell 命令。任意一行命令执行失败(非0退出码),当前任务立即终止,流水线标红失败。
1.7 核心:命令退出码与流水线成败
所有 Linux 命令执行结束后,都会向系统返回一个整数,即退出码(exit code),这是 CI 系统判断任务成败的核心依据:
-
退出码 = 0:命令执行成功,无异常
-
退出码 = 1~255:命令执行失败(文件不存在、权限不足、代码测试报错等)
例如:流水线中执行 pytest 单元测试,若代码存在 bug,pytest 会返回非0退出码,CI 系统判定任务失败,流水线直接终止并标红。
Part 2:深入 .gitlab-ci.yml
Part1 实现了基础流水线运行,本章节将讲解 .gitlab-ci.yml 文件的核心高频配置字段,实现自动测试、报告保存、文件传递、依赖加速等功能。
2.1 回顾:Job 最小基础结构
一个合法的 CI 任务必须包含三大核心字段,结构如下:
job_name: # 自定义任务名(项目内唯一)
stage: 某个阶段名 # 绑定对应执行阶段
script: # 执行命令(必填)
- 命令1
- 命令2
2.2 stages:定义流水线执行顺序
功能说明
stages 为数组格式,用于定义流水线的所有阶段及串行执行顺序,解决构建、测试、部署流程乱序问题。
核心规则
-
不同阶段:串行执行,上一阶段所有任务成功,才进入下一阶段
-
同一阶段:多个任务并行执行,大幅提升流水线效率
-
任一任务失败:后续所有阶段任务终止执行
代码示例
# 声明流水线阶段及执行顺序:构建→测试→部署
stages:
- build # 阶段1:项目编译/构建
- test # 阶段2:代码测试校验
- deploy # 阶段3:项目部署上线
# 构建阶段任务
compile_code:
stage: build
script:
- echo "正在编译代码..."
- make # 适配make编译项目
# 测试阶段任务
run_tests:
stage: test
script:
- echo "正在运行单元测试..."
- pytest tests/
# 部署阶段任务
deploy_to_server:
stage: deploy
script:
- echo "正在部署到服务器..."
- scp ./app.tar user@server:/app/
2.3 variables:自定义变量,简化配置
功能说明
通过 variables 定义全局键值对变量,在任务命令中通过 $变量名 引用,解决配置硬编码、敏感信息泄露问题。
核心优势
-
统一管理重复配置(版本号、镜像名等),修改一处全局生效
-
隔离敏感信息(密码、Token),避免明文写入配置文件
基础使用示例
# 全局变量定义
variables:
APP_NAME: "my-flask-app"
VERSION: "1.0.0"
IMAGE_TAG: "$APP_NAME:$VERSION"
stages:
- build_image
build:
stage: build_image
script:
- echo "正在构建镜像:$IMAGE_TAG"
- docker build -t $IMAGE_TAG .
- echo "镜像标签是:$IMAGE_TAG"
GitLab 内置预定义变量(直接复用)
-
$CI_COMMIT_SHORT_SHA:当前代码提交的短哈希值(用于版本追溯) -
$CI_COMMIT_REF_NAME:当前分支名/标签名 -
$CI_PIPELINE_ID:当前流水线唯一ID
进阶用法(哈希值作为镜像版本,保证版本唯一可追溯):
build:
script:
- docker build -t myapp:$CI_COMMIT_SHORT_SHA .
敏感变量安全配置
禁止将密码、密钥写入配置文件,正确操作:
-
GitLab 项目页面 → Settings → CI/CD → Variables
-
添加自定义敏感变量(如 DOCKER_PASSWORD)
-
勾选 Masked(日志隐藏变量值)、Protected(仅指定分支可用)
-
配置文件中通过
$变量名引用
2.4 artifacts:任务制品跨阶段传递文件
功能说明
artifacts(制品)用于保存当前任务生成的文件/目录,上传至 GitLab 服务器,供后续阶段任务下载使用。若不配置制品,任务结束后临时环境文件会全部销毁。
典型场景
构建阶段生成安装包/测试报告,测试、部署阶段复用该文件,无需重复生成。
示例1:保存测试报告
stages:
- test
- upload_report
# 运行测试并生成报告
run_pytest:
stage: test
script:
- pytest --html=report.html # 生成HTML格式测试报告
artifacts:
name: "test-report-$CI_COMMIT_SHORT_SHA" # 制品压缩包名称
paths:
- report.html # 需要保存的文件路径
expire_in: 7 days # 制品自动过期时间(节省存储空间)
when: always # 无论任务成败,均保存报告
# 上传测试报告
upload_report:
stage: upload_report
script:
- ls -l report.html
- scp report.html user@server:/reports/
核心字段释义
-
paths:必填项,指定需要保存的文件/目录,支持通配符(*.log)
-
name:自定义制品包名称,默认 artifacts.zip
-
expire_in:制品保留时长,默认30天,过期自动删除
-
when:制品保存时机
-
on_success(默认):任务成功才保存
-
on_failure:任务失败时保存(用于排查错误)
-
always:无论成败均保存
-
示例2:保存项目构建目录
build:
stage: build
script:
- mkdir dist
- cp *.py dist/
- echo "v$CI_PIPELINE_ID" > dist/version.txt
artifacts:
paths:
- dist/ # 保存整个构建目录
expire_in: 1 day
deploy:
stage: deploy
script:
- ls dist/ # 可直接读取上游保存的目录文件
- scp -r dist/ user@server:/app/
2.5 cache:缓存依赖,加速流水线
功能说明
每个 CI 任务默认运行在纯净环境,每次执行都会重新下载依赖,耗时极长。cache 用于缓存 npm、pip 等依赖目录,跨流水线复用缓存文件,大幅缩短构建时间。
示例1:Python pip 依赖缓存
stages:
- test
# 全局缓存配置
cache:
key: "$CI_COMMIT_REF_SLUG" # 按分支隔离缓存,避免依赖冲突
paths:
- .cache/pip # pip缓存包目录
- venv/ # 虚拟环境目录
# 全局前置脚本,所有任务执行前运行
before_script:
- python -m venv venv
- source venv/bin/activate
test:
stage: test
script:
- pip install -r requirements.txt --cache-dir .cache/pip
- pytest
示例2:Node 项目依赖缓存
# 依据lock文件变化自动更新缓存,精准适配依赖变更
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
test_node:
script:
- npm ci # 精准安装lock文件锁定的依赖版本
- npm test
缓存核心参数
-
key:缓存唯一标识,支持按分支、按文件内容生成缓存
-
policy:缓存读写规则
-
pull-push(默认):下载缓存,任务结束后更新缓存
-
pull:仅下载缓存,不更新
-
push:仅上传更新缓存,不下载
-
2.6 artifacts 与 cache 核心区别
| 对比维度 | artifacts(制品) | cache(缓存) |
|---|---|---|
| 核心目的 | 跨任务传递业务文件,保障流程闭环 | 缓存临时依赖,加速流水线运行 |
| 典型内容 | 编译包、测试报告、部署文件 | pip/npm/maven 依赖包、缓存目录 |
| 生命周期 | 按 expire_in 定时删除 | 系统自动管理,可随时清理 |
| 跨流水线使用 | 不支持 | 支持(同key缓存可复用) |
| 手动下载 | 支持,GitLab 页面可直接下载 | 不支持 |
| 容错性 | 必须保留,缺失会导致后续任务失败 | 可缺失,无缓存仅需重新下载依赖 |
artifacts 是必须送达的业务快递,cache 是可复用的加速缓存。
2.7 dependencies:精准控制制品下载
功能说明
默认情况下,下游任务会自动下载所有上游任务的制品,通过 dependencies 可指定仅下载所需任务的制品,减少文件冗余、避免文件名冲突。
代码示例
stages:
- build
- test
- deploy
# 构建Linux版本包
build_linux:
stage: build
script:
- echo "linux binary" > bin_linux
artifacts:
paths:
- bin_linux
# 构建Mac版本包
build_mac:
stage: build
script:
- echo "mac binary" > bin_mac
artifacts:
paths:
- bin_mac
# 仅部署Linux版本,只下载对应制品
deploy_linux:
stage: deploy
dependencies:
- build_linux # 仅拉取 build_linux 任务制品
script:
- cat bin_linux # 文件存在
- cat bin_mac # 文件不存在,未下载
补充:配置 dependencies: [] 可禁止下载所有上游制品,适配独立运行的任务。
2.8 environment:环境部署记录与管理
功能说明
将部署任务与指定运行环境绑定(测试环境、预发布环境、生产环境),GitLab 自动记录部署日志、版本、时间、操作人员,支持一键回滚。
代码示例
stages:
- deploy
# 预发布环境部署
deploy_to_staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging # 环境名称
url: https://staging.example.com # 环境访问地址
# 生产环境部署(手动触发,防误操作)
deploy_to_production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
when: manual # 手动触发任务
配置后,可在 GitLab「Deployments → Environments」页面查看所有环境的部署历史、版本记录,支持快速回滚。
2.9 综合:测试+报告保存+模拟通知流水线
整合上述所有核心配置,实现一套完整可用的自动化测试流水线,包含依赖缓存、自动测试、报告持久化、结果输出功能。
# 流水线阶段定义
stages:
- test
- report
# 全局变量
variables:
REPORT_NAME: "test-report.html"
# 依赖缓存配置
cache:
key:
files:
- requirements.txt # 依赖文件变更才更新缓存
paths:
- .cache/pip
# 全局前置操作:安装测试工具
before_script:
- pip install --cache-dir .cache/pip pytest pytest-html
# 自动测试并生成报告
run_tests:
stage: test
script:
- pytest --html=$REPORT_NAME
artifacts:
name: "pytest-report-$CI_COMMIT_SHORT_SHA"
paths:
- $REPORT_NAME
when: always # 失败也保留报告,方便排查
expire_in: 3 days # 报告保留3天
# 读取并模拟上传测试报告
upload_report:
stage: report
dependencies:
- run_tests # 仅获取测试任务的报告制品
script:
- echo "测试报告内容预览:"
- cat $REPORT_NAME | head -n 10
- echo "测试报告上传完成(模拟)"
流水线执行逻辑
-
前置步骤:缓存 pip 依赖,安装 pytest 测试工具,加速后续运行
-
test 阶段:执行自动化测试,生成 HTML 测试报告并持久化保存为制品
-
report 阶段:拉取测试报告,预览内容并模拟上传归档
-
无论测试成败,均保留报告
更多推荐
所有评论(0)