首先来回忆一下之前是怎么发版的?仅有测试和正式两个环境的情况下,大致如下
- 开发完成,开发分支提交代码
- 合并代码到发版(测试)的分支
- 打包构建
- 部署到服务器(测试)
- 进行测试
- 以上步骤不断重复且较为频繁,直到测试通过
- 合并代码到发版(生产)的分支
- 打包构建
- 部署到服务器(生产)
以上步骤,提交代码和合并代码其实还是比较省时的,但是构建和部署是需要花比较多的时间的也是比较容易出错的,尤其是比较大型的项目,那有没有办法让打包构建和部署,甚至是自动测试,交给一些工具来做呢?我们只需要提交代码,工具检测到代码变了,就自动帮我打包构建、自动化测试并部署到服务器。这样一来程序员就可以少干一些事情,同时也能达到一个快捷、稳定、可靠的效果。
基于这个出发点,就开始了CI/CD的学习,本次的目标是:完成提交代码后,能自动构建并部署到测试环境
CI/CD
-
CI: Continuous Integrating,持续集成,这一过程就是将代码变更提交到发版分支(如
master
或test
)等,并通过自动化工具进行构建、测试和验证。- 主要目标:确保每次代码变更都能快速的集成到对应分支,避免代码冲突并尽早发现缺陷
- 关键步骤
- 代码提交:开发人员将代码推送到版本控制系统
- 自动构建:系统自动拉取代码并编译(如构建 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
关键信息获取地方
- 登录GitLab网页界面。
- 进入 项目 > 设置 > CI/CD。
- 展开 Runners 部分,点击 New project runner。
- 复制生成的 注册令牌(Registration token) 和 GitLab实例URL。
bash
# 交互式注册
sudo gitlab-runner register
按提示输入信息
- GitLab实例URL (例如:
https://gitlab.com/
)。 - 注册令牌(从GitLab获取)。
- Runner描述 (例如:
my-centos-runner
)。 - Runner标签 (用逗号分隔,例如:
centos,shell
)。 - 执行器类型 (推荐使用
shell
或docker
)shell
:直接操作操作系统和文件,直接在宿主机运行命令,更简单docker
:通过容器实现资源隔离,避免版本冲突,更推荐
- 选择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
可并行执行。
- 所有
-
示例 :
Yamlstages: - 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_script
和 after_script
-
作用 :定义在所有
script
命令前/后运行的默认脚本。 -
规则 :
- 全局定义(在顶层)时,适用于所有任务。
- 局部定义(在单个任务中)时,覆盖全局配置。
-
示例 :
Yamlbefore_script: - echo "Global setup before each job" build_job: script: - echo "Build step" after_script: - echo "Cleanup after build"
image
(Docker 镜像)
-
作用:指定任务运行的环境(Docker 镜像)。
-
规则 :
- 可全局定义(所有任务默认使用)。
- 也可单独为某个任务指定。
-
示例 :
ymalimage: node:18 build_job: image: maven:3.8.4 script: - mvn clean package
cache
和 artifacts
**
-
cache
:缓存依赖(如node_modules
),加速后续任务。Yamlcache: paths: - node_modules/
-
artifacts
:保存构建产物(如编译结果),供后续阶段使用。Yamlartifacts: paths: - dist/
only
和 except
-
作用:控制任务在哪些分支或事件下触发。
-
示例 :
Yamlbuild_job: only: - main # 仅当推送到 main 分支时执行 - tags # 仅当打标签时执行 except: - develop # 排除 develop 分支
variables
(变量)
-
作用:定义自定义变量或覆盖预定义变量。
-
示例 :
Yamlvariables: ENV_VAR: "production" DOCKER_IMAGE: "my-app:${CI_COMMIT_REF_NAME}"
-
变量优先级顺序
- 任务中定义的变量
- 全局定义的变量
- 预定义变量(如$CI_COMMIT_REF_NAME)
多行命令格式
|
(文字模式)
-
每行作为独立命令执行。
-
示例 :
Yamlscript: - | echo "First line" echo "Second line"
>
(折叠模式)
-
将多行合并为一行(适合长命令)。
-
示例 :
Yamlscript: - > echo "This is a long command that will be folded into a single line."
全局配置和特殊指令
include
-
引入其他
.gitlab-ci.yml
文件或模板。 -
示例 :
Yamlinclude: - remote: "https://example.com/ci-templates.yml"
workflow
-
控制流水线整体行为(如触发条件)。
-
示例 :
Yamlworkflow: 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 Actions
、Jenkins
、Spinnaker
什么的,也可以尝试去实践一下。我认为掌握一些这个知识是非常重要的,即使根本就轮不到你来做这个事,大一些的团队应该都有专门的负责人来做这个事。
共勉。