拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GitHub/GitLab/Gerrit/GerritHub 选型与落地实践

GitHub/GitLab/Gerrit/GerritHub 选型与落地实践

很多人第一次看到这四个名字,脑子里浮现的都是"不就是放代码的地方吗"。真到了要给团队定平台的时候才发现,GitHub、GitLab、Gerrit、GerritHub 根本不在同一个维度上打架——一个卖的是社交化协作生态,一个卖的是一体化 DevOps 流水线,一个把代码评审当成铁律门禁,最后一个只是个桥接产物。这篇文章不站队,只讲清楚各自的机制、代价和适用边界,然后给你一套能直接照着走的选型逻辑和落地配置。不管你是三个人凑起来的创业小队,还是要在内网搭一套带审计的代码平台,看完应该都能判断自己该上哪一个、怎么搭、坑在哪。

1. 先把定位掰开:四个名字压根不是同类竞品

1.1 GitHub:生态与协作网络的中心节点

GitHub 的核心资产从来不是 Git 仓库本身,而是围绕仓库长出来的那张网。Pull Request 把"提代码"变成了一场公开讨论,Issue 把需求和缺陷收进了同一条时间线,Actions 让自动化直接长在仓库里,再加上 Pages、Codespaces、Packages、Codes Security 这一整套,仓库从存储单元变成了协作单元。

它的强项在"对外":开源项目天然在这里聚集,贡献者不需要被邀请就能 fork、提 PR、参与讨论,维护者的成本很低。企业版也提供了组织、团队、SAML 单点登录、审计日志这些能力,但整体气质还是围绕"人"和"协作"设计的,不是围绕"流程门禁"设计的。

有个细节值得注意:GitHub 的权限粒度是围绕仓库和团队角色来的(Read、Triage、Write、Maintain、Admin),配合 Rulesets 和 Branch Protection 能挡住直接推主干的动作,但它默认假设"分支上的提交是可信的",评审是建议性、可绕过(只要有权限)的,而不是强制的。这个假设决定了它适不适合做严肃的合规门禁。

1.2 GitLab:把整条交付链塞进一个产品

GitLab 的思路是"一个平台解决从计划到部署的全部环节"。它把 Issue、看板、代码托管、Merge Request、CI/CD、制品库、容器镜像仓库、安全扫描、Pages、监控全部做进同一个应用里,数据在同一个 PostgreSQL 里,权限在同一套模型里,页面在同一套导航里。

这个设计带来的最大好处是"少跳转"。你提一个 MR,Pipeline 就在同一个页面跑;Pipeline 里的镜像直接推到内置的 Registry;部署完还能看到环境状态。对于不想把十几个工具拼起来的中小团队,这个吸引力是致命的。

GitLab 有社区版(CE)和企业版(EE),CE 是开源许可,功能覆盖了绝大多数团队的核心诉求:代码托管、MR、CI/CD、Registry、Pages 全都有。EE 主要多了合规、审计、深度安全扫描、多级审批这些面向大企业的能力。自建的话,官方提供 Omnibus 包(deb/rpm)、容器镜像、Helm Chart 三种主流方式,社区版的安装包是公开下载的,离线环境也能提前把包和依赖拉全再装。

更关键的一点:GitLab 的 CI/CD 是内建的,不需要额外装 Jenkins 之类的东西去"配 connection"。它用.gitlab-ci.yml声明流水线,GitLab Runner 执行,支持 shell、docker、kubernetes 多种 executor。这套东西对"我想让推代码自动跑测试然后自动部署"的需求,几乎是开箱即用的。

1.3 Gerrit:把评审做成一道物理门禁

Gerrit 是另一个物种。它不是"代码托管平台 + 评审功能",而是"评审是唯一的入库方式"。在 Gerrit 的世界里,你的本地 commit 不能直接推到分支上,只能推到refs/for/<branch>这个虚拟引用,推上去的每个 commit 会变成一个 Change,必须拿到足够的评审票(比如 Code-Review +2 加 Verified +1)才能被 submit 合入。

