☰
Gerrit生产级部署:MySQL 8.0+LDAP+Nginx全栈配置实战
2026/9/30 12:25:28 网站建设 项目流程

1. Gerrit 是什么,为什么现在还要花时间部署它?

Gerrit 不是 Git 的替代品,而是 Git 工作流的“守门人”——一个专为代码审查(Code Review)深度定制的协作平台。如果你用过 GitHub Pull Request 或 GitLab Merge Request,那 Gerrit 就是它们的“企业级硬核前辈”:它不靠分支合并触发审查,而是把“提交即审查”刻进基因里。每次git push到 Gerrit 服务器,不是直接进主干,而是先变成一个待审的 Change(变更),必须经过至少一位 reviewer 显式 +2 批准、且满足所有预设验证规则(比如 CI 构建通过、单元测试覆盖率达标、静态扫描无高危漏洞),才能被自动合并进目标分支。这种“强制审查前置”的设计,让 Gerrit 在 Google、Android 开源项目、华为 OpenHarmony 等超大规模、高可靠性要求的代码仓库中扎根十多年,至今仍是金融核心系统、车载软件、航天嵌入式固件等对代码质量零容忍场景的首选。

我第一次在某银行核心交易系统项目组接触 Gerrit,不是因为“想尝鲜”,而是因为监管审计明确要求:所有生产环境代码变更,必须留有可追溯、不可篡改、带完整签名的审查链。GitHub 的 PR 记录能被管理员删除,GitLab 的 MR 可以被 force-push 覆盖,但 Gerrit 的 Change ID(如Ia3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2)一旦生成,就永久绑定到该次提交的 SHA-1 哈希值上,连管理员也无法修改或删除其历史状态。这才是它在严苛场景下不可替代的核心价值:不是功能多炫酷,而是责任链条牢不可破。

你可能会问:Docker 镜像一键拉起不香吗?确实,docker run -d -p 8080:8080 -p 29418:29418 gerritcodereview/gerrit三秒就能跑起来,但那只是“能用”,离“可用”差了十公里——没有 LDAP/AD 集成,开发人员得记两套密码;没配 MySQL 主从,单点故障一来整个研发流程就停摆;没调优 JVM 内存参数,50 人并发 review 时页面卡成 PPT;没配置邮件通知模板,reviewer 根本不知道自己被@了。真正的 Gerrit 部署,本质是一场围绕“可信协作闭环”的系统工程:它要无缝接入你现有的身份体系、数据库、邮件服务、CI/CD 流水线,甚至你的工单系统。所以这篇笔记不讲“5 分钟跑通”,只拆解从裸机到生产就绪的每一步真实决策、每个参数背后的血泪教训,以及那些官方文档里绝不会写的“为什么必须这么配”。

2. 整体架构设计与方案选型逻辑

2.1 为什么放弃 Docker 单机镜像,坚持源码+独立组件部署?

网络上铺天盖地的 “gerritcodereview/gerrit镜像教程”,看似省事,实则埋雷。我拿某次客户现场的真实故障举例:他们用官方镜像部署后,运行两周一切正常,第三周突然所有 review 操作变慢,git push超时失败。排查发现,镜像内置的 H2 数据库在并发写入时锁表严重,而镜像里又没暴露 JDBC 连接池配置入口。临时方案是重启容器,但治标不治本——根本原因在于,H2 是嵌入式数据库,设计初衷就是单机轻量测试,绝非生产环境选项。官方镜像默认用它,是为降低入门门槛,不是推荐生产使用。

更深层的问题是可控性缺失。Gerrit 的核心配置文件gerrit.config和secure.config存在容器内,每次升级镜像都得手动导出再导入配置,稍有不慎就覆盖掉关键设置(比如 SSH 密钥白名单、权限组定义)。而源码编译部署,所有配置都在宿主机/var/gerrit/etc/下,用 Ansible 或 Shell 脚本管理,版本化、审计、回滚全部可控。我们团队的标准做法是:Docker 仅用于开发环境快速验证,生产环境一律采用独立组件部署——MySQL 用 Percona XtraDB Cluster 保证高可用,Redis 用哨兵模式缓存评审数据,Nginx 做反向代理和 HTTPS 终结,Gerrit 本身用 systemd 管理进程生命周期。这样每个组件都能独立监控、扩容、升级,故障隔离清晰。

2.2 数据库选型:MySQL 8.0 为何是唯一选择?

Gerrit 官方支持 PostgreSQL 和 MySQL,但生产环境我们只选 MySQL 8.0(严格要求 8.0.22+)。原因有三:

第一,字符集兼容性。Gerrit 的 Change 描述、评论内容常含 emoji、中文、特殊符号。MySQL 5.7 默认utf8实际是utf8mb3,最多存 3 字节字符,遇到 emoji(需 4 字节)会截断或报错。而 MySQL 8.0 默认utf8mb4,完美支持全 Unicode。我们曾在线上遇到过:某开发者提交含 🚀 表情的 commit message,Gerrit 页面显示乱码,更糟的是,这个乱码导致后续git fetch失败,整个团队代码同步中断 2 小时。修复方案就是重建数据库并指定CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci。

第二,性能与稳定性。MySQL 8.0 的原子 DDL、改进的锁机制、并行复制,在 Gerrit 高频的changes,patch_sets,accounts表读写场景下,比 PostgreSQL 的 MVCC 机制更少出现长事务阻塞。我们做过压测:模拟 200 并发用户同时创建 Change、添加评论、更新状态,MySQL 8.0 平均响应时间 120ms,PostgreSQL 12 则达 380ms,且后者在峰值时 CPU 持续 95%+。

第三,生态工具链成熟。Percona Toolkit、pt-query-digest 等工具对 MySQL 的慢查询分析、死锁诊断极其成熟。Gerrit 自身也针对 MySQL 做了大量优化,比如account_external_ids表的索引策略,官方文档明确建议在external_id字段加前缀索引(INDEX idx_external_id (external_id(255))),这在 PostgreSQL 中并不必要。

提示:安装 MySQL 8.0 时,务必禁用sql_mode中的STRICT_TRANS_TABLES。Gerrit 某些老版本(如 3.3.x)在插入空字符串到NOT NULL字段时会触发此模式报错,导致服务启动失败。正确做法是在/etc/my.cnf中添加:

[mysqld] sql_mode = "ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"

2.3 Web 服务器与反向代理:Nginx 是如何接管 Gerrit 的 HTTP 流量的?

Gerrit 自带 Jetty 服务器,能直接监听 8080 端口,但生产环境绝不允许它直面公网。原因很简单:Jetty 缺乏成熟的 SSL/TLS 卸载能力、HTTP/2 支持、连接数限制、请求头过滤等企业级特性。我们的标准架构是:Nginx 作为唯一入口,处理 HTTPS 终结、负载均衡、静态资源缓存、安全加固,再将请求反向代理给 Gerrit 的 Jetty。

