CI/CD学习记录(基于GitLab)

首先来回忆一下之前是怎么发版的?仅有测试和正式两个环境的情况下,大致如下

  • 开发完成,开发分支提交代码
  • 合并代码到发版(测试)的分支
  • 打包构建
  • 部署到服务器(测试)
  • 进行测试
  • 以上步骤不断重复且较为频繁,直到测试通过
  • 合并代码到发版(生产)的分支
  • 打包构建
  • 部署到服务器(生产)

以上步骤,提交代码和合并代码其实还是比较省时的,但是构建和部署是需要花比较多的时间的也是比较容易出错的,尤其是比较大型的项目,那有没有办法让打包构建和部署,甚至是自动测试,交给一些工具来做呢?我们只需要提交代码,工具检测到代码变了,就自动帮我打包构建、自动化测试并部署到服务器。这样一来程序员就可以少干一些事情,同时也能达到一个快捷、稳定、可靠的效果。

基于这个出发点,就开始了CI/CD的学习,本次的目标是:完成提交代码后,能自动构建并部署到测试环境

CI/CD

  • CI: Continuous Integrating,持续集成,这一过程就是将代码变更提交到发版分支(如 mastertest)等,并通过自动化工具进行构建、测试和验证。

    • 主要目标:确保每次代码变更都能快速的集成到对应分支,避免代码冲突并尽早发现缺陷
    • 关键步骤
      • 代码提交:开发人员将代码推送到版本控制系统
      • 自动构建:系统自动拉取代码并编译(如构建 Docker 镜像、编译代码)
      • 自动测试:运行单元测试、集成测试等,验证代码功能和稳定性
      • 反馈结果:若测试失败,立即通知开发人员修复;若成功,则进入下一阶段
  • CD: 有两个含义

    • Continuous Delivery,持续交付,在CI的基础上,进一步自动化将代码部署到测试、预生产环境中,确保代码随时可以发布到生产环境
    • Continuous Deployment, 持续部署,在持续交付的基础上,自动化将代码直接部署到生产环境

基于GitLab的CI/CD

这里先说三个东西,理解好这三个东西,将会帮助你快速的完成GitLab的CI/CD的操作

  • GitLab---托管 GitLab 实例,用于存储和管理代码的仓库工具
  • Runner---执行器,用于运行GitLab上的CI/CD作业的工具
  • 部署地---部署程序的服务器

以上三个东西,可以在同一台服务器上,也可以各自在不同服务器上,也可以任意组合。不同的组合,这在配置和操作上会有些许不同。 GitLab Doc中指出:出于安全和性能原因,请将 GitLab Runner 安装在与托管 GitLab 实例的机器分开的机器上 由于我这边只有两个服务器,一个是管 GitLab 实例的机器,一个是部署网站的机器,所以我这里就将Runner和部署地放到同一台机器上了。当然你也可以使用本地机器来充当Runner,用于学习测试一样可行。由于我这里已经私有化部署了GitLab了,就不介绍GitLab了,直接将怎么在GitLab上开启CI/CD,这里也简化以下,只讲打包构建和部署两个步骤,至于中间的代码检查、单元测试等就先不提。 目标是:完成提交代码后,能自动构建并部署到测试环境

安装 GitLab Runner(CentOS)

bash 复制代码
# 添加 GitLab 官方仓库
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash

# 安装 GitLab Runner
sudo yum install gitlab-runner

# 安装服务(指定工作目录和用户)
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

# 启动服务
sudo systemctl start gitlab-runner

# 设置开机自启
sudo systemctl enable gitlab-runner

注册GitLab Runner

关键信息获取地方

  1. 登录GitLab网页界面。
  2. 进入 项目 > 设置 > CI/CD
  3. 展开 Runners 部分,点击 New project runner
  4. 复制生成的 注册令牌(Registration token)GitLab实例URL
bash 复制代码
# 交互式注册
sudo gitlab-runner register

