1. GitLab CI 简介与核心概念

说实话,我第一次接触 GitLab CI 的时候,心里是有点抵触的。为什么?因为当时我已经习惯了 Jenkins 那套东西,总觉得换个工具又要重新学一遍。但真正用起来之后,我发现其实没那么复杂——说白了,GitLab CI 就是一个把代码仓库和自动化流水线绑在一起的工具。

GitLab CI 是 GitLab 自带的持续集成/持续部署系统。你的代码推到仓库,它就能自动触发构建、测试、部署。这套逻辑在 DevOps 里其实挺常见的,但 GitLab 把整个流程嵌入了代码管理本身,省掉了单独搭 Jenkins 那一套的麻烦。

我记得有个同事问我:「这东西和 Jenkins 到底有什么区别?」我当时的回答是:「Jenkins 像个万能工具箱,什么都能做,但你要自己搭。GitLab CI 像个专车,直接能跑,但你得按它的规则来。」嗯,这个比喻可能不太严谨,但你想想看,对于 Nextflow 这种计算密集型的工作流来说,我们需要的就是一个能快速拉起环境的通道,而不是一个什么都要手动配的庞然大物。

1.1 GitLab CI 是怎么工作的?

一句话概括:你往仓库里放一个 .gitlab-ci.yml 文件,GitLab 读到它之后,就会按你写的内容去跑一连串的任务。

这背后的机制其实不神秘。每个任务(Job)会被分配给一个 Runner 去执行。Runner 可以是 GitLab 官方的共享 Runner,也可以是你自己的服务器。我个人习惯在公司的内网里搭自己的 Runner,因为 Nextflow 流程常常需要访问大型数据集甚至是数据库连那种配置,共享 Runner 很难满足这种需求。

我曾经遇到过一个很典型的坑:流程跑了一半,日志里突然报错说某个文件找不到。后来排查了半天才发现,问题出在 Runner 上——那个 Runner 被多个项目共用了,缓存路径乱糟糟的,导致依赖下载不全。从那以后,我开始用 tag 来约束 Runner 的使用场景,让每个关键流程都有专属的执行环境。

核心要点:GitLab CI 的本质是事件驱动的任务调度。你提交代码,它响应。你把 .gitlab-ci.yml 写好,它按剧本执行。不复杂,但细节很多。

1.2 几个你必须搞明白的核心概念

我认为,学 GitLab CI 不用急着背命令,先把下面这几个概念捋顺了,后面自然就通了。

1.2.1 .gitlab-ci.yml 文件

这是整个 CI 的配置中心。所有规则、步骤、环境变量、依赖关系,都在这个文件里定义。YAML 格式,缩进要严格,否则直接报错。我刚开始写的时候被缩进坑过好几次——一个空格的差别就能让整个流水线罢工。

典型结构长这样:

stages:
  - build
  - test
  - deploy

build-job:
  stage: build
  script:
    - echo "编译代码"

test-job:
  stage: test
  script:
    - echo "跑测试"

deploy-job:
  stage: deploy
  script:
    - echo "部署到服务器"

1.2.2 Job 与 Stage

Job 是最小的执行单元。每个 Job 里定义一个 script 块,里面写你要执行的命令。Stage 是 Job 的分组,定义了执行的先后顺序。

举个例子:如果 stage 有 build、test、deploy 三个,那么所有 build 类型的 Job 会先并行跑完,再轮到 test,最后再轮到 deploy。同一个 stage 里的 Job 是并行的,不同的 stage 之间是串行的。

概念 说明 我的建议
Job 最小的执行单元,每个 Job 跑一个任务 一个 Job 只做一件事,别塞太多命令进去
Stage Job 的分组,决定执行顺序 stage 命名要直观,别用 stage-1、stage-2 这种
Pipeline 一次完整的 CI 执行流 一次 Pipeline 包含所有 stage 和 Job 的完整执行

1.2.3 Pipeline

