资讯详情

资讯详情

内网自建Git服务实战:CentOS 8 + Apache HTTP协议搭建轻量代码托管

1. 为什么要在内网自建 Git 服务1.1 从一次代码托管迁移说起前段时间帮一个做嵌入式开发的小团队整理代码仓库他们之前一直把代码放在公共托管平台上项目本身不涉及什么敏感内容但问题是每次拉取和推送都要走外网办公室的网络出口带宽有限几个人同时 clone 一个大仓库整个办公网就卡得不行。更麻烦的是他们的构建服务器在内网没法直接访问外网每次部署都要手动拷贝代码包效率极低。这个场景其实很典型。很多中小团队、实验室、工作室都有类似需求代码需要集中管理但又不希望或者不方便依赖外部托管服务。这时候在内网自建一个 Git 服务就是最直接的解法。Git 本身是分布式的但团队协作总需要一个中心节点来汇总代码这个中心节点可以用最朴素的方式搭建——不需要 GitLab、Gitea 这类重型平台直接用 Git 自带的 HTTP 协议支持就能跑起来。我最终给他们选的方案是VMware 虚拟机 CentOS 8 Apache httpd Git 的 HTTP 后端。选这个组合的理由很实在虚拟机方便快照和迁移CentOS 8 的软件源里 Git 和 httpd 版本都够用HTTP 协议对防火墙友好只需要放行 80 或自定义端口而且配置过程透明可控出问题容易排查。整套方案不需要额外安装任何第三方 Git 服务端软件纯靠系统自带的组件就能完成。这篇文章就把这套方案的完整落地过程拆开讲清楚包括环境准备、协议选型、Apache 配置、Git 仓库初始化、权限控制、以及我实际踩过的那些坑。适合有一定 Linux 基础、想在内网快速搭一个轻量 Git 服务的读者参考。1.2 HTTP 协议方案与其他协议的取舍Git 支持多种传输协议常见的有本地协议file://、SSH 协议、Git 协议git://和 HTTP/HTTPS 协议。每种协议都有自己的适用场景选型时不能只看哪个流行要看实际约束条件。本地协议基本只能用于本机操作团队协作场景直接排除。SSH 协议是很多人的首选配置简单、传输效率高但它要求每个使用者都在服务器上有系统账号或者至少配置好公钥认证。对于一个十几人的小团队管理 SSH 密钥还算可控但如果人员流动频繁账号管理就会变成负担。Git 协议git://速度最快但它默认没有认证机制只能做只读共享写操作需要额外配置而且很多企业防火墙会封掉 9418 端口。HTTP 协议的优势在于第一它走的是标准 Web 端口防火墙和代理设备天然放行第二认证可以交给 Apache 处理用 HTTP Basic Auth 就能实现用户名密码控制不需要给每个人开系统账号第三客户端配置简单git clone http://...这种形式对新手最友好。缺点也有配置比 SSH 略复杂传输效率稍低尤其是大仓库首次 clone而且明文 HTTP 传输时密码是 Base64 编码的安全性有限。注意如果服务只在纯内网使用HTTP 明文传输的风险可以接受如果涉及跨网段或者对安全有要求建议在 Apache 上配置 TLS 证书启用 HTTPS。本文以 HTTP 为主线讲解HTTPS 的差异点会在配置章节单独说明。综合来看对于内网、小团队、快速搭建、不想管系统账号这个需求组合HTTP 协议方案是最合适的。下面进入具体实施。2. 环境准备与基础配置2.1 虚拟机与系统安装要点VMware 里装 CentOS 8 这一步本身没什么难度但有几个细节如果一开始没注意后面会反复折腾。首先是虚拟机资源配置。Git 服务本身对 CPU 和内存要求不高2 核 2G 就能跑得很稳但如果仓库数量多、单仓库体积大比如超过 1GB建议给到 2 核 4G并且磁盘用厚置备Thick Provision而不是精简置备避免仓库增长过程中磁盘空间动态扩展带来的性能抖动。磁盘容量按所有仓库总大小 × 2.5来估算比较稳妥多出来的空间留给 Git 的对象存储和 Apache 日志。网络模式建议用桥接Bridged或者 NAT 加端口转发。桥接模式下虚拟机会获得独立 IP内网其他机器可以直接访问配置最简单。如果办公网 IP 资源紧张用 NAT 模式然后在 VMware 的虚拟网络编辑器里把宿主机的某个端口转发到虚拟机的 80 端口外部访问时用宿主机IP:转发端口即可。安装 CentOS 8 时软件选择界面勾选最小安装就够了不需要图形界面。但要注意最小安装默认不带net-toolsifconfig命令用不了习惯用ip addr查看网络信息。另外安装完成后第一件事是确认网络连通性CentOS 8 默认使用 NetworkManager 管理网络用nmcli命令检查和启用网卡。# 查看网络连接状态 nmcli connection show # 启用网卡假设连接名为 ens33 nmcli connection up ens33 # 查看 IP 地址 ip addr show ens332.2 软件源与依赖安装CentOS 8 在 2021 年底已经停止维护官方镜像源地址失效直接yum install会报错。这是很多人卡住的第一个坑。解决办法是把软件源切换到 vault 归档地址或者使用国内镜像站的归档源。# 备份原有源文件 cd /etc/yum.repos.d/ mkdir backup mv *.repo backup/ # 使用 vault 归档源以基础源为例 cat /etc/yum.repos.d/CentOS-Base.repo EOF [base] nameCentOS-8-Base baseurlhttp://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os/ gpgcheck0 enabled1 [appstream] nameCentOS-8-AppStream baseurlhttp://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/ gpgcheck0 enabled1 EOF # 清理并重建缓存 dnf clean all dnf makecache源配好之后安装 Git 和 Apachednf install -y git httpd安装完成后验证版本git --version # 输出类似 git version 2.27.0 httpd -v # 输出类似 Server version: Apache/2.4.37CentOS 8 自带的 Git 2.27 和 httpd 2.4.37 完全满足需求不需要额外编译安装。这里有个经验不要盲目追求最新版 Git系统源里的版本经过充分测试和 httpd 的mod_cgi模块兼容性最好自己编译的 Git 有时候会因为路径问题导致git-http-backend找不到。2.3 防火墙与 SELinux 处理CentOS 8 默认开启 firewalld 和 SELinux这两个都是配置正确时无感、配置错误时抓狂的组件。我的建议是firewalld 按需放行端口SELinux 不要直接关闭而是用正确的布尔值和上下文来适配。firewalld 放行 HTTP 服务# 放行 http 服务对应 80 端口 firewall-cmd --permanent --add-servicehttp # 如果自定义端口比如 8080 firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload # 确认规则 firewall-cmd --list-allSELinux 方面Apache 要执行git-http-backend这个 CGI 程序需要开启对应的布尔值# 允许 httpd 执行 CGI 脚本 setsebool -P httpd_enable_cgi on # 允许 httpd 访问网络Git 后端需要 setsebool -P httpd_can_network_connect on如果仓库目录放在非标准路径比如/data/git还需要给目录打上正确的 SELinux 上下文标签semanage fcontext -a -t httpd_sys_content_t /data/git(/.*)? restorecon -Rv /data/git提示很多人遇到 403 Forbidden 或者 500 Internal Server Error 时第一反应是关掉 SELinux这能解决问题但会留下安全隐患。正确做法是查看/var/log/audit/audit.log用ausearch -m avc -ts recent找到被拒绝的操作然后针对性地调整策略。我实测下来只要把上面几个布尔值和上下文配对SELinux 开着完全不影响 Git 服务运行。3. Apache 与 Git HTTP 后端配置3.1 理解 git-http-backend 的工作机制Git 自带的git-http-backend是一个 CGI 程序它把 HTTP 请求翻译成 Git 的内部操作。当客户端执行git clone http://server/repo.git时实际发生的流程是这样的客户端先发一个 GET 请求探测仓库信息/info/refs?servicegit-upload-packApache 把这个请求交给git-http-backend处理后端调用git upload-pack生成引用列表返回客户端拿到引用列表后再发起 POST 请求拉取实际的对象数据。推送push流程类似走的是git-receive-pack。理解这个机制很重要因为它决定了 Apache 的配置重点需要把特定路径的请求交给 CGI 处理同时要正确传递PATH_INFO和GIT_PROJECT_ROOT等环境变量。配置错了表现就是 clone 时提示 repository not found 或者卡在 Reading repository information。Apache 需要启用几个模块mod_cgi执行 CGI、mod_alias路径映射、mod_env设置环境变量、mod_auth_basic和mod_authn_file认证。CentOS 8 的 httpd 默认已经加载了大部分确认一下即可httpd -M | grep -E cgi|alias|env|auth_basic|authn_file如果某个模块没加载在/etc/httpd/conf.modules.d/下对应的配置文件中取消注释即可。3.2 仓库目录规划与初始化目录规划这件事一开始想清楚能省很多事。我的建议是采用这样的结构/data/git/ # Git 仓库根目录 ├── project-a.git/ # 裸仓库bare repository ├── project-b.git/ └── ... /etc/httpd/conf.d/git.conf # Apache 虚拟主机配置 /etc/httpd/git.passwd # 认证密码文件所有仓库都用裸仓库bare repository也就是没有工作区的仓库。裸仓库的目录名习惯以.git结尾这是约定俗成的规范客户端 clone 时 URL 里带上.git更清晰。创建裸仓库的命令# 创建仓库根目录 mkdir -p /data/git # 创建裸仓库 git init --bare /data/git/project-a.git # 修改属主让 Apache 用户apache有读写权限 chown -R apache:apache /data/git chmod -R 755 /data/git这里有个关键点Apache 默认以apache用户运行而 Git 仓库的读写操作都是通过 Apache 发起的所以仓库目录的属主必须是apache否则推送时会报权限错误。很多人用 root 创建仓库后忘了改属主结果 clone 正常但 push 失败排查半天。另外裸仓库默认的HEAD指向refs/heads/master如果团队用main作为主分支名需要手动调整git symbolic-ref HEAD refs/heads/main3.3 Apache 配置文件详解核心配置写在/etc/httpd/conf.d/git.conf里。下面这份配置我用了很多次稳定可靠# 设置 Git 项目根目录环境变量 SetEnv GIT_PROJECT_ROOT /data/git SetEnv GIT_HTTP_EXPORT_ALL # 把 git-http-backend 映射为一个 CGI 脚本路径 ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/ # 认证配置 Location /git/ AuthType Basic AuthName Git Repository AuthUserFile /etc/httpd/git.passwd Require valid-user /Location # 针对 Git 请求的细化配置 LocationMatch ^/git/.*/git-receive-pack$ AuthType Basic AuthName Git Repository AuthUserFile /etc/httpd/git.passwd Require valid-user /LocationMatch # 目录权限 Directory /usr/libexec/git-core Options ExecCGI Require all granted /Directory逐段解释一下。GIT_PROJECT_ROOT告诉后端去哪里找仓库GIT_HTTP_EXPORT_ALL表示导出根目录下所有仓库不需要每个仓库单独放git-daemon-export-ok文件。ScriptAlias把/git/开头的 URL 映射到 CGI 程序注意结尾的斜杠不能少否则路径拼接会出错。认证部分AuthUserFile指向密码文件Require valid-user表示所有请求都需要认证。这里有个细节如果希望读公开、写认证可以把认证只加在git-receive-pack的 LocationMatch 上这样 clone 不需要密码push 才需要。但内网环境下我一般建议全部认证简单省事。密码文件用htpasswd命令生成# 创建密码文件并添加第一个用户 htpasswd -c /etc/httpd/git.passwd alice # 追加用户注意不要再用 -c否则会覆盖 htpasswd /etc/httpd/git.passwd bobhtpasswd命令来自httpd-tools包如果提示找不到命令先dnf install -y httpd-tools。配置写完后检查语法并重启服务apachectl configtest # 输出 Syntax OK 才继续 systemctl restart httpd systemctl enable httpd3.4 HTTPS 配置的差异点如果决定启用 HTTPS配置上主要改两处一是把 VirtualHost 的 80 端口改成 443 并加载 SSL 模块二是配置证书路径。证书可以用自签名的内网环境下客户端首次访问会提示不受信任配置git config --global http.sslVerify false可以跳过验证仅限内网可信环境。VirtualHost *:443 SSLEngine on SSLCertificateFile /etc/httpd/ssl/server.crt SSLCertificateKeyFile /etc/httpd/ssl/server.key # 其余 Git 配置同上 /VirtualHost自签名证书生成mkdir -p /etc/httpd/ssl openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /etc/httpd/ssl/server.key \ -out /etc/httpd/ssl/server.crt \ -subj /CNgit.internal注意http.sslVerify false是全局配置会影响所有 HTTPS 请求。更精细的做法是只对特定域名关闭验证git config --global http.https://git.internal/.sslVerify false。4. 客户端使用与权限管理4.1 客户端克隆与推送实操服务端配好后客户端操作就很简单了。首次 clone 时Git 会提示输入用户名密码git clone http://git.internal/git/project-a.git # 提示 Username for http://git.internal: alice # 提示 Password for http://alicegit.internal: ******每次操作都输密码很烦可以用凭证缓存# 缓存凭证 1 小时3600 秒 git config --global credential.helper cache --timeout3600 # 或者永久存储明文保存在 ~/.git-credentials内网可用 git config --global credential.helper store推送代码cd project-a git remote add origin http://git.internal/git/project-a.git git push -u origin main如果服务端仓库是空的第一次推送用-u建立追踪关系后续直接git push即可。4.2 多用户权限的几种实现思路HTTP Basic Auth 的权限模型比较粗要么能访问要么不能访问没有只读用户和读写用户的区分。如果团队需要更细的权限控制有几种思路。第一种是按仓库分密码文件。为每个仓库配置独立的 Location指向不同的密码文件这样能实现某些人只能访问某些仓库。缺点是仓库多了配置会膨胀管理麻烦。第二种是读写分离。读操作clone、fetch不需要认证或者用公共账号写操作push用单独的认证。前面提到的 LocationMatch 方案就是这个思路。第三种是结合 gitolite 等工具。gitolite 通过 SSH 公钥做细粒度权限控制但那就偏离了 HTTP 方案的初衷。如果权限需求真的复杂到这种程度建议直接上 Gitea 这类带 Web 界面的平台配置成本反而更低。我的经验是十人以内的小团队用统一认证 全员读写就够了靠代码评审流程来保证质量不必在服务端权限上过度设计。人数再多、或者有外部协作方就该考虑更专业的平台了。4.3 仓库备份与迁移自建服务最怕的就是数据丢失。Git 仓库的备份其实很简单因为裸仓库本身就是一堆文件直接打包复制即可# 全量备份 tar czf git-backup-$(date %Y%m%d).tar.gz -C /data git # 或者用 rsync 增量同步到备份机 rsync -avz --delete /data/git/ backup-server:/backup/git/更稳妥的做法是用git clone --mirror做仓库级备份这样备份出来的是完整的裸仓库恢复时直接放回去就能用git clone --mirror /data/git/project-a.git /backup/project-a.git迁移到新服务器时把/data/git整个目录拷过去改好属主和 SELinux 上下文Apache 配置照搬基本就能无缝切换。客户端只需要改一下 remote URL 或者改 hosts 解析。5. 常见问题排查与避坑经验5.1 高频报错速查表下面这张表是我在实际运维中整理出来的覆盖了 90% 以上的问题报错现象根本原因解决方向403 ForbiddenSELinux 上下文错误或目录权限不足检查httpd_sys_content_t标签和目录属主500 Internal Server ErrorCGI 未启用或 git-http-backend 路径错误确认httpd_enable_cgi布尔值和 ScriptAlias 路径repository not foundGIT_PROJECT_ROOT 配置错误或仓库不存在核对环境变量和实际目录结构push 时 permission denied仓库属主不是 apachechown -R apache:apache /data/gitclone 卡住不动防火墙拦截或 PATH_INFO 传递异常检查 firewalld 规则和 Apache 的 AllowOverride认证反复弹出密码文件路径错误或权限问题确认 AuthUserFile 路径和文件可读性大仓库 clone 超时Apache 默认超时时间太短调整Timeout和ProxyTimeout参数5.2 几个容易忽略的细节第一个细节是 Apache 的Timeout参数。默认是 60 秒clone 大仓库时如果网络慢很容易超时中断。在git.conf里加上Timeout 600能显著改善体验。同理LimitRequestBody默认限制请求体大小推送大文件时可能被截断需要适当调大。第二个细节是git-http-backend的路径。不同发行版和安装方式下这个程序的路径可能不同。CentOS 8 通过 dnf 安装的 Git路径是/usr/libexec/git-core/git-http-backend。如果不确定用rpm -ql git | grep http-backend查一下。ScriptAlias 写错路径表现就是 404 或者 500。第三个细节是并发推送。Git 的 HTTP 后端在处理 push 时如果两个用户同时推同一个分支后推的会被拒绝non-fast-forward。这是 Git 的正常行为不是服务配置问题。解决办法是让用户先 pull 再 push或者配置服务端的receive.denyNonFastForwards策略。第四个细节是日志。Apache 的访问日志在/var/log/httpd/access_log错误日志在error_log。Git 操作出问题时先看 error_log里面通常有明确的错误信息。如果日志里只有 Premature end of script headers多半是 CGI 执行失败检查 SELinux 和文件权限。5.3 性能调优的实用建议内网 Git 服务的性能瓶颈通常不在 CPU而在磁盘 I/O 和网络。几个实用的调优点启用 Apache 的KeepAlive减少连接建立开销KeepAlive On MaxKeepAliveRequests 200 KeepAliveTimeout 15如果仓库数量多考虑用git gc定期整理仓库压缩对象存储# 对所有仓库执行 gc for repo in /data/git/*.git; do git -C $repo gc --aggressive --prunenow done这个操作比较耗时建议放在业务低峰期用 cron 定时执行。我一般配置成每周日凌晨跑一次。另外如果团队经常 clone 同一个大仓库可以在服务端开启git daemon作为补充让只读操作走更高效的 Git 协议写操作仍然走 HTTP。不过这会增加配置复杂度小团队没必要。6. 写在最后的一点个人体会这套方案我从几年前开始用前后在好几个不同规模的环境里部署过最大的感受是简单的东西往往最耐用。GitLab、Gitea 功能确实强大但它们引入了数据库、后台服务、Web 框架等一堆依赖出问题时排查链路很长。而 Apache git-http-backend 这套组合组件少、日志清晰、配置透明出问题基本看两眼日志就能定位。当然它也有明显的边界。没有 Web 界面新人上手需要记几个命令权限控制粗糙不适合复杂的组织架构没有 Issue、PR 这些协作功能。所以我的建议是十人以内、纯代码托管需求这套方案足够一旦涉及跨团队协作、需要 Web 界面或者细粒度权限就该考虑更专业的平台了。最后分享一个我踩过的坑有次迁移服务器仓库目录拷过去后忘了改 SELinux 上下文clone 一直报 403查了半天权限和 Apache 配置都没问题最后ausearch一看才发现是 SELinux 拦的。从那以后我养成了一个习惯——迁移或新建目录后第一件事就是restorecon -Rv刷一遍上下文能省掉很多莫名其妙的排查时间。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →