GitLab CI/CD 从入门到精通 本文从原理讲起逐步深入到实战帮你彻底搞懂 GitLab CI/CD第一章CI/CD 是什么第二章GitLab CI/CD 的工作原理第三章Runner —— 真正的执行者第四章Runner 安装与注册实操第五章.gitlab-ci.yml —— 定义流水线第六章Pipeline 执行流程详解第七章核心关键词参考第八章高级用法第九章最佳实践第十章常见问题排查第一章CI/CD 是什么1.1 三个概念术语全称意思CIContinuous Integration持续集成频繁地把代码合并到主干自动构建和测试CDContinuous Delivery持续交付自动部署到测试/预发布环境CDContinuous Deployment持续部署自动部署到生产环境1.2 没有 CI/CD 的痛苦开发写完代码 → 手动打包 → 发给运维 → 运维手动部署 ↓ 测试发现bug → 通知开发 → 改完再来一遍 ↓ 每次发版都要折腾半天还容易出错1.3 有了 CI/CD 之后git push → GitLab 自动触发 Pipeline → 自动编译 (build) → 自动跑测试 (test) → 自动打包镜像 (package) → 自动部署 (deploy) → 通知结果全程自动化不需要人肉操作。第二章GitLab CI/CD 的工作原理2.1 整体架构┌─────────────────────────────────────────────────────┐ │ GitLab Server │ │ (你的 GitLab 实例例如 gitlab.com) │ │ │ │ 1. 接收 git push │ │ 2. 解析 .gitlab-ci.yml │ │ 3. 创建 PipelineJob → Stage → Pipeline │ │ 4. 把 Job 分配给 Runner │ │ 5. 接收 Runner 返回的执行结果 │ │ 6. 展示日志 / 通知状态 │ └──────────────────────┬──────────────────────────────┘ │ 通信HTTPS ▼ ┌─────────────────────────────────────────────────────┐ │ GitLab Runner │ │ (安装在你自己的服务器上的代理程序) │ │ │ │ 1. 轮询或长连接等待 Job │ │ 2. 收到 Job 后执行指令 │ │ 3. 把日志和结果反馈给 GitLab │ │ 4. 执行完后继续等待下一个 Job │ └─────────────────────────────────────────────────────┘2.2 核心通信机制Runner 通过HTTPS或WebSocket与 GitLab Server 通信Polling轮询Runner 每隔几秒问 GitLab 有活干吗WebSocket长连接GitLab 有活直接推给 RunnerGitLab 15.10 支持2.3 一句话理解GitLab 大脑接收代码、解析配置、规划流程、分派任务 Runner 手脚实际干活的编译、测试、部署 ​ .gitlab-ci.yml 施工图纸告诉大脑每一步怎么做第三章Runner —— 真正的执行者3.1 Runner 是什么Runner 是一个独立的程序GitLab Runner安装在你自己的服务器上。它的唯一职责是等待 GitLab 分配 Job然后执行 Job 里的命令把结果报告回去。3.2 Runner 的类型类型作用域适用场景Shared Runner整个 GitLab 实例所有项目可用公司级统一 RunnerGroup Runner某个 Group 下所有项目可用部门级 RunnerSpecific Runner绑定到特定项目项目专属 Runner3.3 Executor —— 以什么方式执行 JobRunner 本身只是个调度器真正执行命令的是Executor。GitLab Runner 支持多种 ExecutorExecutor说明适用场景Shell直接在宿主机上执行命令最简单不需要 DockerDocker每个 Job 启动一个 Docker 容器执行最常用环境隔离Kubernetes在 K8s Pod 中执行K8s 集群环境Docker Machine自动创建云服务器执行大规模动态扩缩容SSH通过 SSH 连接到远程服务器执行远程执行Parallels在虚拟机中执行Mac 环境95% 的场景用 Docker Executor本文也以 Docker 为主。3.4 Runner 的工作流1. Runner 启动向 GitLab 注册自己 2. Runner 持续等待轮询或 WebSocket 3. GitLab 有 Job 产生 → 分配给 Runner 4. Runner 按 Executor 类型创建运行环境 5. Runner 执行 .gitlab-ci.yml 中定义的 script 6. Runner 实时上传日志 → GitLab 7. Job 完成 → Runner 把结果成功/失败报告 GitLab 8. Runner 回到等待状态3.5 一个 Job 在 Runner 上的完整执行过程Docker ExecutorGitLab 分配 Job → Runner 收到 Job 信息 ↓ Runner 拉取指定 Docker 镜像image: node:20-alpine ↓ Runner 启动容器把项目代码挂载进去 ↓ Runner 按顺序执行命令 1. before_script如果有 2. script主要任务 3. after_script如果有 ↓ Runner 收集 artifacts如果有 ↓ Runner 删除容器清理环境 ↓ Runner 把结果报告给 GitLab第四章Runner 安装与注册实操4.1 在 Linux 上安装Ubuntu / CentOS# 添加 GitLab 官方仓库 # Ubuntu/Debian curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash ​ # CentOS/RHEL curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash ​ # 安装 sudo apt-get install gitlab-runner # Ubuntu sudo yum install gitlab-runner # CentOS ​ # 查看状态 sudo gitlab-runner status4.2 在 Windows 上安装# 1. 下载 exe # 访问 https://gitlab-runner-downloads.s3.amazonaws.com/latest/index.html # 下载 gitlab-runner-windows-amd64.exe ​ # 2. 重命名并放到某个目录例如 C:\GitLab-Runner # 改名为 gitlab-runner.exe ​ # 3. 安装为 Windows 服务 cd C:\GitLab-Runner .\gitlab-runner.exe install .\gitlab-runner.exe start4.3 在 macOS 上安装sudo curl --output /usr/local/bin/gitlab-runner \ https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64 sudo chmod x /usr/local/bin/gitlab-runner4.4 获取注册 Token注册 Runner 需要 Token获取方式GitLab 项目 → Settings → CI/CD → → Runners → Expand → → Specific runners → 复制 Registration token不同范围 Runner 的 Token 位置Runner 类型Token 在哪里Project项目项目 → Settings → CI/CD → RunnersGroup群组群组 → Settings → CI/CD → RunnersInstance实例管理员 → Admin Area → CI/CD → Runners4.5 注册 Runner注册为 Docker Executor最常用sudo gitlab-runner register \ --non-interactive \ --url https://gitlab.com \ # 你的 GitLab 地址 --token 你的注册Token \ # 上一步获取的 Token --description my-docker-runner \ # 描述名称 --executor docker \ # 使用 Docker Executor --docker-image alpine:latest \ # 默认镜像 --docker-privileged \ # 特权模式dind 需要 --tag-list docker,linux \ # 标签重要 --run-untaggedtrue # 是否允许运行无标签 Job注册为 Shell Executor简单但隔离性差sudo gitlab-runner register \ --non-interactive \ --url https://gitlab.com \ --token 你的注册Token \ --description my-shell-runner \ --executor shell \ --tag-list shell,linuxShell Executor直接在宿主机上执行命令简单但不安全只推荐个人项目或测试使用。Windows 上注册# PowerShell 管理员模式 .\gitlab-runner.exe register \ --non-interactive \ --url https://gitlab.com \ --token 你的注册Token \ --description windows-runner \ --executor shell \ --tag-list windows4.6 注册后的配置文件注册完成后Runner 信息保存在/etc/gitlab-runner/config.tomlLinux或config.toml同目录[[runners]] name my-docker-runner url https://gitlab.com token xxxxxx executor docker [runners.docker] image alpine:latest privileged true volumes [/cache]4.7 Runner 常用管理命令# 查看状态 sudo gitlab-runner status ​ # 启动 / 停止 / 重启 sudo gitlab-runner start sudo gitlab-runner stop sudo gitlab-runner restart ​ # 查看运行中的 Job sudo gitlab-runner list ​ # 调试模式运行前台看详细日志 sudo gitlab-runner run --debug ​ # 卸载服务 sudo gitlab-runner uninstall ​ # 验证配置 sudo gitlab-runner verify4.8 确定 Runner 是否可用注册后在 GitLab 上确认项目 → Settings → CI/CD → Runners → 应该能看到刚刚注册的 Runner → 绿色圆点 在线 灰色 离线第五章.gitlab-ci.yml —— 定义流水线5.1 这个文件是干嘛的# .gitlab-ci.yml 是一份 YAML 格式的施工图 # 放在项目根目录 # GitLab 检测到这个文件后每次代码推送都会按图执行5.2 最小可运行示例# 最简单的流水线 —— 只需要一个 Job job1: script: - echo Hello GitLab CI/CD!效果每次git push后GitLab 自动创建一个 PipelineRunner 执行这个 Job打印一行字。5.3 三个核心概念# ┌──────────────────────────────────────────────────────────┐ # │ Pipeline流水线 │ # │ 一次 CI/CD 执行的完整过程 │ # │ │ # │ ├── Stage阶段 │ # │ │ 属于同一阶段的任务Job可以并行执行 │ # │ │ │ # │ │ ├── Job任务 │ # │ │ │ 具体的执行单元编译、测试、部署 │ # │ │ │ - 包含 script执行命令 │ # │ │ │ - 在 Runner 上运行 │ # │ │ │ - 有独立的日志和控制 │ # │ │ │ │ # │ │ ├── Job2 │ # │ │ │ 和 Job1 同 Stage所以并行执行 │ # └──────────────────────────────────────────────────────────┘5.4 标准三段式 Pipelinestages: # 定义阶段顺序执行 - build - test - deploy ​ # 执行时序 # 时间 → # build: [build-job1] [build-job2] ← 这两个并行 # test: [test-job] ← build 全部完成后才启动 # deploy: [deploy-job] ← test 完成后才启动5.5 一个完整的真实示例# ──── 告诉 GitLab 分几个阶段 ──── stages: - build - test - deploy ​ # ──── 定义各个阶段要做什么 ──── ​ # build 阶段编译代码 build-job: stage: build script: - echo 正在编译... - mkdir -p output - echo 编译产物 output/app.jar artifacts: # 保留产物给后续阶段 paths: - output/ ​ # test 阶段运行测试 test-job: stage: test script: - echo 正在运行测试... - echo 测试通过 ​ # deploy 阶段部署 deploy-job: stage: deploy script: - echo 正在部署到服务器... only: - main # 只有 main 分支才执行部署第六章Pipeline 执行流程详解6.1 从 git push 到部署完成的完整链路你执行 git push │ ▼ GitLab 收到推送 │ ▼ GitLab 检查项目根目录有没有 .gitlab-ci.yml ├── 有 → 创建 Pipeline └── 无 → 什么都不做 │ ▼ GitLab 解析 .gitlab-ci.yml确定 ├── 要创建哪些 Job ├── 每个 Job 属于哪个 Stage ├── 每个 Job 需要满足什么条件才执行rules └── 每个 Job 需要什么样的 Runnertags │ ▼ GitLab 把 Job 放入待执行队列 │ ▼ Runner 轮询或通过 WebSocket 收到有 Job 的通知 │ ▼ Runner 检查自己是否满足 Job 的要求 ├── 标签tags是否匹配 └── Executor 是否可用 │ ▼ Runner 执行 Job ├── 拉取 Docker 镜像Docker Executor ├── 启动容器 ├── 克隆项目代码 ├── 还原缓存cache ├── before_script ├── script主要任务 ├── after_script ├── 保存 artifacts ├── 上传缓存cache └── 清理容器 │ ▼ Runner 把日志和结果实时报告给 GitLab │ ▼ GitLab 更新 Pipeline 状态 │ ▼ 当前 Stage 所有 Job 都成功后 → 启动下一个 Stage 失败 → 停止 Pipeline默认行为 │ ▼ 所有 Stage 都完成 → Pipeline 结束 │ ▼ 触发后续动作如有配置 ├── 发送通知邮件、Slack、Webhook └── 触发下游 Pipeline6.2 触发 Pipeline 的方式触发方式说明对应 $CI_PIPELINE_SOURCEgit push推送代码push创建 MR创建/更新合并请求merge_request_eventWeb UI在 GitLab 页面手动触发web定时任务按计划触发scheduleAPI通过 API 调用apiTag 推送推送标签push附加 $CI_COMMIT_TAG触发 Token跨项目触发triggerPipeline 完成上游 Pipeline 完成后pipeline6.3 阶段Stage的执行规则Stage 1 (build) Stage 2 (test) Stage 3 (deploy) ┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐ │ Job A ──并行───► │ │ Job C │ │ Job E │ │ Job B ──并行───► │ │ Job D │ │ │ └────────┬───────────┘ └────────┬───────────┘ └────────┬───────────┘ ▼ ▼ ▼ 全部成功 → 进入下一阶段 全部成功 → 进入下一阶段 全部成功 → Pipeline ✅ 任一失败 → Pipeline ❌ 任一失败 → Pipeline ❌ 任一失败 → Pipeline ❌例外情况allow_failure: true→ 该 Job 失败不影响 Pipelineneeds→ 允许跳过阶段等待形成 DAG 图第七章核心关键词参考7.1stages— 定义阶段顺序stages: - build - test - deploy - .post # 前缀 . 表示隐藏阶段用于模板7.2script— 执行的命令job: script: - echo 第一行命令 - cd src make build - echo 完成7.3image— 运行环境Docker Executor 专用# 指定 Docker 镜像作为运行环境 default: image: node:20-alpine # 全局默认镜像 ​ test-job: image: python:3.11-slim # 单个 Job 覆盖 script: - python -m pytest7.4tags— 选择哪个 Runner 执行job: tags: # 只有带有这些标签的 Runner 才能执行这个 Job - docker - linux非常重要如果 Job 指定了tags但没有任何 Runner 带有这些标签Job 会卡住stuck7.5rules— 条件控制控制 Job 在什么情况下执行或不执行job: rules: - if: $CI_PIPELINE_SOURCE merge_request_event # MR 事件 - if: $CI_COMMIT_BRANCH main # main 分支 - if: $CI_COMMIT_TAG # 打标签 when: manual # 需要手动触发 - when: never # 其他情况不执行when的可选值值含义on_success默认前面的 Stage 都成功才执行always总是执行即使前面失败manual需要人工点击执行delayed延迟一定时间后执行never不执行7.6artifacts— Job 的产物build-job: script: - mvn package artifacts: paths: # 保留的文件 - target/*.jar exclude: # 排除的文件 - target/*-sources.jar expire_in: 1 week # 过期时间自动清理 when: always # 即使 Job 失败也保留 reports: # 特殊报告在 UI 上展示 junit: target/test-report.xml7.7cache— 依赖缓存cache: key: ${CI_COMMIT_REF_SLUG} # 按分支名缓存 paths: - node_modules/ # 要缓存的文件/目录 policy: pull-push # pull:只下载 | push:只上传 | pull-push:都做artifacts和cache的区别artifactscache用途传递阶段间的产物加速依赖安装生命周期按expire_in过期跨 Pipeline 共享典型使用jar 包、编译产物npm 包、Maven 库是否传给下游默认传递可用dependencies控制不自动传递7.8needs— 跨阶段依赖stages: - build - test - deploy ​ build-a: stage: build script: echo 快 build-b: stage: build script: sleep 30 ; echo 慢 ​ test-a: stage: test needs: [build-a] # build-a 完成后立即执行不等 build-b script: echo 测A ​ test-b: stage: test needs: [build-b] # build-b 完成后才执行 script: echo 测B7.9parallel— 并行矩阵test-job: script: - echo 浏览器: $BROWSER 版本: $VERSION parallel: matrix: - BROWSER: [chrome, firefox] VERSION: [latest, previous] # 生成 2 × 2 4 个并行 Job7.10retry— 失败重试test-flaky: script: - npm test retry: max: 2 # 失败后最多重试 2 次总共 3 次 when: - runner_system_failure # Runner 系统问题 - stuck_or_timeout_failure - unknown_failure7.11timeout— 超时控制default: timeout: 30 minutes ​ long-test: script: - huge-test timeout: 2 hours7.12environment— 部署环境管理deploy: script: - deploy.sh environment: name: production/$CI_COMMIT_BRANCH url: https://example.com action: start # start | stop | prepare第八章高级用法8.1include— 复用其他文件include: # 从同一个仓库引用 - local: /ci-templates/common.yml ​ # 从其他项目引用 - project: my-group/ci-templates file: /templates/deploy.yml ref: v1.0 ​ # 从远程 URL - remote: https://example.com/template.yml ​ # 使用 GitLab 官方模板 - template: Auto-DevOps.gitlab-ci.yml8.2extends— 继承# 定义基座以点开头 隐藏 Job不会被直接执行 .ruby-base: image: ruby:3.2 before_script: - bundle install ​ # 继承并扩展 rspec: extends: .ruby-base script: - bundle exec rspec ​ rubocop: extends: .ruby-base script: - bundle exec rubocop8.3workflow— 控制整个 Pipeline# 在文件最顶部定义控制是否创建 Pipeline workflow: rules: - if: $CI_PIPELINE_SOURCE merge_request_event - if: $CI_COMMIT_BRANCH $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG - when: never # 其他情况不触发 Pipeline8.4 触发下游 Pipeline# 触发同一个项目中的子流水线 trigger-child: trigger: include: child-pipeline.yml strategy: depend ​ # 触发另一个项目的流水线Multi-Project Pipeline trigger-downstream: trigger: project: my-group/another-service branch: main strategy: depend # 等待下游完成8.5resource_group— 互斥执行# 同一时间只允许一个 Job 执行防止并发部署 deploy: script: - deploy.sh resource_group: production environment: name: production第九章最佳实践9.1 Pipeline 设计原则1. build 阶段尽量快 —— 只编译不做多余的事 2. test 阶段覆盖全面 —— 单元测试 集成测试 lint 3. deploy 阶段按环境推进 —— dev → staging → production 4. 敏感操作手动确认 —— 生产部署用 when: manual9.2 效率优化# 1. 用 needs 加速 test-unit: needs: [build-lib] ​ # 2. 合理缓存 cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ - .m2/repository/ policy: pull-push ​ # 3. artifacts 设置过期 artifacts: expire_in: 1 week ​ # 4. 可中断 build: interruptible: true9.3 安全实践# 1. 敏感信息用 Masked 变量 # 在 GitLab UI 中设置Settings → CI/CD → Variables → 勾选 Masked ​ # 2. 限制环境访问 deploy-prod: rules: - if: $CI_COMMIT_BRANCH main when: manual ​ # 3. 不要打印敏感变量 script: - echo $MY_SECRET # ❌ 绝对不要 - ./deploy.sh # ✅ 让脚本读取环境变量9.4 分支策略推荐Trunk-based推荐workflow: rules: - if: $CI_PIPELINE_SOURCE merge_request_event # MR 时构建测试 - if: $CI_COMMIT_BRANCH $CI_DEFAULT_BRANCH # main 时部署 - when: never ​ # MR Pipelinebuild → test # main Pipelinebuild → test → deploy第十章常见问题排查10.1 Pipeline 没有触发❌ git push 后 Pipeline 没动静排查步骤① 检查 .gitlab-ci.yml 是否在项目根目录 ls .gitlab-ci.yml # 必须有 ​ ② 检查文件语法 GitLab CI/CD → Pipelines → CI Lint → 粘贴内容验证 ​ ③ 检查 workflow rules 确认没有 when: never 误拦截 ​ ④ 检查项目 CI/CD 是否开启 Settings → General → Visibility → 确认 CI/CD 未禁用10.2 Job 卡住了Stuck❌ Job 一直显示 This job is stuck原因没有匹配的 Runner。排查# ① Job 的 tags 和 Runner 的 tags 要匹配 job: tags: - docker # → Runner 上也要注册了 docker 标签 ​ # ② 或者 Job 不写 tags允许任何 Runner 执行 job: tags: [] ​ # ③ 或者 Runner 注册时加了 --run-untaggedtrue10.3 Runner 离线❌ Runner 显示灰色离线排查# ① 检查 Runner 进程 sudo gitlab-runner status ​ # ② 查看日志 sudo gitlab-runner run --debug ​ # ③ 检查网络 curl -I https://gitlab.com # 能不能连通 GitLab ​ # ④ 重启 sudo gitlab-runner restart ​ # ⑤ 重新注册 sudo gitlab-runner unregister -t token sudo gitlab-runner register ... # 重新注册10.4 Docker Executor 无法连接 Docker❌ Cannot connect to the Docker daemon# 方案一使用 dind推荐 default: services: - docker:27.4.1-dind variables: DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: /certs ​ # 方案二绑定 Docker Socket不推荐安全性差 # 注册时加--docker-volumes /var/run/docker.sock:/var/run/docker.sock10.5 常用调试技巧# 1. 打印所有环境变量 debug: script: - env | sort ​ # 2. 开启详细日志 debug: variables: CI_DEBUG_TRACE: true script: - echo 查看每行命令的执行日志 ​ # 3. 本地验证语法 # npx gitlab-ci-local --file .gitlab-ci.yml ​ # 4. 暂时允许失败调试阶段 test-experiment: allow_failure: true10.6 错误对照表错误信息原因解决方法This job is stuck无可用 Runner检查 tags 匹配No such file or directory路径不对检查命令执行目录Permission denied权限不够检查 Token、SSH 密钥Cannot connect to the Docker daemonDocker 没起来配置 dind servicesJob exceeded timeout执行超时增大 timeout 值Artifact too large产物太大用 exclude 去掉不必要文件ERROR: Job failed: exit code 1命令执行失败查看具体日志哪行报错10.7 YAML 语法提醒# 缩进必须是空格不能用 Tab # 冒号后面必须有空格 # 数组用 - 开头 ​ # ✅ 正确 job: script: - echo hello ​ # ❌ 错误 job: script: - echo hello # script: 后面必须有子缩进附录官方参考GitLab CI/CD 官方文档.gitlab-ci.yml 完整关键词参考安装 GitLab Runner注册 GitLab RunnerCI/CD 预定义变量Docker ExecutorPipeline 架构基于 GitLab 官方文档编写如有更新请以官网为准。 官方文档https://docs.gitlab.com