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 项目

  1. 登录 GitLab 官网(gitlab.com)或国内极狐 GitLab(jihulab.com),两者功能用法完全一致

  2. 新建空白项目,命名为 my-first-ci

  3. 本地终端克隆空仓库,执行命令:

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:查看流水线运行结果

  1. 浏览器打开 GitLab 项目主页,点击左侧菜单「CI/CD → Pipelines」

  2. 页面会显示新建流水线,状态依次为 pending(等待)→ running(运行中)

  3. 几秒后刷新页面,状态变为绿色 ✅ passed(运行成功)

  4. 点击流水线编号,进入后打开 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 .

敏感变量安全配置

禁止将密码、密钥写入配置文件,正确操作:

  1. GitLab 项目页面 → Settings → CI/CD → Variables

  2. 添加自定义敏感变量(如 DOCKER_PASSWORD)

  3. 勾选 Masked(日志隐藏变量值)、Protected(仅指定分支可用)

  4. 配置文件中通过 $变量名引用

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 "测试报告上传完成(模拟)"

流水线执行逻辑

  1. 前置步骤:缓存 pip 依赖,安装 pytest 测试工具,加速后续运行

  2. test 阶段:执行自动化测试,生成 HTML 测试报告并持久化保存为制品

  3. report 阶段:拉取测试报告,预览内容并模拟上传归档

  4. 无论测试成败,均保留报告

更多推荐