这个模型来自 Android 项目当年管理海量外部贡献者的现实需求:几万个贡献者、每天上千个补丁、必须保证每一个进入主干的提交都被至少一个维护者看过、每一次修改都有完整历史。分支-PR 模型在这个规模下会失控,因为分支会爆炸,评审状态会散落。Gerrit 用 Change 这个更细的粒度解决了它。

代价也很明显:学习曲线陡。你得装commit-msghook 才能生成 Change-Id,得学会推refs/for/,得理解refs/changes/下的补丁集编号,得接受"一个 commit 一个 change"的强制拆解。对于习惯git push origin main的人,第一次用大概率会撞一头包。

1.4 GerritHub:不是平台,是桥

GerritHub 经常被误解成"另一个 Git 平台",实际上它是把 GitHub 和 Gerrit 拼起来的托管服务。你用 GitHub 账号登录,把 GitHub 上的仓库导入进来,然后走 Gerrit 的评审流程,评审通过后再同步回 GitHub。它的价值场景很窄但很清晰:代码想公开放在 GitHub 上获取社区曝光,流程上又要 Gerrit 式的严格门禁。

说白了,它是给"既要开源可见性、又要提交纪律"的项目准备的折中方案,不是一个从零开始搭代码平台的选项。个人或小团队自建环境时基本不需要考虑它。

2. 机制差异才是重点:Push 模型与 Change 模型

2.1 GitHub/GitLab 的分支-PR 模型

这两个平台共用一套心智模型:分支是工作单元,Pull Request / Merge Request 是评审载体,合并动作由评审发起者(或被授权者)执行。

具体流程是:从主干切一个 feature 分支,在分支上随便提交多少个 commit 都行,推上去,开 PR,讨论,改,再推,最后点合并按钮。合并策略可选 merge commit、squash、rebase,历史形态由你控制。

这套模型的好处是"低摩擦"。新人第一天就能上手,因为它跟本地 Git 的直觉一致。分支天然隔离了工作,评审是异步的、可回退的、可放弃的。

坏处是"历史容易烂"。如果团队不约定 squash 或 rebase,主干上会堆满"fix typo""再改一下""真的最后一版"这种提交。而且分支的生命周期一旦拉长,合并冲突会变成灾难。解决这个问题的办法通常不是换平台,而是加规则:开启分支保护、要求线性历史、强制 squash 合并、加 CI 门禁。GitHub 用 Branch Protection + Rulesets 做,GitLab 用 Protected Branch + Push Rules + Merge Request Approval 做。

2.2 Gerrit 的 Change 模型与那些绕不开的引用

Gerrit 把工作单元从"分支"降到了"单个 commit"。一个 commit 就是一个 Change,修改意见通过补丁集(Patch Set)迭代。你在本地用git commit --amend改同一个 commit,再推一次,Gerrit 会自动识别成同一个 Change 的新补丁集,评审历史完整保留。

这里的关键机制是 Change-Id。它写在 commit message 的最后一段,格式类似:

修复批量导入时的空指针问题 Change-Id: I3f5a9c2b7e1d4f8a6c0b2e9d7f4a1c3e5b8d0f2a

没有这个尾注,Gerrit 会直接拒绝你的推送,报错长这样:

! [remote rejected] HEAD -> refs/for/master (missing Change-Id in commit message footer)

生成它的方式是装 hook:

# 从 Gerrit 服务器拉取 hook curl -Lo .git/hooks/commit-msg http://gerrit.example.com/tools/hooks/commit-msg chmod +x .git/hooks/commit-msg

装完之后,每次git commit都会自动往 message 尾部追加 Change-Id。这个 hook 必须对每个克隆都装一次,是个典型的"新人必踩"的坑。

推送目标也不是分支名,而是特殊引用:

# 推送到评审队列 git push origin HEAD:refs/for/master # 带 topic 标记,方便批量关联 git push origin HEAD:refs/for/master%topic=bugfix-import # 已经是维护者,想直接入库(需要权限) git push origin HEAD:refs/heads/master

