ARTICLE DETAIL

资讯详情

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

Gerrit 集成 Gitweb 实战:配置、部署与避坑指南

Gerrit 集成 Gitweb 实战:配置、部署与避坑指南 1. 为什么要在 Gerrit 里挂上 Gitweb很多团队把 Gerrit 当作代码评审的唯一入口日常提交、打分、合并都在 Gerrit 的 Web 界面里完成。但用久了会发现一个尴尬的地方Gerrit 自带的文件浏览和 diff 视图适合看某一次改动却不适合随便翻翻仓库。比如你想快速看一眼某个文件的历史版本、想按目录树浏览整个仓库、想直接下载某个历史提交的 tar 包Gerrit 原生界面做起来都别扭。Gitweb 正好补上这块。它是 Git 官方自带的一个轻量级 Web 前端用 Perl 写的 CGI 脚本能提供仓库列表、目录树浏览、文件 blame、commit 历史、snapshot 下载这些功能。把它和 Gerrit 集成之后你在 Gerrit 的每个项目页面里会多出一个 gitweb 链接点进去就能直接跳到对应仓库的 Gitweb 视图浏览体验一下子顺了很多。这篇内容面向的是已经跑起来 Gerrit、想再补一层仓库浏览能力的运维或开发同学。我会从 Gitweb 的定位讲起把gitweb.cgi的部署、gerrit.config里gitweb段的配置、反向代理的衔接、以及实际踩过的坑完整走一遍。关键词里出现的gerrit.config、gitweb.url、gitweb.cgi都会落到具体配置项上不是泛泛而谈。先说清楚一个前提Gerrit 集成 Gitweb 有两种典型形态。一种是 Gitweb 和 Gerrit 跑在同一台机器、同一个 Web 服务下通过 CGI 直接调用另一种是 Gitweb 独立部署在别的地址Gerrit 只负责生成跳转链接。两种形态的配置项不一样后面会分开讲。选哪种取决于你的部署规模和网络拓扑不是越简单越好也不是越分离越好。2. Gitweb 与 Gerrit 的职责边界先理清楚2.1 Gitweb 到底提供什么不提供什么Gitweb 本质是一个只读的 Git 仓库浏览器。它读取的是仓库的.git目录通过 CGI 动态生成 HTML 页面。它能做的事情包括列出所有仓库、展示某个仓库的摘要信息最近提交、分支、标签、描述、按目录树浏览文件、查看单个文件的 blame、查看 commit 和 commitdiff、生成 snapshottar.gz / zip下载。它不能做的事情也很明确不能提交代码、不能做权限评审、不能管理用户。所以它和 Gerrit 是互补关系不是替代关系。Gerrit 管改动的流转Gitweb 管仓库的浏览。理解这一点后面配置时就不会纠结为什么 Gitweb 里改不了代码这种问题。还有一个容易被忽略的点Gitweb 的权限模型非常弱。它基本依赖 Web 服务器层面的访问控制比如 HTTP Basic Auth、IP 限制本身没有细粒度的仓库级权限。这意味着如果你把 Gitweb 暴露在公网又不加任何限制等于把整个仓库的代码公开了。这一点在集成时必须想清楚后面第 5 节会专门讲访问控制。2.2 Gerrit 侧需要 Gitweb 补哪些能力从实际使用场景倒推Gerrit 用户最常需要 Gitweb 补的能力有三个。第一是目录树浏览。Gerrit 的项目页面只列出分支和最近的改动你想看仓库里现在有哪些目录、某个目录下有哪些文件Gerrit 原生界面给不了直观的树形视图。Gitweb 的tree视图正好解决这个。第二是历史版本的文件内容。评审时经常需要对比这个文件三个月前长什么样Gerrit 的 diff 是围绕 change 的翻历史版本很费劲。Gitweb 可以直接定位到某个 commit 下的某个文件。第三是snapshot 下载。有些场景需要把某个 tag 或某个 commit 的完整代码打包下载Gerrit 不提供这个功能Gitweb 的 snapshot 链接一点就行。这三块能力决定了 Gitweb 集成的价值。如果你的团队根本不需要这些那集成 Gitweb 就是多余的别为了看起来完整而增加维护面。2.3 两种集成形态的取舍前面提到的两种形态这里展开说下取舍逻辑。同机 CGI 形态Gitweb 的gitweb.cgi和 Gerrit 跑在同一台机器通常由同一个 Web 服务器Apache 或 Nginx fcgiwrap托管。Gerrit 配置里gitweb.cgi指向本地脚本路径gitweb.url指向 Gitweb 的访问地址。优点是部署紧凑、延迟低、不用跨机网络缺点是 Web 服务器配置耦合Gitweb 出问题可能影响 Gerrit 的 Web 服务。独立部署形态Gitweb 单独跑在一台机器或一个独立容器里Gerrit 只配置gitweb.url指向它的地址。优点是解耦、可以独立扩缩容、权限控制更灵活缺点是多了一个网络跳转、需要保证两边仓库路径一致或可访问。我的经验是小团队十几个人、单机 Gerrit用同机 CGI 就够了配置量小中大型团队或者 Gerrit 已经容器化的倾向独立部署因为容器里塞 Perl CGI 环境比较别扭。下面两节分别讲这两种形态的落地。3. 同机 CGI 形态gitweb.cgi 的部署与 gerrit.config 对接3.1 安装 Gitweb 与依赖大多数 Linux 发行版把 Gitweb 拆成了独立包。Debian/Ubuntu 上是gitwebRHEL/CentOS 上是gitweb在 EPEL 里。装完之后gitweb.cgi通常在/usr/share/gitweb/gitweb.cgi配套的静态资源在/usr/share/gitweb/static/。# Debian / Ubuntu apt-get install -y gitweb # RHEL / CentOS需要 EPEL yum install -y gitweb装完先确认脚本存在、Perl 依赖齐全ls -l /usr/share/gitweb/gitweb.cgi perl -c /usr/share/gitweb/gitweb.cgiperl -c是语法检查输出 syntax OK 才算依赖没缺。如果报Cant locate CGI.pm说明 Perl 的 CGI 模块没装补上libcgi-pm-perlDebian或perl-CGIRHEL。提示新版 Perl 已经把 CGI.pm 从核心模块里移除了很多系统装完 gitweb 后perl -c会直接报错。这一步千万别跳过否则后面 Web 服务器调 CGI 时只会给你一个 500排查起来很费时间。3.2 配置 gitweb 的仓库根目录Gitweb 需要知道去哪里找仓库。它的配置文件通常是/etc/gitweb.conf。核心配置项是$projectroot指向 Gerrit 的 Git 仓库根目录。Gerrit 的仓库默认放在$GERRIT_SITE/git/下每个项目一个目录比如$GERRIT_SITE/git/All-Projects.git、$GERRIT_SITE/git/my-project.git。所以# /etc/gitweb.conf our $projectroot /home/gerrit/site/git; our $projects_list /home/gerrit/site/git;这里有个关键细节Gerrit 的仓库目录名带.git后缀而 Gitweb 默认展示时会去掉后缀。如果你希望 Gitweb 里显示的项目名和 Gerrit 里一致保持默认即可如果发现名字对不上检查$projectroot是否指到了正确的层级。另外$projects_list如果指向一个目录Gitweb 会扫描该目录下的所有仓库如果指向一个文本文件则按文件里列出的仓库展示。指向目录更省事但仓库多了之后每次请求都要扫目录性能会下降。仓库数量超过几百个时建议改成生成静态列表文件。3.3 gerrit.config 里 gitweb 段的写法Gerrit 的配置文件是$GERRIT_SITE/etc/gerrit.config。集成 Gitweb 的核心就是加一个[gitweb]段[gitweb] type gitweb cgi /usr/share/gitweb/gitweb.cgi url http://gerrit.example.com/gitweb/逐项解释type固定写gitweb告诉 Gerrit 用 Gitweb 的 URL 拼接规则。Gerrit 还支持cgit、custom等类型这里用gitweb。cgigitweb.cgi的本地路径。Gerrit 用它来判断 Gitweb 是否可用有些版本会做存在性检查。urlGitweb 的访问基地址。Gerrit 会在这个地址后面拼上?p项目名.git之类的参数生成跳转链接。改完gerrit.config需要重启 Gerrit 才生效$GERRIT_SITE/bin/gerrit.sh restart重启后进入任意项目的页面应该能看到一个 gitweb 的链接。点进去如果 404 或者 500问题多半出在 Web 服务器没配好 CGI而不是 Gerrit 配置。3.4 Web 服务器侧把 CGI 挂起来Gerrit 自己不带 CGI 执行能力gitweb.cgi得靠外部 Web 服务器跑。常见做法是用 Apache 或 Nginx fcgiwrap。Apache 方案ScriptAlias /gitweb/ /usr/share/gitweb/ Directory /usr/share/gitweb/ AllowOverride None Options ExecCGI FollowSymLinks AddHandler cgi-script .cgi Require all granted /DirectoryApache 原生支持 CGI配置最直接。ScriptAlias把/gitweb/映射到脚本目录AddHandler让.cgi被当作 CGI 执行。Nginx 方案Nginx 不直接跑 CGI需要fcgiwrap做桥接。location /gitweb/ { root /usr/share/gitweb; index gitweb.cgi; include fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/share/gitweb/gitweb.cgi; fastcgi_param GITWEB_CONFIG /etc/gitweb.conf; fastcgi_pass unix:/var/run/fcgiwrap.socket; }fcgiwrap需要单独启动通常用 systemd 管理。GITWEB_CONFIG这个参数很关键不显式传的话 Gitweb 可能读不到你的/etc/gitweb.conf导致仓库列表为空。注意Nginx 方案里fastcgi_param必须包含SCRIPT_FILENAME和GITWEB_CONFIG少一个都会出问题。我见过有人只配了fastcgi_pass结果 Gitweb 页面能打开但仓库列表是空的排查半天才发现是配置文件路径没传进去。3.5 验证链路是否打通配置完之后按这个顺序验证能快速定位问题出在哪一环命令行直接跑 CGIGITWEB_CONFIG/etc/gitweb.conf perl /usr/share/gitweb/gitweb.cgi看是否输出 HTML。这一步验证 Perl 环境和配置。通过 Web 服务器访问http://gerrit.example.com/gitweb/看是否出仓库列表。这一步验证 Web 服务器 CGI 配置。从 Gerrit 项目页面点 gitweb 链接看跳转是否正确。这一步验证gerrit.config的url拼接。哪一步断了就查哪一步别一上来就怀疑 Gerrit。实际排查中问题八成出在第 2 步的 Web 服务器配置上。4. 独立部署形态gitweb.url 指向外部服务的配置要点4.1 独立部署时的 gerrit.config 差异独立部署时Gitweb 跑在别的地址Gerrit 不需要cgi这一项只需要url[gitweb] type gitweb url http://gitweb.internal.example.com/Gerrit 生成链接时会拼成http://gitweb.internal.example.com/?pmy-project.git;asummary这种形式。注意url结尾的斜杠有没有斜杠会影响拼接结果。带斜杠时拼出来是.../?p...不带斜杠可能拼成...?p...虽然多数情况下都能用但为了规范建议带上。独立部署最大的坑是仓库路径一致性。Gitweb 要能读到 Gerrit 的仓库要么两边共享同一个存储NFS、共享卷要么 Gitweb 所在机器上有一份同步的仓库副本。如果 Gitweb 读的是另一份副本那副本的更新延迟会导致Gerrit 里刚合并的代码Gitweb 里看不到。4.2 共享存储与副本同步的取舍共享存储方案Gerrit 和 Gitweb 挂同一个 NFS 或共享卷Gitweb 的$projectroot直接指向共享路径。优点是实时一致缺点是 NFS 的性能和稳定性会同时影响两边而且 Gitweb 的目录扫描在 NFS 上可能更慢。副本同步方案用git clone --mirror或git fetch定期同步。优点是解耦缺点是有一致性延迟而且同步脚本本身要维护。仓库多的时候全量同步的开销不小。我的建议是如果 Gitweb 只是内部浏览用对实时性要求不高副本同步 定时任务比如每 5 分钟一次完全够用如果团队对看到的就是最新的有强需求那就上共享存储但要接受 NFS 带来的运维复杂度。4.3 跨机访问时的 URL 与权限衔接独立部署时Gitweb 的访问地址往往是内网地址而 Gerrit 可能对公网开放。这时候要小心Gerrit 页面上的 gitweb 链接如果指向内网地址公网用户点了会打不开。解决办法有两种一是给 Gitweb 也配一个公网可达的地址但要加访问控制二是接受gitweb 链接只对内网用户有效这个现实在文档里说明。权限衔接方面独立部署的 Gitweb 通常用 Web 服务器层面的认证。如果 Gerrit 用的是 LDAP 或 OAuthGitweb 这边很难复用同一套认证往往退化成 HTTP Basic Auth 或者干脆内网免认证。这是独立部署形态绕不开的妥协集成前要和团队对齐预期。5. 访问控制别让 Gitweb 变成代码泄露的口子5.1 Gitweb 权限模型的先天不足前面提过Gitweb 本身没有仓库级权限。它读的是文件系统只要 Web 服务器进程有读权限它就能展示。这意味着 Gerrit 里那些只有特定组能看的仓库在 Gitweb 里可能对所有人可见。这是集成 Gitweb 时最大的安全风险没有之一。我见过真实案例某团队 Gerrit 权限配得很细某个核心仓库只对三个人开放结果 Gitweb 一挂整个仓库的代码对所有能访问 Gitweb 的人可见等于权限体系形同虚设。5.2 用 Web 服务器层做访问限制既然 Gitweb 自己管不了权限就得在 Web 服务器层补。常见手段IP 白名单只允许公司内网网段访问 Gitweb。HTTP Basic Auth加一层账号密码至少挡住匿名访问。反向代理统一认证如果公司有统一的 SSO 网关把 Gitweb 挂在网关后面。Apache 的 IP 限制示例Directory /usr/share/gitweb/ Require ip 10.0.0.0/8 192.168.0.0/16 /DirectoryNginx 的 Basic Auth 示例location /gitweb/ { auth_basic Restricted; auth_basic_user_file /etc/nginx/gitweb.htpasswd; # ... 其余 fastcgi 配置 }5.3 敏感仓库的隔离思路如果团队里有少数几个高度敏感的仓库最稳妥的做法是不让它们出现在 Gitweb 里。Gitweb 的$projects_list如果指向一个显式的列表文件就可以只列出允许展示的仓库敏感仓库不写进去即可。# /etc/gitweb.conf our $projects_list /etc/gitweb/projects.list;projects.list里每行一个仓库路径。这样即使 Web 服务器能读到敏感仓库的文件Gitweb 也不会展示它。这是最小暴露面的思路比事后加权限更可靠。提示用$projects_list指向文件后新增仓库需要手动或脚本更新这个列表。可以写个定时任务从 Gerrit 的仓库目录生成列表同时排除敏感仓库。这样既自动化又可控。6. 实测中反复出现的几个坑6.1 仓库列表为空或项目名对不上最常见的问题是 Gitweb 页面能打开但仓库列表是空的。原因通常有三个$projectroot路径写错、Web 服务器进程没有该目录的读权限、GITWEB_CONFIG没传导致读的是默认配置。排查顺序先确认$projectroot路径下确实有.git目录再确认 Web 服务器运行用户Apache 是www-data或apacheNginx 是nginx对该目录有读和执行权限最后确认配置文件被正确加载。项目名对不上多半是因为 Gerrit 仓库目录带.git后缀而 Gitweb 展示时做了处理。如果发现 Gerrit 里叫my-projectGitweb 里显示成别的检查$projectroot的层级和$strict_export等配置。6.2 中文文件名或提交信息乱码Gitweb 默认的字符集处理有时候对中文不友好页面上的中文文件名或提交信息会显示成乱码。解决办法是在/etc/gitweb.conf里显式设置字符集our $site_name My Gitweb; our diff_opts (-M); $ENV{GITWEB_CONFIG} /etc/gitweb.conf;更关键的是确保 Gitweb 输出的 HTML 声明了 UTF-8。如果乱码依旧检查 Git 仓库里提交信息的编码以及 Web 服务器是否在响应头里覆盖了字符集。这个坑不致命但很烦尤其是团队里有中文提交习惯的时候。6.3 snapshot 下载失败或文件名异常snapshot 功能依赖 Gitweb 调用git archive。如果下载时报错先确认git命令在 Web 服务器进程的 PATH 里。CGI 环境下的 PATH 往往和登录 shell 不一样这是经典坑。另一个常见问题是 snapshot 的文件名。Gitweb 生成的 tar.gz 文件名默认包含项目名和 commit 短哈希如果项目名里有特殊字符文件名可能异常。可以在gitweb.conf里调整snapshot_opts相关配置或者接受默认行为。6.4 Gerrit 升级后 gitweb 链接失效Gerrit 升级有时会调整gerrit.config的解析逻辑或者改变 URL 拼接规则。升级后如果发现 gitweb 链接 404先检查gerrit.config的[gitweb]段是否还在、url是否被重置。升级前备份配置文件是个好习惯能省掉很多重新排查的时间。7. 几个提升体验的进阶配置7.1 自定义 Gitweb 外观与站点名Gitweb 的默认界面比较朴素可以通过gitweb.conf和 CSS 做轻度定制。站点名用$site_name设置页面顶部的标题就变了。CSS 可以覆盖/usr/share/gitweb/static/gitweb.css改配色和字体。这些改动不影响功能但能让内部工具看起来更像自己家的。7.2 让 Gitweb 显示仓库描述Gitweb 的仓库摘要页可以显示一段描述文字来源是仓库.git/description文件。Gerrit 创建仓库时这个文件通常是默认内容。可以写个脚本从 Gerrit 的项目配置里同步描述过去这样 Gitweb 里每个仓库都有可读的说明浏览时体验好很多。7.3 和 Gerrit 的 change 链接联动Gitweb 展示 commit 时如果 commit message 里包含 Gerrit 的 Change-Id理论上可以做成跳转到 Gerrit 对应 change 的链接。这需要改 Gitweb 的模板或加 JavaScript属于锦上添花。如果团队评审流程重度依赖 Gerrit这个联动能省不少从 commit 找 change的时间。8. 我在这套集成里踩出来的经验说几个文档里不会写、但实际会遇到的点。第一先决定要不要集成再决定怎么集成。Gitweb 不是 Gerrit 的必需品它的价值集中在浏览和下载两个场景。如果团队日常只用 Gerrit 做评审、代码浏览靠本地 IDE那集成 Gitweb 的收益很低反而多了一个要维护的服务和一条潜在的安全口子。我见过为了功能完整而集成、结果半年没人点的案例。第二权限问题要在集成前就想清楚而不是出事后再补。Gitweb 的权限短板是结构性的不是配置能完全弥补的。最稳的做法是默认不暴露敏感仓库 Web 层加访问限制双保险。等代码泄露了再回头加限制代价就大了。第三同机 CGI 形态的维护成本被低估了。Perl CGI 在现代运维体系里算是老技术容器化、自动化部署时经常遇到依赖缺失、PATH 不一致的问题。如果团队已经在往容器化走独立部署 Gitweb甚至用现成的 Gitweb 容器镜像往往比在同机塞 CGI 更省心。第四gerrit.config改完一定要重启并验证。Gerrit 对配置的加载不是完全热更新的[gitweb]段的改动基本都要重启。重启后别只看 Gerrit 页面有没有链接一定要实际点进去验证跳转和内容因为链接生成和实际访问是两回事。第五留一份配置备份和部署脚本。Gitweb 集成的配置分散在gerrit.config、gitweb.conf、Web 服务器配置三处任何一处丢了都要重新摸索。把这些配置纳入版本管理换机器或重装时直接套用能省掉大量重复劳动。这套集成本身不复杂难的是把权限、路径、字符集这些边角问题处理干净。把上面这些点过一遍基本就能跑出一个稳定可用的 Gitweb 集成剩下的就是按团队实际需求微调了。
返回列表