1. 为什么选择在 Ubuntu 上自建代码托管平台1.1 自建托管平台的核心动机很多团队在起步阶段会直接使用公有代码托管服务省事、免维护这本身没什么问题。但当项目逐渐变大尤其是涉及私有代码资产、内部构建流水线、权限分级管理时把代码放在别人的服务器上总让人心里不踏实。我在几个不同规模的团队里都经历过这个阶段一开始用公有服务后来因为合规要求、网络延迟、CI 资源限制等原因最终都转向了自建。自建代码托管平台的好处很直接数据完全在自己手里权限模型可以按团队实际情况定制CI/CD 的 runner 可以部署在内网机器上构建速度不受公网带宽限制。而且一旦跑起来后续的维护成本其实比想象中低很多——只要前期把安装和配置做扎实后面基本就是偶尔升级一下版本。Ubuntu 作为服务器操作系统在这个场景下几乎是默认选择。它的软件源丰富、社区文档齐全、长期支持版本稳定而且绝大多数运维人员对它的熟悉程度最高。把代码托管平台装在 Ubuntu 上遇到问题搜一下基本都能找到答案这对没有专职运维的小团队来说非常重要。1.2 安装方式的选择为什么推荐程序包安装安装这类平台通常有三条路源码编译、容器化部署、程序包直接安装。源码编译最灵活但最折腾依赖一堆 Ruby 环境编译一次少说半小时出了问题排查起来也头疼。容器化部署看起来优雅但涉及到数据持久化、网络模式、资源限制这些细节时对不熟悉容器的人反而增加了心智负担。程序包安装是我最推荐的方式原因有三点。第一官方提供的程序包已经把运行时依赖、数据库、缓存、Web 服务全部打包好了安装过程就是解压加配置不需要你单独去装 PostgreSQL、Redis、Nginx 这些东西。第二升级路径清晰后续换版本只需要下载新包再执行一条重配置命令。第三出问题时日志集中、组件版本匹配不会出现“某个依赖版本不对导致服务起不来”这种经典噩梦。注意程序包安装虽然省事但对系统资源有最低要求。官方建议至少 4GB 内存如果团队人数超过 10 人或者要跑 CI建议直接上 8GB。内存不足时服务会频繁重启表现得很像“随机崩溃”排查起来很浪费时间。1.3 适用场景与读者定位这套方案适合几类人一是小团队的技术负责人想给团队搭一个私有的代码托管和协作平台二是个人开发者想在自己的开发机上跑一套完整环境做实验三是运维人员需要在内网快速部署一套可用的代码管理服务。不管你之前有没有接触过这类平台只要你会用基本的 Linux 命令、能看懂配置文件跟着走一遍就能跑起来。我会把每一步的操作意图和可能踩的坑都讲清楚而不是只丢一堆命令让你复制。2. 安装前的环境准备与依赖检查2.1 系统版本与硬件资源确认动手之前先确认系统版本。打开终端执行lsb_release -a输出里会显示发行版代号比如 22.04 对应 jammy20.04 对应 focal。这个代号很重要因为下载程序包时要选对应的版本选错了安装会失败。硬件方面用下面两条命令看内存和磁盘free -h df -h /内存低于 4GB 的话建议先加内存再装或者至少配置好交换分区。磁盘方面程序包本身加上依赖大概占 2GB 左右但代码仓库和 CI 产物会持续增长所以根分区至少留 20GB 余量。我见过有人装完只剩几百兆空间跑了两天仓库就写不进去了。2.2 更新系统与安装基础依赖先更新软件源并升级已有包sudo apt update sudo apt upgrade -y然后安装几个后续会用到的工具sudo apt install -y curl openssh-server ca-certificates tzdata perl这里解释一下每个包的作用。curl用来下载程序包openssh-server保证你能通过 SSH 推送代码ca-certificates是 HTTPS 证书验证的基础tzdata负责时区不装的话日志时间可能对不上perl是安装脚本运行时需要的。提示如果你的服务器在国内apt update可能会比较慢。可以先把软件源换成国内镜像这一步能省不少时间。换源的方法网上很多核心就是修改/etc/apt/sources.list里的地址改完再执行一次apt update。2.3 配置邮件服务可选但建议代码托管平台需要发邮件比如用户注册确认、密码重置、合并请求通知。如果只是内部测试可以跳过这一步但生产环境建议配上。最省事的方案是装一个本地邮件转发工具sudo apt install -y postfix安装过程中会弹出一个配置界面选择“Internet Site”然后填上你的域名。如果你有现成的 SMTP 服务也可以后面在平台配置文件里直接指定外部 SMTP那样就不用装 postfix 了。2.4 防火墙与端口规划安装前先想清楚要用哪些端口。默认情况下Web 界面走 80 或 443SSH 推送走 22。但如果服务器上已经有别的服务占了 22 端口就需要给代码托管平台单独分配一个 SSH 端口比如 2222。用ufw管理防火墙的话放行规则这样写sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 2222/tcp sudo ufw enable注意如果你打算用 2222 作为 SSH 端口记得在后续配置文件里同步修改否则推送代码时会连不上。这个坑我踩过表现是git clone一直卡住然后超时排查半天才发现是端口没对上。3. 下载与安装程序包的核心步骤3.1 获取对应版本的程序包官方提供了两种获取方式一种是添加软件源后用包管理器安装另一种是直接下载程序包文件。前者方便后续升级后者适合离线环境。这里我推荐直接下载程序包因为可控性更强下载完可以先校验再安装。先确定系统代号然后拼出下载地址。以 Ubuntu 22.04 为例curl -L -O --output-dir /tmp \ https://packages.example.com/ubuntu/jammy/packages/gitlab-ce_16.0.0-ce.0_amd64.deb实际使用时把域名和版本号替换成你要的。下载完成后检查文件大小正常应该在 1GB 左右。如果只有几十 KB说明下载的是错误页面而不是真正的包。3.2 校验与安装下载完先校验一下文件完整性ls -lh /tmp/gitlab-ce_*.deb确认大小正常后执行安装sudo dpkg -i /tmp/gitlab-ce_*.debdpkg安装过程中如果提示缺少依赖执行下面这条命令自动补齐sudo apt install -f -y安装完成后系统会自动创建配置目录/etc/gitlab、数据目录/var/opt/gitlab、日志目录/var/log/gitlab。这三个目录后面会经常用到建议记一下。提示安装过程会触发一系列初始化脚本可能需要几分钟。期间终端看起来像卡住了其实是在后台配置数据库和缓存耐心等它跑完就行不要强行中断。3.3 首次配置修改外部访问地址安装完成后第一件事是修改配置文件里的外部访问地址。打开配置文件sudo vim /etc/gitlab/gitlab.rb找到external_url这一行改成你的实际地址external_url http://your-server-ip-or-domain如果你打算用 HTTPS就写成https://开头。如果 SSH 端口不是默认的 22还要加上这两行gitlab_rails[gitlab_shell_ssh_port] 2222改完保存执行重配置命令让改动生效sudo gitlab-ctl reconfigure这条命令是整套方案的核心它会根据配置文件重新生成所有组件的配置并重启服务。第一次执行时间比较长大概三到五分钟。看到输出里出现gitlab Reconfigured!就说明成功了。3.4 验证服务状态重配置完成后检查各组件是否正常运行sudo gitlab-ctl status正常输出里每个组件前面都应该是run:。如果有down:的说明对应组件没起来需要看日志排查。查看整体日志sudo gitlab-ctl tail这个命令会实时滚动所有组件的日志按CtrlC退出。排查问题时非常有用。4. 初始化设置与日常运维要点4.1 获取初始管理员密码安装完成后系统会生成一个初始管理员密码存在这个文件里sudo cat /etc/gitlab/initial_root_password这个文件在首次重配置后 24 小时会被自动删除所以拿到密码后立刻登录修改。用浏览器打开你配置的地址用户名填root密码粘贴文件里的那串字符。登录后第一件事就是改密码路径在右上角头像 - Edit profile - Password。改完密码后建议把那个初始密码文件手动删掉避免残留。4.2 创建用户与项目结构规划作为管理员接下来要规划用户和项目结构。我的经验是不要一上来就建一堆用户先把组织结构想清楚。通常有两种模式按团队分组建项目或者按项目类型分组。创建用户的路径在 Admin Area - Users - New user。填用户名、邮箱系统会发一封设置密码的邮件。如果邮件服务没配好也可以手动设置密码。项目分组建议用命名空间来管理比如backend、frontend、infra这样的组名然后把对应项目放进去。这样权限管理会清晰很多给某个人加某个组的权限他就能访问组下所有项目。4.3 备份策略与恢复演练自建平台最重要的一件事就是备份。程序包安装方式自带备份工具执行sudo gitlab-backup create备份文件默认存在/var/opt/gitlab/backups/目录下。但注意这个命令只备份数据不备份配置文件。配置文件需要单独备份sudo cp /etc/gitlab/gitlab.rb /path/to/backup/ sudo cp /etc/gitlab/gitlab-secrets.json /path/to/backup/gitlab-secrets.json这个文件特别关键它存了数据库加密密钥和 CI 变量密钥。如果恢复时没有这个文件所有加密数据都解不开CI 变量会全部丢失。注意备份文件要定期同步到另一台机器或对象存储上。我见过有人备份文件放在同一块盘上结果盘坏了数据和备份一起没了。备份的价值在于异地同盘备份等于没备份。恢复的时候先把配置文件和 secrets 文件放回原位然后执行sudo gitlab-ctl stop sudo gitlab-backup restore BACKUP备份文件名 sudo gitlab-ctl reconfigure sudo gitlab-ctl start恢复过程会覆盖现有数据所以操作前务必确认清楚。4.4 版本升级的正确姿势升级不能跳版本必须按官方给出的升级路径一步步来。比如从 15.x 升到 16.x中间可能要经过好几个中间版本。跳版本升级大概率会导致数据库迁移失败服务起不来。升级步骤和安装类似下载新版本程序包执行dpkg -i安装然后gitlab-ctl reconfigure。但升级前一定要做两件事一是完整备份二是确认当前版本和目标版本之间的升级路径。查看当前版本sudo gitlab-rake gitlab:env:info输出里会显示版本号。然后对照官方文档的升级路径表确认下一步该升到哪个版本。5. 常见问题排查与性能调优5.1 服务起不来怎么办最常见的情况是重配置后某个组件一直处于down状态。排查顺序是这样的先看整体状态gitlab-ctl status找到挂掉的组件名然后单独看它的日志sudo gitlab-ctl tail 组件名比如postgresql起不来通常是内存不够或者数据目录权限不对。nginx起不来多半是端口被占用。redis起不来可能是之前的进程没杀干净。内存不足是最常见的原因。用free -h看剩余内存如果可用内存低于 1GB服务会很不稳定。解决办法是加内存或者调整配置减少并发。可以在配置文件里调低 worker 数量puma[worker_processes] 2 sidekiq[max_concurrency] 10改完重配置生效。5.2 推送代码报错排查推送代码时报错先看错误信息。如果是Connection refused说明 SSH 端口不对或者服务没监听。检查配置文件里的 SSH 端口和防火墙放行规则是否一致。如果是Permission denied (publickey)说明 SSH 密钥没配好。检查用户设置里的 SSH Keys 是否添加正确公钥内容有没有复制完整。如果是remote: HTTP Basic: Access denied说明密码或令牌不对。现在很多版本已经不支持密码推送需要用个人访问令牌。在用户设置里生成一个令牌推送时用令牌当密码。5.3 性能调优的几个关键参数默认配置适合小团队人数多了之后需要调优。几个关键参数参数默认值建议调整说明puma worker_processes自动内存/1GB每个 worker 约占 1GB 内存sidekiq max_concurrency2510-15并发太高会拖慢数据库postgresql shared_buffers256MB内存的 25%提升数据库缓存命中率nginx worker_processes自动CPU 核数匹配 CPU 核心数调整这些参数需要改/etc/gitlab/gitlab.rb然后重配置。每次只调一个参数观察效果后再调下一个不要一次性全改否则出问题不知道是哪个引起的。5.4 磁盘空间告警处理磁盘满了是另一个高频问题。先定位是哪个目录占了大头sudo du -sh /var/opt/gitlab/* | sort -rh | head -10通常占空间最多的是仓库数据、CI 产物和日志。日志可以配置轮转在配置文件里加上logging[logrotate_frequency] daily logging[logrotate_rotate] 30CI 产物可以设置过期时间在项目的 CI/CD 设置里配置。仓库数据如果确实很大可以考虑用对象存储来存但那属于进阶配置了。提示清理日志时不要直接rm用gitlab-ctl提供的命令或者 logrotate 来管理直接删文件可能导致服务句柄异常。6. 我踩过的坑与实操心得6.1 端口冲突导致安装后无法访问第一次装的时候服务器上已经跑了一个 Web 服务占了 80 端口。安装完访问不了浏览器一直转圈。排查了半天才发现是端口冲突。解决办法是在配置文件里把external_url改成别的端口比如http://ip:8080然后重配置。这个坑的教训是安装前先用ss -tlnp看一下端口占用情况把要用的端口提前规划好能省很多事。6.2 内存不足引发的“随机崩溃”有台测试机只有 2GB 内存装完之后服务时不时就挂。看日志发现是 OOM killer 把进程杀了。后来加了交换分区又调低了 worker 数量才勉强稳住。但说实话2GB 跑这个确实太勉强生产环境千万别省这点内存钱。6.3 备份恢复时忘记 secrets 文件有一次做恢复演练只恢复了数据备份忘了gitlab-secrets.json。结果恢复完所有 CI 变量都变成乱码合并请求的令牌也失效了。从那以后我的备份脚本里一定会把配置文件和 secrets 文件一起打包并且定期验证恢复流程。6.4 升级前没看升级路径有次想从 14.x 直接升到 16.x觉得版本差不多应该没事。结果数据库迁移到一半报错服务彻底起不来最后只能从备份恢复。后来老老实实按官方路径14.x - 15.0 - 15.4 - 15.11 - 16.0一步步来虽然麻烦但稳。6.5 一些提升体验的小技巧配置好之后可以做一些小优化提升日常使用体验。比如开启容器镜像仓库方便存 Docker 镜像配置 CI runner 时用 tag 区分不同环境的构建任务给常用项目设置默认分支保护规则防止误推。还有一个很实用的功能是 Web IDE在浏览器里就能改代码、提交合并请求对于临时改个配置或者修个文档特别方便不用本地 clone 再推。最后分享一个关于时区的小细节。如果日志时间和你本地时间对不上检查配置文件里的时区设置gitlab_rails[time_zone] Asia/Shanghai改完重配置日志时间就正常了。这个虽然是小问题但排查问题时时间对不上会很影响判断。