refs/for/master是一个虚拟引用,不真实存在于仓库里。Gerrit 拦截这个推送,把 commit 转成 Change。对应的,每个 Change 的每个补丁集在服务器上真实的引用长这样:refs/changes/34/1234/2,表示 1234 号 Change 的第 2 个补丁集。想拉某个特定补丁集下来看,就 fetch 这个引用。

2.3 提交粒度与历史整洁度的对照

两种模型对"提交质量"的约束强度完全不同。

分支-PR 模型里,评审者看到的是"分支上所有 commit 的集合",只要最终合并后的结果对,中间提交烂一点通常没人管。Gerrit 里,每个 commit 都要单独拿票,评审者必须逐个看,所以它天然逼着开发者把提交拆干净、写清楚。这也是为什么很多用惯了 Gerrit 的人回不去 PR 模型,以及为什么很多从 PR 模型迁到 Gerrit 的团队前期怨声载道——修改习惯要重建。

下面这张表把核心机制差异摆在一起:

维度GitHubGitLabGerrit
工作单元分支 + PR分支 + MR单个 commit(Change)
推送目标refs/heads/*refs/heads/*refs/for/*
评审强制力可配置,有权限可绕过可配置,审批规则可强制天然强制,不拿票进不去
改稿方式追加 commit 或 force push追加 commit 或 force push--amend生成新补丁集
需要额外 hook否否是(commit-msg)
线性历史保证靠分支保护规则靠分支保护规则天然线性(Fast Forward Only)

3. 能力矩阵:权限、CI、存储、生态逐项对比

3.1 权限模型与分支保护的实际差异

GitHub 的权限体系分两层:组织层用团队(Team)聚合人,仓库层给角色。角色从 Read 到 Admin 五档,配合 Rulesets 可以做到"某些路径只能特定团队改"(用 CODEOWNERS)。审计日志在企业版里才完整。

GitLab 的权限模型更细,项目角色有五档(Guest、Reporter、Developer、Maintainer、Owner),群组可以继承,Protected Branch 能精确到"谁可以 merge、谁可以 push、谁都不能 push"。EE 还支持多级审批规则,比如"必须产品 + 安全各批一次才能合"。对流程要求严的组织,这层能力比 GitHub 更顺手。

Gerrit 的权限是围绕"引用"配置的。你可以给refs/heads/*、refs/for/*、refs/changes/*分别配不同权限,甚至用 Prolog 写规则做动态判断(比如"只有改动了某个目录的文件才需要安全组投票")。它还有 label 机制,你可以自定义Code-Review、Verified、CI-Build之外的任意标签,比如Security-Review,并规定每个标签需要几张票、谁能投负票。这个粒度是另外两个平台做不到的。

3.2 CI/CD:内建、外挂与门禁的三种打法

GitLab 的 CI 是亲儿子。.gitlab-ci.yml放在仓库根目录,Runner 一注册,推送即触发。一个典型的多阶段流水线是这样的:

stages: - test - build - deploy variables: IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA unit_test: stage: test image: python:3.11-slim script: - pip install -r requirements.txt - pytest --junitxml=report.xml artifacts: reports: junit: report.xml paths: - report.xml expire_in: 7 days build_image: stage: build image: docker:24 services: - docker:24-dind script: - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY - docker build -t $IMAGE_TAG . - docker push $IMAGE_TAG only: - main deploy_prod: stage: deploy image: bitnami/kubectl:1.29 script: - mkdir -p ~/.kube - cp "$KUBE_CONFIG" ~/.kube/config - kubectl set image deployment/app app=$IMAGE_TAG -n prod environment: name: prod when: manual only: - main

几个容易踩的点:$KUBE_CONFIG这类敏感变量要在项目设置里配成 File 类型,不然换行会丢;dind服务需要 Runner 开启 privileged;only: main现在官方推荐改成rules:写法,但only依然能用。生产部署那个 job 设成when: manual是个好习惯,避免误触发。

GitHub 这边用 Actions。它的优势是 Marketplace 上现成的 action 多到离谱,几乎任何需求都能找到别人写好的步骤。劣势是 workflow 文件写起来啰嗦,复杂流水线容易变成 YAML 迷宫,而且私有仓库的分钟数是计费的。

Gerrit 本身不带 CI,它靠的是"外部 CI 往 label 上投票"。典型的做法是 Jenkins 监听 Gerrit 的 stream events,收到patchset-created事件后拉取对应补丁集跑构建,跑完通过 SSH 命令给Verified标签投 +1 或 -1:

# Jenkins 侧给补丁集投票 ssh -p 29418 jenkins@gerrit.example.com \ gerrit review --verified +1 --message "'构建通过'" \ 1234,2

注意那个1234,2是 Change 号和补丁集号。这个设计的好处是 CI 和评审彻底解耦,换 CI 系统不影响 Gerrit;坏处是链路长,出问题要两头查。

3.3 检索、存储与周边生态

GitHub 的代码搜索和跨仓库检索能力是三个里最强的,公开代码的检索价值几乎是不可替代的。GitLab 有全局搜索和高级搜索(EE 更强),自建环境的 Elasticsearch 集成需要额外配。Gerrit 的搜索是围绕 Change 设计的,你想按 "owner:self status:open label:Code-Review+2" 这种条件筛 change 很方便,但做全文代码搜索不是它的强项。

存储层面,GitHub 是托管服务,你不需要关心;GitLab 自建要关注磁盘(仓库 + 制品 + 镜像 + 日志一起涨,很容易把盘吃满);Gerrit 自建主要是仓库 + 索引,压力小很多,但它对 NoteDb 的备份要求更严,配置不当恢复会很痛苦。

周边生态一句话概括:GitHub 最广,GitLab 最全,Gerrit 最窄但最深。选之前先想清楚自己是需要"什么都能接上",还是需要"某一件事做到极致"。

4. 选型决策:按规模和流程成熟度对号入座

4.1 什么情况下闭着眼睛选 GitHub

三种情况基本可以不用犹豫。第一种,项目要开源、要吸引外部贡献者。生态效应摆在那,贡献者不会为了给你提个 PR 专门去注册别的平台。第二种,团队小、流程轻,主要诉求是"代码有地方放、能评审、能跑个自动测试"。Actions 加 Branch Protection 就够了,自己维护服务器的时间省下来干正事。第三种,需要重度依赖第三方集成,比如各种 SaaS 的部署钩子、项目管理工具、安全扫描服务,它们对 GitHub 的支持通常最全。

要接受的前提是:托管意味着你的代码在别人的机器上,合规要求高的场景要提前评估;另外免费额度、私有仓库限制、Actions 分钟数这些账要算清楚,团队规模上去之后成本不是小数。

4.2 什么时候必须自建 GitLab

出现下面任意一条,自建 GitLab 就该进入候选:代码不能出内网、有明确的数据驻留要求、需要 GitLab CI 那种"提交即流水线"的一体化体验、希望把制品库和容器镜像仓库跟代码放一起管、团队人不多但想要一套完整的 DevOps 工具链而不是拼装一堆。

自建 GitLab 最需要注意的是资源和运维成本。官方推荐的最低配置在实际使用中基本不够用,真正跑起来给 20 人团队用,4 核 8G 只是起点,磁盘要按"仓库大小 × 3 到 5 倍"预留,因为制品、镜像、备份、日志都会占地方。备份策略必须提前设计,不能等出事再想。

4.3 什么时候该上 Gerrit

判断标准很单一:你的项目是否到了"每一次进入主干的改动都必须被人工审视、且不能有任何绕过路径"的程度。典型信号包括:参与贡献的人成百上千且大部分是外部人员、代码质量事故的代价极高(比如涉及底层系统或硬件固件)、审计要求每个改动都要留下完整评审记录、主干必须保持严格线性且随时可发布。

如果只是"我们想规范一点",别上 Gerrit。它的学习成本和流程刚性对小团队是净负担。如果确实需要,那它带来的历史整洁度和评审可追溯性是 PR 模型很难比的。

4.4 一张决策表,直接对号

你的处境首选理由
开源项目,要外部贡献GitHub生态和曝光不可替代
3-15 人小队,快速迭代GitLab CE 自建 或 GitHub一体化程度高,运维成本可接受
代码不能出内网GitLab CE 自建 或 Gerrit完全可控
需要严格审计与强制门禁Gerrit评审即门禁,无绕过路径
大厂底层项目,外部贡献多Gerrit 主 + GitHub 镜像展示门禁与曝光兼顾
想要 CI/CD 开箱即用GitLab.gitlab-ci.yml内建支持
已有 Jenkins,只想换代码平台Gerrit 或 GitLab两者都能跟 Jenkins 对接
就想要个免费私有仓库GitLab CE 自建无分钟数限制

5. 落地实操:从零搭起的关键步骤与配置

5.1 GitLab 社区版容器化部署的实操要点

用容器方式搭 GitLab CE 是最省事的路径,但有几个参数不写对,后面会一直出问题。

docker run -d \ --name gitlab \ --hostname gitlab.example.com \ --restart always \ -p 8443:443 -p 8081:80 -p 2222:22 \ -v /srv/gitlab/config:/etc/gitlab \ -v /srv/gitlab/logs:/var/log/gitlab \ -v /srv/gitlab/data:/var/opt/gitlab \ -e GITLAB_OMNIBUS_CONFIG="external_url 'https://gitlab.example.com'; gitlab_rails['gitlab_shell_ssh_port']=2222; nginx['listen_port']=80; letsencrypt['enable']=false" \ gitlab/gitlab-ce:latest

拆开说每个点为什么这么写。端口映射把宿主机的 8081 对到容器 80,是为了避开宿主机上可能已经在跑的 80 端口服务,这个冲突非常常见。SSH 映射到 2222 是因为宿主机 22 通常被系统 sshd 占了,如果 SSH 端口映射了但gitlab_shell_ssh_port没同步改成 2222,克隆时给出的地址会指向 22,直接连不上。external_url必须跟实际访问地址完全一致,否则页面里的链接、clone 地址、webhook 回调全是错的。

三个数据卷一个都不能省。/etc/gitlab装配置和密钥,/var/log/gitlab是日志,/var/opt/gitlab是仓库数据。备份的时候,gitlab-backup create只备数据部分,配置文件不在里面,恢复时必须保证配置文件跟备份是同一版本的状态,否则会失败。这是我见过最容易翻车的地方。

启动后等初始化完成(大概 2 到 5 分钟,取决于机器),拿初始密码:

docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

登录后第一件事改密码,第二件事去"设置 - 通用"里把外部访问的域名和 SSH 端口确认一遍。

5.2 SSH 密钥与代码拉取的完整链路

不管用哪个平台,SSH 密钥配不对,后面所有操作都难受。生成一对:

ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519_gitlab

用 ed25519 而不是 RSA 是因为它更短更快,现在的平台都支持。多平台共存时,靠~/.ssh/config分流:

Host gitlab.example.com HostName gitlab.example.com Port 2222 User git IdentityFile ~/.ssh/id_ed25519_gitlab IdentitiesOnly yes Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github IdentitiesOnly yes

IdentitiesOnly yes这行很关键,不加的话 SSH 会把所有密钥挨个试一遍,某些服务器在试错次数超限后直接断开,表现为"密钥明明加了但认证失败"。

验证连通性:

ssh -T git@gitlab.example.com # 返回 Welcome to GitLab, @username! 就是通了

拉取代码时有个常见困惑:页面给的 clone 地址如果带着机器 ID 或者一长串随机域名,说明external_url没配好。正确做法是在gitlab.rb里把external_url改成你的真实域名,然后执行:

docker exec -it gitlab gitlab-ctl reconfigure

改external_url之后必须 reconfigure,光改文件不生效。这个动作会重新生成 nginx 配置和一堆内部链接,耗时几分钟是正常的。

5.3 Gerrit 的初始化与评审流程打通

Gerrit 初始化比 GitLab 轻,但配置项更绕。装完之后第一步是建第一个管理员账号,然后创建项目:

ssh -p 29418 admin@gerrit.example.com gerrit create-project \ --parent All-Projects \ --owner Administrators \ --description "核心服务" \ core-service

接着给项目配权限。最需要改的是refs/heads/*的 Submit 权限,默认只有管理员能直接推,正常流程应该只允许通过refs/for/*提交然后由 label 门禁放行。同时把强制 Change-Id 的选项打开,这样没装 hook 的提交会在推送时就被拒绝,错误暴露得早,比在评审阶段才发现强。

如果项目里已经有代码,把现有仓库导入:

# 从现有仓库克隆裸版本 git clone --bare https://github.com/example/legacy.git legacy.git cd legacy.git # 推到 Gerrit git push ssh://admin@gerrit.example.com:29418/core-service refs/heads/*:refs/heads/*

这里要提一句:Gerrit 默认策略下直接推refs/heads/*需要对应权限,批量导入时用管理员账号操作,导入完成后再收紧权限。导入完别忘了在项目配置里设置 Submit Type,想要严格线性历史就选 Fast Forward Only。

Jenkins 侧的对接,核心是两件事:装 Gerrit Trigger 插件并配置 Gerrit 服务器连接和事件监听;在构建后步骤里用gerrit review命令投票。这里有个坑,投票用的 SSH 密钥和 Jenkins 用的密钥不是一回事,需要单独在 Gerrit 上给 jenkins 账号配 SSH 公钥,并且这个账号要有Label Verified的投票权限,否则构建跑成功但投不进去票,Change 永远卡在那里。

5.4 迁移与共存:别想一次全搬完

现实中很少是"从零选一个",更多是"已经在用 A,想加 B"或者"想从 A 换到 B"。我的建议是共存期一定要留。

从 GitHub 迁到自建 GitLab,代码本身好迁,git clone --mirror再push --mirror就完了。难迁的是 Issue、PR 讨论、Wiki。GitLab 官方提供了导入工具,但评论和状态映射经常不完整。实际做法是:代码先迁,旧仓库设为只读并保留一段时间做查询,新工作全部在新平台开,不要试图把历史对话搬过来。

从 GitHub 迁到 Gerrit 更要注意:Gerrit 不接受一个分支上有一堆杂乱提交,导入后如果主干历史不线性,设置 Fast Forward Only 会导致后续提交推不上去。这种情况下要么接受 Merge If Necessary,要么重新整理历史——后者对活跃项目基本不现实。

镜像保留的问题,如果内网 GitLab 是主力,同时想在 GitHub 上展示代码,可以用推送镜像的方式做单向同步。要注意方向必须是单向,双向同步的冲突解决成本很高,容易把两边都搞乱。

6. 常见坑与排查清单

6.1 认证与权限类问题速查

这类问题占了实际求助的一大半,基本都是配置问题。

现象真实原因处理方式
SSH 提示 Permission denied公钥没加、密钥选错、config 缺 IdentitiesOnly用ssh -vT看用了哪把密钥
能 clone 不能 push角色是 Reporter 或分支受保护在项目成员页确认角色
页面 clone 地址是机器 IDexternal_url没配或没 reconfigure改配置后执行 reconfigure
登录报 token 或版本错误API token 过期或客户端版本与服务器不匹配重新生成 token,检查客户端版本
Gerrit 提示 not permitted: create没有目标分支的推送权限用refs/for/而非refs/heads/
切换账号后仍用旧身份凭据管理器缓存了旧凭证清掉系统凭据里的对应条目

关于 GitLab 账号,自建实例默认关闭注册,需要管理员在后台创建用户或者临时开放注册再关闭。很多人第一次自建完发现"注册不了",就是没意识到这一点。

6.2 提交被拒的典型报错与原因

Gerrit 的报错信息其实很直白,看懂关键词就好办。

# 缺 Change-Id ! [remote rejected] HEAD -> refs/for/master (missing Change-Id in commit message footer) # 解决:安装 commit-msg hook 后 git commit --amend 重新生成 # 没有新改动 ! [remote rejected] HEAD -> refs/for/master (no new changes) # 原因:你推的 commit 内容跟已有补丁集完全一样,把改动 amend 进同一个 commit 再推 # 直接推主干被拒 ! [remote rejected] HEAD -> refs/heads/master (prohibited by Gerrit) # 原因:没有直接推送权限,改推 refs/for/master

no new changes这个报错特别常见于"改了代码但忘了git add",或者"改了文件但内容跟上一版一样"。推到评审队列之前先git status看一眼,能省不少来回。

GitLab 那边常见的推送失败是"分支受保护"和"pre-receive hook 拒绝"。前者去项目设置里看 Protected Branches 列表,确认你的角色在 Allowed to push 里;后者通常是 Push Rules 配了"提交信息必须符合正则"之类的规则,去设置里核对规则内容。

6.3 CI 不触发与流水线卡住的排查

GitLab 的流水线卡在 pending 状态,九成是 Runner 的问题。检查顺序是:Runner 是否在线(设置 - CI/CD - Runner 里看绿点)、Runner 的 tag 跟 job 的 tag 是否匹配、Runner 是否设置了"只运行带 tag 的 job"而你的 job 没打 tag。这三种情况在页面上都不报错,只显示等待,特别容易让人怀疑人生。

# 在 Runner 机器上查看注册状态和并发配置 gitlab-runner list gitlab-runner verify

另一个高频问题是容器化构建里用 docker 命令失败,报Cannot connect to the Docker daemon。原因是 Runner 用了 docker executor 但没挂载宿主机的 docker socket,或者用了 dind 服务但没开 privileged。两种方案选一个:挂 socket 简单但隔离性差;dind 隔离好但配置多几行。另外DOCKER_HOST环境变量没设,也会导致构建脚本里的 docker 命令找不到 daemon。

Gerrit 这边 CI 不通,先看 Jenkins 的 Gerrit Trigger 有没有收到事件。没收到就去 Gerrit 侧检查 stream events 的连接和监听配置;收到但没触发构建,看项目名和分支的匹配规则。这类问题的排查最好打开 Jenkins 的系统日志实时看,比翻历史记录快得多。

6.4 备份、恢复与版本升级的注意事项

GitLab 的备份有个硬约束:备份文件必须和恢复时的 GitLab 版本一致(至少是相同主版本)。跨版本恢复基本会失败,正确姿势是先恢复到同版本,再逐步升级。

# 备份(在容器内执行) docker exec -t gitlab gitlab-backup create # 备份文件默认在 /var/opt/gitlab/backups 下 # 恢复前先把配置文件也备份一份 docker exec -t gitlab tar -czf /tmp/etc-backup.tar.gz /etc/gitlab

恢复的时候,/etc/gitlab/gitlab-secrets.json必须跟备份时一致,否则所有加密数据都读不出来,包括 CI 变量、Registry 凭据。这个文件丢了,数据基本等于废了。所以完整的备份方案是"数据备份 + 配置文件 + secrets 文件"三件套,缺一不可。

磁盘空间也要盯。GitLab 跑久了,/var/log/gitlab能吃掉几十个 G,Registry 和制品库也会无限膨胀。建议配一个定期清理策略:日志用 logrotate,制品设过期时间,备份文件保留最近若干份,镜像定期清理未打 tag 的层。

Gerrit 的备份相对简单,主要是仓库目录加etc目录。新版用 NoteDb,元数据都在 Git 仓库里,备份仓库目录基本就够。但升级前一定要读对应版本的 release notes,因为索引格式变化时升级后需要重建索引,这个步骤漏掉会导致搜索功能异常。

最后分享一个我踩过的坑:自建平台上做测试环境时,很多人习惯直接复制生产配置,结果两个实例用了相同的 secrets 文件,后来做单点登录和跨实例跳转的时候出现诡异的身份串号问题。自建服务在复制配置时,一定要重新生成密钥和 secrets,或者明确知道哪些能复用、哪些必须区分。这个教训不便宜。

返回列表