当一个 commit 被推送到仓库,GitLab 会创建一个 Pipeline。这个 Pipeline 里包含了所有需要执行的 Job。你可以把 Pipeline 理解成一次自动化流程的“快照”——从代码提交到结果输出,整个过程都被记录下来了。

我记得有一次调试一个 Nextflow 流程的部署问题时,就是靠 Pipeline 的日志一步步回溯到 Runner 环境配置错误的。那一刻我觉得,嗯,Pipeline 这东西不只是一个进度条,它是你排查问题的第一道防线。

1.2.4 Runner

Runner 是真正干活的人。它是独立运行在某个机器上的程序,接受 GitLab 的指令,然后到指定的环境里去跑你的 Job。

Runner 有三种类型:

  • 共享 Runner:所有项目都能用,由 GitLab 官方或运维团队提供。
  • 组 Runner:某个组内的项目共享,适合团队级别。
  • 专用 Runner:只服务于一个项目,隔离性最好。

我个人强烈推荐,如果你的 Nextflow 流程涉及敏感数据或大型计算,一定要用专用 Runner,而且最好配上 Docker 或 Kubernetes 执行器。为什么?因为 Nextflow 本身对计算资源的管理非常细粒度,如果 Runner 环境不稳定,后面的流程全白搭。

安装一个最简单的 Runner:

docker run -d --name gitlab-runner \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

# 然后注册,把 Runner 关联到你的项目
docker exec -it gitlab-runner gitlab-runner register

1.2.5 环境变量与缓存

环境变量是用来传递配置信息的。比如 Nextflow 流程需要的参数、数据库地址、认证信息等,都可以放在环境变量里。GitLab CI 允许你在项目的 Settings -> CI/CD -> Variables 里设置,也可以在 .gitlab-ci.yml 里直接写 variables 字段。

缓存呢,是用来加速的。比如你的项目每次都要下载一堆依赖,配置一下缓存就可以避免重复下载。

技巧:把敏感信息放在 GitLab 的 CI/CD Variables 里,而不是硬编码在 YAML 文件里。我曾经见过有人把数据库密码明文写在 .gitlab-ci.yml 里,然后不小心把它上传到了公开仓库……那是真正的灾难。

下面是一个带缓存的例子,适合 Node.js 项目,但思路通用:

variables:
  NODE_VERSION: "18"
  NEXTFLOW_VERSION: "24.04.4"

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/

before_script:
  - node --version
  - npm install

test:
  script:
    - npm run test

对于 Nextflow 流程来说,缓存通常用来存 Singularity/Docker 镜像或者 Conda 环境。嗯,这一步做得好,能让整个 Pipeline 快上不少。

1.2.6 Artifacts

Artifacts 是 Job 产出的文件,比如编译后的包、测试报告、日志。这些文件可以在 Job 之间传递,也可以下载下来查看。

举个例子:用 Nextflow 跑完一个分析流程,可能会生成结果文件。你可以把结果文件设置成 Artifacts,这样后续的部署 Job 就能直接拿到它,或者你也可以在 GitLab 的 Pipeline 页面直接下载。

注意:Artifacts 默认有过期时间,别指望它做长期存储。对于 Nextflow 的最终结果,我建议另外找地方存,比如 S3 或者公司的 NAS。

总结一句话:GitLab CI 不是魔法,它只是一套规则清晰的自动化框架。你把文件写好,它替你干活。后面的章节里,我们会用这套东西来驱动 Nextflow 流程的部署。别急,接着往下看。

GitLab CI 核心概念关系图 代码提交 创建 Pipeline 读取 .gitlab-ci.yml 解析 Stage 和 Job 分配 Runner 执行 Job 共用 Runner 专用 Runner 执行脚本(script 块) 使用 Environment 使用 Cache 生成 Artifacts Pipeline 完成,通知结果 多个 Job 可在同一 Stage 并行 Stage 之间按顺序串行执行