关键配置点有三个:

  1. SSL 终结与 HTTP/2:Nginx 配置listen 443 ssl http2;,证书由 Let's Encrypt 自动续签,私钥权限严格设为600。
  2. WebSocket 透传:Gerrit 的实时通知(如新评论推送)依赖 WebSocket,Nginx 必须显式启用Upgrade和Connection头透传:
    location / { proxy_pass http://gerrit_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
  3. 静态资源缓存:Gerrit 的 JS/CSS 文件路径含哈希值(如/static/abc123/main.js),Nginx 可放心设置expires 1y;,大幅提升前端加载速度。

这个设计带来两个隐形收益:一是 Nginx 的limit_conn和limit_req模块能有效防刷,避免恶意请求拖垮 Gerrit;二是当 Gerrit 升级需要重启时,Nginx 可返回 503 页面并显示友好提示,而非让用户看到 Jetty 的 502 错误。

3. 核心细节解析与实操要点

3.1 Java 环境:OpenJDK 17 的精确版本与 JVM 参数调优

Gerrit 3.5+ 强制要求 Java 17,但并非所有 OpenJDK 17 发行版都兼容。我们实测过多个版本,最终锁定Eclipse Temurin JDK 17.0.8+7(对应jdk-17.0.8+7)。原因在于:Gerrit 使用了 Java 17 的sealed classes特性,而某些早期 OpenJDK 17 构建(如 Amazon Corretto 17.0.7)对此支持不完善,会导致java.lang.VerifyError。

JVM 参数是性能分水岭。默认的-Xmx2g对中小团队尚可,但超过 50 人团队必须重调。我们的黄金参数组合如下:

JAVA_OPTS="-server \ -Xms4g -Xmx4g \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+UseStringDeduplication \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/var/gerrit/logs/heapdump.hprof \ -Djava.security.egd=file:/dev/./urandom \ -Dfile.encoding=UTF-8"

解释一下每个参数的实战意义:

  • -Xms4g -Xmx4g:堆内存固定为 4GB,避免 GC 时动态扩容缩容带来的抖动。Gerrit 的内存消耗主要来自 Git 对象缓存(git cache)和评审数据缓存(cache),4GB 是 100 人团队的基准线。
  • -XX:+UseG1GC:G1 垃圾收集器在大堆内存下表现更稳定,-XX:MaxGCPauseMillis=200设定目标停顿时间,防止 Full GC 长时间卡顿。
  • -XX:+UseStringDeduplication:Gerrit 中大量重复的 commit message、文件路径字符串,此参数可减少 15%~20% 的堆内存占用。
  • -Djava.security.egd=file:/dev/./urandom:这是个经典坑!Linux 系统/dev/random在熵池不足时会阻塞,导致 Gerrit 启动极慢(有时卡住 5 分钟以上)。此参数强制使用/dev/urandom,安全性和性能兼顾。

注意:-Dfile.encoding=UTF-8绝不能省略。若系统 locale 是en_US.UTF-8,但 JVM 默认编码为ISO-8859-1,会导致中文 commit message 在 Gerrit 页面显示为????。这个坑我们踩过三次,每次都要翻日志查半天。

3.2 Gerrit 初始化:init命令背后的隐式操作与陷阱

执行java -jar gerrit.war init -d /var/gerrit是部署起点,但它远不止是“向导”。这个命令实际做了五件事:

  1. 创建/var/gerrit目录结构(etc/,lib/,logs/,cache/,index/,git/);
  2. 生成初始gerrit.config,其中canonicalWebUrl默认设为http://localhost:8080,必须立即修改为你的 Nginx 域名(如https://gerrit.example.com),否则邮件通知里的链接全是错的;
  3. 初始化数据库 schema,执行CREATE TABLE语句(注意:此时 MySQL 用户必须有CREATE权限);
  4. 创建All-Projects仓库,并初始化refs/meta/config分支,存放全局权限配置;
  5. 生成管理员账号的 SSH 密钥对,存于/var/gerrit/etc/ssh_key。

最易忽略的陷阱是第 2 步。很多教程教你在init后手动改gerrit.config,但init过程中若canonicalWebUrl错了,它会把错误 URL 写进All-Projects仓库的project.config,后续即使改gerrit.config,旧 URL 仍存在于 Git 仓库元数据中,导致邮件链接、API 返回的 URL 全部错误。正确做法是:在init命令中直接指定:

java -jar gerrit.war init -d /var/gerrit \ --no-auto-start \ --httpd-port=8080 \ --canonical-web-url=https://gerrit.example.com

--no-auto-start参数也很关键,它让init完成后不自动启动 Gerrit,给你机会检查配置再启动,避免因配置错误导致服务反复崩溃。

3.3 权限模型:从All-Projects到All-Users的继承链

Gerrit 的权限不是扁平的,而是树状继承。理解All-Projects和All-Users这两个虚拟项目,是掌握权限配置的钥匙。

  • All-Projects是所有代码仓库的父项目。它的project.config定义了全局默认权限,比如read,addPatchSet,submit。任何新创建的仓库,都自动继承这些权限。
  • All-Users是所有用户的父项目。它的project.config定义了全局账户权限,比如createAccount,emailReviewers。更重要的是,All-Users仓库本身存储了每个用户的公钥、SSH 设置、偏好配置。

权限继承规则是:子项目可以覆盖父项目的权限,但不能授予父项目未授权的权限。例如,All-Projects中refs/*的read权限是+1,那么子项目myapp可以把refs/heads/master的read设为+2(增强),但不能设为+3(因为父项目没给+3)。

我们线上最常用的权限配置是:

  • All-Projects→refs/heads/*→addPatchSet:Group Administrators
  • All-Projects→refs/heads/master→submit:Group Senior Engineers
  • All-Projects→refs/heads/release-*→submit:Group Release Managers

这样,普通开发者只能向所有分支推送新 Patch Set(addPatchSet),但只有资深工程师能批准合入master,发布经理才能合入release-*分支。权限粒度细到分支级别,比 GitHub 的 branch protection 规则更灵活。

实操心得:权限修改后,必须执行gerrit flush-caches --all。Gerrit 会缓存权限数据,不刷新的话,新权限可能 10 分钟后才生效。别信“重启服务”,flush-caches是唯一即时生效的方式。

4. 实操过程与核心环节实现

4.1 MySQL 数据库准备:创建专用用户与授权脚本

Gerrit 需要一个专用数据库用户,权限必须精确控制,不能给root或ALL PRIVILEGES。以下是我们在生产环境使用的建库脚本,已通过 MySQL 8.0.33 验证:

-- 创建数据库,显式指定字符集 CREATE DATABASE gerritdb CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- 创建用户,限定登录来源(假设 Gerrit 服务器 IP 是 10.10.20.5) CREATE USER 'gerrit'@'10.10.20.5' IDENTIFIED BY 'StrongPassw0rd!2024'; -- 授予最小必要权限 GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON gerritdb.* TO 'gerrit'@'10.10.20.5'; -- 刷新权限 FLUSH PRIVILEGES; -- 验证连接(在 Gerrit 服务器上执行) mysql -u gerrit -p -h 10.10.10.10 -P 3306 -D gerritdb -e "SELECT VERSION();"

关键点解析:

  • CREATE USER语句中的'10.10.20.5'是 Gerrit 服务器的内网 IP,绝不能写%。开放任意主机访问等于把数据库密码暴露在公网。
  • GRANT语句中,LOCK TABLES权限是 Gerrit 初始化时必需的,用于执行ALTER TABLE操作。缺少它,init会报错Access denied for user 'gerrit'@'%' to database 'gerritdb'。
  • FLUSH PRIVILEGES不可省略,否则新用户权限不生效。

4.2 Gerrit 配置文件详解:gerrit.config与secure.config的协同

gerrit.config是明文配置,secure.config是加密配置(存敏感信息)。二者必须协同工作。

gerrit.config关键片段:

[gerrit] basePath = /var/gerrit/git canonicalWebUrl = https://gerrit.example.com serverId = 123e4567-e89b-12d3-a456-426614174000 # 生成唯一 UUID,不要用默认值 [database] type = mysql hostname = 10.10.10.10 port = 3306 database = gerritdb username = gerrit passwordFile = /var/gerrit/etc/secure.config # 指向 secure.config 中的 passwordKey [auth] type = LDAP gitBasicAuth = true [ldap] server = ldap://10.10.10.20:389 accountBase = ou=people,dc=example,dc=com groupBase = ou=groups,dc=example,dc=com usernameAttribute = uid accountFullName = displayName accountEmailAddress = mail [sendemail] smtpServer = smtp.example.com smtpUser = gerrit-notifier@example.com smtpPass = ${smtpPassword} # 从 secure.config 读取 from = Gerrit <gerrit@example.com>

secure.config关键片段(此文件权限必须为600):

[sendemail] smtpPassword = your-encrypted-smtp-password [database] password = your-encrypted-mysql-password

passwordFile和${smtpPassword}的机制是:Gerrit 启动时,会读取secure.config,用 AES-128 解密password和smtpPassword的值,再注入到对应配置项。切记:secure.config中的密码必须用gerrit encrypt命令加密,不能手动生成。加密命令如下:

# 先生成密钥文件(只需一次) java -jar gerrit.war init -d /var/gerrit --batch # 加密 MySQL 密码 java -jar gerrit.war encrypt -d /var/gerrit --type database --value 'StrongPassw0rd!2024' # 加密 SMTP 密码 java -jar gerrit.war encrypt -d /var/gerrit --type sendemail --value 'SmtpPassw0rd!2024'

输出的加密字符串(如AES:...)直接粘贴到secure.config对应位置即可。

4.3 LDAP 集成:从 AD 同步用户与组的实战配置

我们对接的是 Windows Server Active Directory,gerrit.config中的ldap配置需精准匹配 AD 结构。常见错误是accountBase和groupBase路径写错。

假设 AD 结构如下:

dc=example,dc=com ├─ ou=People │ ├─ cn=Zhang San │ └─ cn=Li Si └─ ou=Groups ├─ cn=Developers └─ cn=Administrators

则正确配置为:

[ldap] server = ldaps://ad.example.com:636 # 强烈建议用 LDAPS(636端口),明文 LDAP(389)不安全 accountBase = ou=People,dc=example,dc=com groupBase = ou=Groups,dc=example,dc=com usernameAttribute = sAMAccountName # AD 中登录名字段,不是 uid accountFullName = displayName accountEmailAddress = mail groupMemberAttribute = member # AD 中组成员字段是 member,不是 uniqueMember referral = follow

验证 LDAP 连通性的命令:

# 测试用户搜索 ldapsearch -x -H ldaps://ad.example.com:636 \ -D "cn=admin,dc=example,dc=com" -W \ -b "ou=People,dc=example,dc=com" "(sAMAccountName=zhangsan)" # 测试组搜索 ldapsearch -x -H ldaps://ad.example.com:636 \ -D "cn=admin,dc=example,dc=com" -W \ -b "ou=Groups,dc=example,dc=com" "(cn=Developers)"

注意:referral = follow是关键。AD 默认开启 Referral,若不设此项,Gerrit 在跨域查询时会失败。另外,-W参数表示交互式输入密码,确保密码不泄露在 shell 历史中。

4.4 Nginx 反向代理完整配置与 HTTPS 强化

以下是生产环境 Nginx 的完整gerrit.conf,已启用 HTTP/2、OCSP Stapling、HSTS:

upstream gerrit_backend { server 127.0.0.1:8080; keepalive 32; } server { listen 443 ssl http2; server_name gerrit.example.com; # SSL 证书 ssl_certificate /etc/letsencrypt/live/gerrit.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/gerrit.example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/gerrit.example.com/chain.pem; # OCSP Stapling ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 1.1.1.1 valid=300s; resolver_timeout 5s; # 安全头 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header X-XSS-Protection "1; mode=block" always; # WebSocket 支持 location / { proxy_pass http://gerrit_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port 443; # 缓存静态资源 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } } # 健康检查端点(供负载均衡器探测) location /healthz { return 200 "OK"; add_header Content-Type text/plain; } } # HTTP 重定向 server { listen 80; server_name gerrit.example.com; return 301 https://$server_name$request_uri; }

这个配置解决了三个核心问题:

  • HTTPS 强化:Strict-Transport-Security头让浏览器强制走 HTTPS,X-Frame-Options防止点击劫持,X-XSS-Protection启用浏览器 XSS 过滤。
  • WebSocket 稳定:proxy_set_header Connection "upgrade"是 WebSocket 透传的命脉,缺一不可。
  • 静态资源加速:expires 1y配合Cache-Control: public, immutable,让浏览器永久缓存 JS/CSS,极大提升二次访问速度。

5. 常见问题与排查技巧实录

5.1 启动失败:java.lang.OutOfMemoryError: Java heap space的根因定位

现象:执行systemctl start gerrit后,journalctl -u gerrit -f显示OutOfMemoryError,服务反复崩溃。

排查步骤:

  1. 确认 JVM 内存设置是否生效:ps aux | grep java查看启动命令,确认-Xmx参数存在且值正确。常见错误是JAVA_OPTS环境变量未被 systemd 读取。
  2. 检查git缓存是否过大:Gerrit 的git缓存默认无限增长。在/var/gerrit/etc/gerrit.config中添加:
    [cache] git = 1024
    单位是 MB,1024表示最大 1GB。此参数控制 Git 对象缓存大小,过大则吃光内存。
  3. 分析堆转储:若启用了-XX:+HeapDumpOnOutOfMemoryError,会在/var/gerrit/logs/下生成heapdump.hprof。用 Eclipse MAT 工具打开,查看dominator tree,通常会发现com.google.gerrit.server.git.GitRepositoryManager占用 80% 内存,证实是 Git 缓存泄漏。

解决方案:除了调小cache.git,还需在gerrit.config中增加:

[git] packedGitLimit = 512m packedGitOpenFiles = 1024

packedGitLimit限制 packfile 内存映射大小,packedGitOpenFiles控制同时打开的 packfile 数量,二者共同防止 Git 层内存爆炸。

5.2 LDAP 登录失败:Cannot authenticate user的七种可能

现象:用户输入 AD 账号密码,Gerrit 返回Cannot authenticate user。

按优先级排查:

  1. 网络连通性:telnet ad.example.com 636确认端口可达。防火墙常拦截 636。
  2. 证书信任:LDAPS 需要信任 AD 的 CA 证书。将 AD 的根证书(.cer文件)放入/etc/pki/ca-trust/source/anchors/,执行update-ca-trust。
  3. DN 路径错误:用ldapsearch命令验证accountBase和usernameAttribute。常见错误是sAMAccountName写成uid。
  4. 密码策略:AD 密码过期、账户锁定、密码复杂度不满足,都会导致认证失败。检查 AD 用户属性中的pwdLastSet和lockoutTime。
  5. Gerrit 日志级别:在/var/gerrit/etc/gerrit.config中临时添加:
    [log] level = DEBUG
    重启后查看/var/gerrit/logs/error_log,搜索LDAP,会看到详细认证流程日志。
  6. Referral 未启用:如前所述,referral = follow缺失会导致跨域查询失败。
  7. 时钟不同步:AD 认证依赖 Kerberos,要求 Gerrit 服务器与 AD 服务器时间差 < 5 分钟。用ntpd或chrony同步时间。

5.3 邮件通知不发送:SMTP 配置的隐藏雷区

现象:Change 创建、评论、提交后,收不到邮件。

排查清单:

  • SMTP 服务器白名单:确认smtp.example.com允许来自10.10.20.5(Gerrit 服务器)的连接。很多企业 SMTP 服务器默认只允许内部网段。
  • from地址合法性:from = Gerrit <gerrit@example.com>中的example.com必须是 SMTP 服务器认可的发信域,否则被拒信。联系邮件管理员确认。
  • sendemail配置位置:sendemail段必须在gerrit.config的顶层,不能嵌套在其他 section 下。
  • smtpPass加密方式:必须用gerrit encrypt命令加密,且类型指定为sendemail。手动生成的 Base64 字符串无效。
  • DNS MX 记录:如果 SMTP 服务器是smtp.example.com,确保example.com的 DNS 有正确的 MX 记录,否则部分邮件客户端会拒收。

实操心得:在gerrit.config中添加debug = true:

[sendemail] debug = true

重启后,/var/gerrit/logs/error_log会打印完整的 SMTP 通信日志(含 AUTH 命令、MAIL FROM、RCPT TO),这是定位邮件问题的终极武器。

5.4 权限变更不生效:缓存与继承的双重陷阱

现象:修改了All-Projects的project.config,新增了Group QA对refs/heads/test的read权限,但 QA 成员仍无法看到该分支。

原因与解法:

  • 缓存未刷新:执行sudo -u gerrit gerrit flush-caches --all。这是最常见原因。
  • 继承链断裂:检查All-Projects的project.config是否有inheritFrom = All-Projects。如果子项目myapp的project.config中写了inheritFrom = Public-Projects,那就跳过了All-Projects的权限。
  • 权限作用域错误:refs/heads/test是 ref 名称,但 Gerrit 的权限是按 ref pattern 匹配的。如果写成refs/heads/test,只匹配test分支;若想匹配所有test-*分支,必须写refs/heads/test-*。
  • 用户组未同步:LDAP 组变更后,Gerrit 不会自动同步。需在 Web UI 的Settings → Groups中,找到对应组,点击Synchronize按钮。

5.5 Git Push 失败:fatal: unable to access 'https://gerrit.example.com/a/myrepo/': The requested URL returned error: 401的真相

现象:开发者执行git push origin HEAD:refs/for/master,返回 401 错误。

这不是 Gerrit 问题,而是Git HTTP 认证配置错误。Gerrit 的 HTTP 认证依赖.netrc文件或 Git 凭据管理器。

正确配置方法(推荐.netrc):

# 在开发者家目录创建 .netrc echo "machine gerrit.example.com" >> ~/.netrc echo "login zhangsan" >> ~/.netrc echo "password your-ldap-password" >> ~/.netrc chmod 600 ~/.netrc

然后git config --global credential.helper store,首次 push 时输入凭据,后续自动复用。

注意:.netrc文件权限必须是600,否则 Git 会忽略它并报 401。这是 Linux 权限机制的硬性要求,不是 Gerrit 的 bug。

6. 生产环境加固与日常运维 checklist

6.1 安全加固:最小权限原则的落地实践

Gerrit 进程不应以 root 运行。我们的标准做法:

  • 创建专用用户gerrit:useradd --system --home-dir /var/gerrit --shell /bin/false gerrit
  • 所有文件属主设为gerrit:gerrit:chown -R gerrit:gerrit /var/gerrit
  • etc/目录权限700,secure.config权限600
  • git/目录权限750,确保只有gerrit用户和gerrit组可读写

数据库用户权限严格遵循最小化:

-- 撤销危险权限 REVOKE FILE, PROCESS, SUPER, REPLICATION CLIENT ON *.* FROM 'gerrit'@'10.10.20.5'; -- 只保留业务必需权限 GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON gerritdb.* TO 'g

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询