按提示输入信息

  1. GitLab实例URL (例如:https://gitlab.com/)。
  2. 注册令牌(从GitLab获取)。
  3. Runner描述 (例如:my-centos-runner)。
  4. Runner标签 (用逗号分隔,例如:centos,shell)。
  5. 执行器类型 (推荐使用 shelldocker
    1. shell:直接操作操作系统和文件,直接在宿主机运行命令,更简单
    2. docker:通过容器实现资源隔离,避免版本冲突,更推荐
  6. 选择docker才有的镜像选项 (例如:node:20-alpine)

至此展开 Runners ,就能看到新注册的Runner了

Docker支持

bash 复制代码
# 安装Docker依赖
sudo yum install -y yum-utils

# 添加Docker官方仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装Docker引擎
sudo yum install docker-ce docker-ce-cli containerd.io

# 启动Docker服务
sudo systemctl start docker
sudo systemctl enable docker
gitlab-runner 用户添加到 docker
bash 复制代码
sudo usermod -aG docker gitlab-runner
sudo systemctl restart gitlab-runner
第三方镜像站点

由于官方的站点速度慢,经常会拉取失败,为了解决这个问题,我们可以在/etc/docker/中增加daemon.json

json 复制代码
{

"registry-mirrors": [

"https://docker.registry.cyou",

"https://docker-cf.registry.cyou",

"https://dockercf.jsdelivr.fyi",

"https://docker.jsdelivr.fyi",

"https://dockertest.jsdelivr.fyi",

"https://mirror.aliyuncs.com",

"https://dockerproxy.com",

"https://mirror.baidubce.com",

"https://docker.m.daocloud.io",

"https://docker.nju.edu.cn",

"https://docker.mirrors.sjtug.sjtu.edu.cn",

"https://docker.mirrors.ustc.edu.cn",

"https://mirror.iscas.ac.cn",

"https://docker.rainbond.cc",

"https://do.nark.eu.org",

"https://dc.j8.work",

"https://dockerproxy.com",

"https://gst6rzl9.mirror.aliyuncs.com",

"https://registry.docker-cn.com",

"http://hub-mirror.c.163.com",

"http://mirrors.ustc.edu.cn/",

"https://mirrors.tuna.tsinghua.edu.cn/",

"http://mirrors.sohu.com/"

],

"insecure-registries": [

"registry.docker-cn.com",

"docker.mirrors.ustc.edu.cn"

],

"debug": true,

"experimental": false

}

配置docker执行时挂载到宿主机的volumes(可选)

修改在/etc/gitlab-runner/config.yml文件

toml 复制代码
concurrent = 1
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "tadpole-vue3-235"
  url = "xxxxxxx"
  id = 7
  token = "xxxxxxx"
  token_obtained_at = 2025-09-11T10:05:04Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  [runners.cache]
    MaxUploadedArchiveSize = 0
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "node:20-alpine"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache:/cache:rw", "/usr/share/nginx/html:/usr/share/nginx/html:rw"]
    shm_size = 0
    network_mtu = 0

至此,基于docker的Runner就基本配置完毕了,接下来就需要写.gitlab-ci.yml文件了

gitlab-ci.yml

首先,这里需要注意一下版本,如果GitLab的实例版本比较低,很多语法是不支持的,可能导致比如AI推荐你的语法但实际用不了。

gitlab-ci.yml语法

核心语法和指令

stages(阶段)
  • 作用:定义流水线的执行阶段,控制任务的顺序。

  • 规则

    • 所有 job 必须属于某个 stage
    • 同一 stage 中的 job 可并行执行。
  • 示例

    Yaml 复制代码
    stages:
      - build
      - test
      - deploy
** job(任务)**
  • 定义 :每个任务由 name(名称)和配置项组成。
  • 关键配置
    • stage: 指定任务所属的阶段。
    • script: 要执行的命令列表。
    • before_script/after_script: 在 script 前后运行的默认脚本。
    • only/except: 控制任务触发的分支或事件。
    • image: 使用的 Docker 镜像(可选)。
    • cache/artifacts: 缓存依赖或保存文件。
示例任务
Yaml 复制代码
build_job:
  stage: build
  script:
    - echo "Building the project..."
  before_script:
    - apt-get update
    - apt-get install -y build-essential
before_scriptafter_script
  • 作用 :定义在所有 script 命令前/后运行的默认脚本。

  • 规则

    • 全局定义(在顶层)时,适用于所有任务。
    • 局部定义(在单个任务中)时,覆盖全局配置。
  • 示例

    Yaml 复制代码
    before_script:
      - echo "Global setup before each job"
    
    build_job:
      script:
        - echo "Build step"
      after_script:
        - echo "Cleanup after build"
image(Docker 镜像)
  • 作用:指定任务运行的环境(Docker 镜像)。

  • 规则

    • 可全局定义(所有任务默认使用)。
    • 也可单独为某个任务指定。
  • 示例

    ymal 复制代码
    image: node:18
    
    build_job:
      image: maven:3.8.4
      script:
        - mvn clean package
cacheartifacts**
  • cache :缓存依赖(如 node_modules),加速后续任务。

    Yaml 复制代码
    cache:
      paths:
        - node_modules/
  • artifacts:保存构建产物(如编译结果),供后续阶段使用。

    Yaml 复制代码
    artifacts:
      paths:
        - dist/
onlyexcept
  • 作用:控制任务在哪些分支或事件下触发。

  • 示例

    Yaml 复制代码
    build_job:
      only:
        - main        # 仅当推送到 main 分支时执行
        - tags        # 仅当打标签时执行
      except:
        - develop     # 排除 develop 分支
variables(变量)
  • 作用:定义自定义变量或覆盖预定义变量。

  • 示例

    Yaml 复制代码
    variables:
      ENV_VAR: "production"
      DOCKER_IMAGE: "my-app:${CI_COMMIT_REF_NAME}"
  • 变量优先级顺序

    • 任务中定义的变量
    • 全局定义的变量
    • 预定义变量(如$CI_COMMIT_REF_NAME)

多行命令格式

|(文字模式)
  • 每行作为独立命令执行。

  • 示例

    Yaml 复制代码
    script:
      - |
        echo "First line"
        echo "Second line"
>(折叠模式)
  • 将多行合并为一行(适合长命令)。

  • 示例

    Yaml 复制代码
    script:
      - > 
        echo "This is a long command that will be folded into a single line."

全局配置和特殊指令

include
  • 引入其他 .gitlab-ci.yml 文件或模板。

  • 示例

    Yaml 复制代码
    include:
      - remote: "https://example.com/ci-templates.yml"
workflow
  • 控制流水线整体行为(如触发条件)。

  • 示例

    Yaml 复制代码
    workflow:
      rules:
        - if: '$CI_COMMIT_BRANCH == "main"'
          when: always
        - when: never

示例

yaml 复制代码
# GitLab CI/CD 配置 - 使用Docker执行器和pnpm
# 镜像
image: node:20-alpine

variables:
	# 使用pnpm包管理器
	CI: 'true'
	NODE_ENV: 'test'
	# pnpm配置
	PNPM_VERSION: '10.12.4'
	# 构建输出目录
	BUILD_DIR: dist
	# 部署目标目录
	DEPLOY_PATH: /usr/share/nginx/html
	# SSH配置
	SSH_HOST: 'xxx.xxx.x.xxx'
	SSH_USER: 'root'  

# 全局缓存配置
cache:
	key: ${CI_COMMIT_REF_SLUG}-pnpm-store
	paths:
		- .pnpm-store/

stages:
	- build
	- deploy

# 构建阶段
build-project:
	stage: build
	before_script:
		- echo "设置pnpm环境..."
		- npm install -g pnpm@${PNPM_VERSION} || echo "pnpm已安装"
		- pnpm config set store-dir .pnpm-store
	script:
		- echo "构建项目..."
		- pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
		# 修改build命令,使用正确的vite.js路径
		- node --max_old_space_size=5120 node_modules/vite/bin/vite.js build --mode test
	# 上传构建产物
	artifacts:
		paths:
			- dist/
			expire_in: 7 days # 自定义过期时间
	only:
		- dev
	tags:
		- docker,vue

  

# 部署到测试环境 
deploy-to-test:
	stage: deploy
	script:
		- echo "🚀 开始本地部署到 ${DEPLOY_PATH}"
		# 检查目标目录是否存在
		- 'if [ ! -d "${DEPLOY_PATH}" ]; then echo "创建部署目录: ${DEPLOY_PATH}"; mkdir -p ${DEPLOY_PATH}; fi'
		- echo "🛡️ 检查目录权限..."
		- 'touch ${DEPLOY_PATH}/.permission-test && rm ${DEPLOY_PATH}/.permission-test && echo "目录权限正常" || (echo "错误: 没有写入${DEPLOY_PATH}的权限" && exit 1)'
		- echo "📤 开始同步文件..."
		- cp -r dist/* ${DEPLOY_PATH}/
		- echo "✅ 文件同步完成"
		- echo "🔍 验证部署..."
		- echo "服务器磁盘使用情况:" && df -h ${DEPLOY_PATH}
		- echo "部署目录内容:" && ls -la ${DEPLOY_PATH}/ | head -10
		- echo "index.html大小:" && du -sh ${DEPLOY_PATH}/index.html 2>/dev/null || echo "index.html不存在"
		- echo "🎉 部署成功,访问地址 http://${SSH_HOST}"
	environment:
		name: test
		url: http://${SSH_HOST}
	dependencies:
		- build-project
	only:
		- dev
	tags:
		- docker,vue

最后

CI/CD的方案还有很多,比如GitHub ActionsJenkinsSpinnaker什么的,也可以尝试去实践一下。我认为掌握一些这个知识是非常重要的,即使根本就轮不到你来做这个事,大一些的团队应该都有专门的负责人来做这个事。

共勉。

相关推荐
齐杰拉2 小时前
源码精读:拆解 ChatGPT 打字机效果背后的数据流水线
前端·chatgpt
文心快码BaiduComate2 小时前
“一人即团队”——一句话驱动智能体团队
前端·后端·程序员
我是ed2 小时前
# vue3 实现前端生成水印效果
前端
IAtlantiscsdn2 小时前
Redis7底层数据结构解析
前端·数据结构·bootstrap
小枫编程2 小时前
Spring Boot 与前端文件上传跨域问题:Multipart、CORS 与网关配置
前端·spring boot·后端
uhakadotcom3 小时前
入门教程:如何编写一个chrome浏览器插件(以jobleap.cn收藏夹为例)
前端·javascript·面试
捡芝麻丢西瓜3 小时前
SPM 之 混编(OC、Swift)项目保姆级教程(Swift Package Manager)
前端
我是天龙_绍3 小时前
cdn是个啥?
前端
南雨北斗3 小时前
VSCode三个TS扩展工具介绍
前端