GitLab CI/CD 落地:Runner 注册与流水线模板实战

上一篇文章里 GitLab 平台已经跑起来了,但开发还在找我手动拉代码、打包、传服务器。有次周五下午发版,我连做了六次”拉码、打包、scp、解压、换软链”,第七次是回滚。当天晚上我就把流水线搭了起来——人肉发布这件事,重复三次以上就该交给机器。

一、GitLab CI 的基本模型

GitLab CI/CD 的逻辑不复杂:

  • 代码仓库里放一个 .gitlab-ci.yml 文件,描述”要做什么”;
  • Runner 是执行这些任务的代理,注册到 GitLab 之后领任务、干活、回报结果;
  • 推送代码或创建 MR 触发流水线,按阶段顺序执行。

三个角色的分工:GitLab 负责调度,Runner 负责执行,yml 文件负责定义流程。运维的活主要在两块:把 Runner 管好,把 yml 模板写好给开发用。开发不需要懂 Runner 装在哪,只要知道”推代码之后跑去哪看结果”。

二、Runner 的注册与执行器选择

Runner 可以注册在三个级别:项目级、组级、实例级。项目级最省事,实例级最通用,企业里一般按”组级 + 标签区分用途”来铺。

1
2
3
4
5
# 1. 注册(在 Runner 所在机器上执行)
# token 来源:项目 → Settings → CI/CD → Runners
gitlab-runner register
# 交互项:GitLab 地址 http://10.0.8.200:8090
# token、描述、标签(如 deploy、test)、执行器

执行器(executor)的选择我踩过一次坑,值得展开说:

执行器 特点 适用场景
shell 直接跑在 Runner 机器上,起步最快 简单构建,但环境互相污染
docker 每个任务一个容器,环境隔离 大多数场景推荐
kubernetes 跑在 K8s 集群里,弹性扩缩 大型团队、构建量大

一开始我用 shell 执行器图省事,结果两个项目依赖的 Node 版本打架,构建时好时坏。换成 docker 执行器之后,每个任务生成一个干净容器,这类玄学问题直接消失。”环境隔离”这四个字,值这一次改造。

给 Runner 打标签是为了分流:镜像构建的活儿给标签 build 的 Runner,部署任务给标签 deploy 的 Runner——生产部署的 Runner 可以放到内网、单独配权限,构建的放外边跑,互不影响。

1
2
3
4
# 2. 状态检查三连,排障时常用
gitlab-runner status
gitlab-runner verify # 验证和 GitLab 的连通性
gitlab-runner list # 列出所有已注册的 Runner

三、.gitlab-ci.yml 模板逐段拆解

这份模板是给开发的标准件,我一般直接放进项目根目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
stages:            # 流水线阶段,按顺序执行
- build
- test
- deploy

build-job: # 构建:编译 + 打镜像
stage: build
tags: [build] # 指定带 build 标签的 Runner 执行
script:
- echo "编译代码"
- docker build -t registry.example.com/app:$CI_COMMIT_SHORT_SHA .
- docker push registry.example.com/app:$CI_COMMIT_SHORT_SHA
only:
- main # 只在 main 分支触发

test-job: # 测试:单元测试 / 代码检查
stage: test
tags: [test]
script:
- echo "运行测试"

deploy-job: # 部署:推送到测试 / 生产环境
stage: deploy
tags: [deploy]
script:
- echo "部署到服务器"
when: manual # 生产环境手动确认,防手滑
only:
- main

几个关键点:

  • stages 定义阶段顺序,同一阶段的 Job 并行,前一阶段全绿才进下一阶段;
  • tags 决定这个 Job 由哪台 Runner 执行,写错标签的直接后果是任务一直挂着不跑——这是新人最常问的”为什么流水线卡住了”的原因之一;
  • only: main 限定触发分支,避免 feature 分支一推就触发部署;
  • when: manual 给生产部署加一道人工闸门,点一下才执行。

镜像标签用 $CI_COMMIT_SHORT_SHA 是我坚持的规范:每次构建的镜像都可以追溯到具体提交,出问题能精确定位到”哪一次构建、哪一次提交”,比 latest 标签靠谱得多。

四、变量与密钥管理

1
2
# 项目 → Settings → CI/CD → Variables
# 存数据库密码、部署密钥这类敏感信息,勾选 Protected + Masked

三条红线:

  1. 敏感信息一律走 CI/CD Variables,禁止写死在 .gitlab-ci.yml 里——yml 是跟代码一起进仓库的,写密码等于把密码提交进 git 历史;
  2. 变量勾 Protected 之后,只有保护分支上的流水线能读到,feature 分支拿不到生产凭据;
  3. Masked 让日志自动打码,但别依赖它——调试时 echo $DB_PASSWORD 这种动作一旦习惯化,迟早出事。

五、日常运维的三个高频问题

Runner 离线。处理顺序:gitlab-runner status 看进程 → gitlab-runner verify 测连通 → 检查机器负载和网络到 GitLab 的通路 → 确认注册 token 没被清理。最隐蔽的一种是 token 被管理员重置后没重新注册,表现是”进程在跑但一直不领任务”。

流水线卡住不执行。先看是不是 tags 写错或者对应 Runner 全忙:Runner 的并发数(config.toml 里的 concurrent)是固定的,一个 Runner 同时只接有限个任务,构建高峰时会排队。第二个常见原因是磁盘满——Runner 机器被构建缓存和镜像撑爆,任务起不来。

构建慢。三个方向:加 cache 关键字缓存依赖目录;换更大的 Runner 机器;给 Docker 配镜像加速。构建时间从 8 分钟压到 3 分钟那次,主要收益就来自依赖缓存。

小结

GitLab CI 落地就三件事:Runner 用 docker 执行器保证环境干净、yml 模板把阶段和触发条件写死、密钥全部进 Variables。这三步做完,开发侧的体验就是”推代码 → 看结果”,运维侧也不用再当人肉发布机。