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 流程的部署。别急,接着往下看。