GitLab私有仓库这件事我先说个结论小团队和个人开发者直接上Docker版社区版比什么都省心。我自己在几台不同配置的服务器上来回折腾过裸机安装和容器部署也经历过磁盘被仓库撑爆、容器内存告警、IDE连不上GitLab这类离谱问题。这篇就把从零搭建GitLab私有仓库的完整过程和踩坑记录写下来包含部署选型、初始化配置、备份恢复、性能调优、CI接入以及高频报错的处理方式。准备在自己服务器上落地GitLab的可以直接照着做。1. 动手之前先想清楚这四件事1.1 部署方式Docker还是裸机GitLab官方提供了两种主流部署路径一个是Ubuntu/Debian/CentOS上的裸机安装一个是Docker容器。我个人的建议非常直接没有特殊合规要求优先Docker。原因是裸机安装最大的痛点是依赖问题。GitLab是Ruby、Go、PostgreSQL、Redis、Nginx一堆组件糅在一起的大型单体服务系统库版本稍微不对安装脚本就会在中途翻脸。而Docker镜像里已经把这些依赖全部锁好了你只要挂好数据目录容器删了重建数据还在升级就是换tag再启动一次回退也方便。有人说Docker性能损耗实际上GitLab这种服务本身就是IO密集和内存密集容器和裸机在绝大多数小规模场景下感受不到差别。真正决定体验的是磁盘类型和内存大小不是容器还是裸机。1.2 硬件资源内存和磁盘到底要多少这里我直接把话说满2GB内存不要尝试跑GitLab跑起来也会在页面初始化阶段频繁502纯粹浪费时间。4GB内存是勉强能跑的底线但需要做后文的内存调优否则系统随时OOM。个人使用或三五人小团队8GB内存是比较舒服的起步配置。磁盘方面镜像本身占几个GB数据卷会随着仓库数量快速增长。50GB是比较合理的起步容量如果你准备长期用建议直接挂独立数据盘把GitLab数据目录规划到这个盘上。另外务必要留出至少20%的余量因为GitLab内部的Git对象、临时文件、备份文件都会抢磁盘磁盘满的时候不只是仓库推不上去整个服务都可能崩溃。1.3 域名、端口与反向代理GitLab有个关键概念叫external_url它直接决定你克隆代码时看到的地址。很多新手在这里翻车是因为服务器上一个端口被多个服务占用尤其22端口被系统自带的SSH服务占用后Docker映射端口冲突于是随便映射成一个随机端口结果克隆地址变成ssh://githost:39871/group/project.git怎么看怎么别扭。我的建议是如果只是内网使用给GitLab分配一个内网域名并做好端口规划。SSH端口如果宿主机已经占用就采用2200这类高位端口映射容器内22并通过gitlab.rb里把gitlab_shell_ssh_port也改成2200确保生成的克隆地址是正确的。域名这块有就配没有也可以直接用IP访问先把服务跑通最重要。1.4 版本选择CE还是EE新版本的系统要求GitLab社区版CE和企业版EE在核心的代码托管、MR、Issue、CI这些功能上没有本质差异不需要License就能长期使用的就是CE。个人和小团队老老实实选CE。版本选择上新手最容易犯的一个错是拉latest标签。latest会跟随官方持续更新今天部署没问题过段时间容器重建就跳到新的大版本配置格式可能变化甚至宿主机内核都跟不上了。GitLab新版本对操作系统的要求也在不断提高比如目前较新的GitLab 19系列明确要求Ubuntu 24.04及以上老系统上裸机安装大概率会失败。我建议在Docker Hub上锁定一个当前稳定版的tag比如gitlab/gitlab-ce:17.x.x这种格式至少不会半夜给你惊喜。2. 我用Docker搭建GitLab的完整过程2.1 目录规划与容器启动命令先看目录规划这一步做不好后面全是坑。GitLab容器里数据分布在三个位置/etc/gitlab存放主配置gitlab.rb/var/log/gitlab存放日志/var/opt/gitlab存放实际数据。三者必须全部挂载出来缺一个都不行。我自己习惯在宿主机建一个统一目录比如/srv/gitlabmkdir -p /srv/gitlab/{config,logs,data}然后启动容器。这里给一个我实测过能稳定运行的docker run命令docker run --detach \ --hostname gitlab.example.com \ --publish 80:80 \ --publish 443:443 \ --publish 2200:22 \ --name gitlab \ --restart always \ --volume /srv/gitlab/config:/etc/gitlab \ --volume /srv/gitlab/logs:/var/log/gitlab \ --volume /srv/gitlab/data:/var/opt/gitlab \ --shm-size 256m \ gitlab/gitlab-ce:17.2.1-ce.0注意几点--hostname要和external_url保持一致否则生成的克隆地址会带错误的域名--shm-size 256m是GitLab官方推荐的默认的64MB共享内存可能会导致某些操作崩溃2200:22的做法是为了避开宿主机SSH端口冲突。如果你习惯用docker-compose等效配置是这样version: 3.6 services: gitlab: image: gitlab/gitlab-ce:17.2.1-ce.0 container_name: gitlab restart: always hostname: gitlab.example.com environment: GITLAB_OMNIBUS_CONFIG: | external_url http://gitlab.example.com gitlab_rails[gitlab_shell_ssh_port] 2200 ports: - 80:80 - 443:443 - 2200:22 volumes: - /srv/gitlab/config:/etc/gitlab - /srv/gitlab/logs:/var/log/gitlab - /srv/gitlab/data:/var/opt/gitlab shm_size: 256m启动后第一次访问页面大概率是502别慌容器内部在做初始化等3到5分钟再刷新。2.2 初始化后的第一件小事改root密码GitLab启动完成后浏览器访问http://你的服务器IP会跳转到登录页。GitLab的初始root密码不会在页面上显示而是写进了容器的配置文件里。Docker部署方式可以这样拿初始密码docker exec gitlab cat /etc/gitlab/initial_root_password这个文件会在首次配置完成后的24小时内自动删除所以拿到后第一件事就是登录后台把root密码改掉顺手在管理后台开启两步验证。这里还要提一句很多人在登录后看到Your account is pending approval from your GitLab administrator这类提示一脸懵。这不是你密码错了而是管理员在后台开启了“新用户注册需要审批”。刚部署完如果你自己是用root注册的管理员账号去Admin Area - Settings - Sign-up Restrictions里检查如果不需要开放注册直接关闭Sign-up enabled顺便把Require admin approval for new sign-ups关掉不然你给同事建了账号对方也登录不了。2.3 配置SSH密钥解决clone地址不对的老问题GitLab的仓库地址默认是SSH协议把公钥配置好是拉取代码的基础操作。配置流程分三步。第一步在本地生成密钥对现在推荐用ed25519算法ssh-keygen -t ed25519 -C youexample.com一路回车即可生成的公钥默认在~/.ssh/id_ed25519.pub。第二步把公钥内容复制到GitLab后台。路径是右上角头像 -Preferences偏好设置 - 左侧SSH Keys把id_ed25519.pub里的内容整个粘进去起个能认出来的标题保存。第三步验证能否正常拉取。正常情况下SSH协议的clone地址是git你的域名:group/project.git。如果你按照上文把SSH端口映射成了宿主机的2200GitLab生成的克隆地址里会带上2200端口如果你不想每次都在地址里带端口可以在本地的SSH配置文件中指定端口。在~/.ssh/config里加一段Host gitlab.example.com HostName gitlab.example.com Port 2200 User git这样后续的clone地址就清爽多了也不用每个项目都改URL。拉取代码用git clone gitgitlab.example.com:group/project.git系统会自动走2200端口。2.4 关闭注册与用户审批如果这是公司或团队内部私有仓库开放注册等于把代码库大门敞开。默认GitLab是允许任何人注册的部署完必须去后台关掉。路径是Admin Area - Settings - General - Sign-up restrictions取消勾选Sign-up enabled。这样登录页的“注册”入口就没有了。新增成员的方式就变成两种管理员创建用户或通过邀请链接添加。我建议用邀请链接因为被邀请的人自己可以设密码、填姓名信息准确性高管理员只需要在Members里发起邀请。这一步是安全底线省不得。私有仓库的核心就是“私有”这两个字注册入口不关后面做再多安全策略都是白搭。3. 日常运维三板斧备份、恢复、迁移3.1 定时备份的正确姿势GitLab提供了一键备份命令容器部署方式这样执行新版GitLab已经统一为gitlab-backupdocker exec -t gitlab gitlab-backup create老版本用的是gitlab-rake gitlab:backup:create如果你用的是较新的镜像直接用前者就行。备份产物会生成在容器内/var/opt/gitlab/backups/目录下也就是我们挂载的/srv/gitlab/data/backups/里一个以时间戳命名的tar包。但这里有个绝大部分教程不会告诉你的坑这个tar包只包含GitLab的数据不包含/etc/gitlab/gitlab.rb和/etc/gitlab/gitlab-secrets.json这两个文件。特别是gitlab-secrets.json里面存了数据库加密密钥、2FA密钥、CI/CD密钥恢复时没有它新机器上所有仓库密钥都会对不上。所以我的备份策略是双管齐下应用备份每天凌晨执行gitlab-backup create保留最近7天。配置备份把/srv/gitlab/config整个目录同步到备份机或对象存储。定时任务可以直接写在宿主机crontab里0 2 * * * docker exec -t gitlab gitlab-backup create find /srv/gitlab/data/backups -name *.tar -mtime 7 -delete备份文件不要和生产数据放在同一个磁盘这是我被坑过一次换来的教训磁盘满的时候备份和业务一起死那就全完了。3.2 换机器迁移与项目导入迁移场景很常见比如换服务器或者从测试环境搬到生产环境。整体迁移思路是新机器装好同样版本的GitLab把备份tar包拷贝过去然后恢复。恢复前需要先停掉两个服务不然数据库和Git对象文件可能还在写入docker exec -it gitlab gitlab-ctl stop puma docker exec -it gitlab gitlab-ctl stop sidekiq docker exec -it gitlab gitlab-backup restore恢复过程会要求确认输入yes即可。恢复完成后重启容器docker exec -it gitlab gitlab-ctl restart docker restart gitlab如果只是想把某个外部项目导入到私有仓库不涉及整体迁移GitLab也提供了Import Project功能。路径是New project - Import project - Git Repository URL填入裸仓库地址、GitHub地址或任何可访问的Git仓库URL即可。这对之前把代码放在第三方平台、现在想收回来的团队特别适用。我实际对比过两种方式项目数量少、只要代码历史用URL导入最轻巧项目多、还想保留Issue、MR、成员权限关系老老实实做整体备份恢复否则后面补配置会补到怀疑人生。3.3 安全修补版本升级的流程GitLab历史上出过不少高危漏洞其中包括远程代码执行类漏洞以及Log4j相关的组件漏洞。修复这类安全问题的核心动作只有一个升级到官方修复版本没有别的捷径。升级流程我总结为三步。第一步先备份。无论小版本还是大版本升级前不备份就是拿生产环境开玩笑。第二步确定升级路径。GitLab官方规定大版本升级不能跨版本跳比如从16.x升到17.x中间不能直接跨多个大版本。小版本连续升级一般没问题但如果你的版本已经很老需要按照官方升级路径逐级走。第三步Docker方式升级非常简单。拉取新版本镜像用原有的挂载目录重新创建容器docker pull gitlab/gitlab-ce:17.x.x-ce.0 docker stop gitlab docker rm gitlab docker run ... # 启动参数与之前保持一致容器启动后会自动执行数据库迁移等必要步骤耐心等待页面恢复即可。升级时还要注意版本与操作系统兼容性现在已经出现了新版本GitLab不支持老版Ubuntu的情况所以Docker部署的优势在升级场景体现得最明显——宿主系统的库版本不影响GitLab运行。4. 磁盘与内存告急时怎么抢救4.1 pack文件太大仓库瘦身你在服务器上可能会看到类似gitlab-pack-fb5fe7dfac8e953d5cc65d26074f72d5fa961d98.pack这样的文件体积几个GB甚至几十GB。这个pack文件是Git仓库的打包对象文件正常情况下会随着仓库历史增长但如果有人把大二进制文件直接提交到了仓库这个文件就会迅速膨胀。排查仓库体积可以拎出一个仓库统计一下对象大小git count-objects -vH看size-pack字段如果达到几百MB甚至GB级别基本可以确定有大文件混在了历史中。处理的第一步是禁止手动删除pack文件这是死命令。直接删pack文件会让Git对象库损坏仓库直接报废。正确的做法是重写历史去掉误提交的大文件然后到GitLab后台执行仓库清理。常见工具是git filter-repo执行类似这样的清理把历史中的大文件过滤掉git filter-repo --path bigfile.zip --invert-paths然后强制推送覆盖远程分支git push --force --all git push --force --tags推完之后管理员在项目页面的Settings - Repository - Repository Cleanup里点击清理GitLab会在服务端执行GC把不再被引用的对象移除磁盘空间才会真正释放。日常预防比事后清理重要得多。团队规范里明确禁止提交构建产物、依赖包、安装包大文件要走Git LFS或者对象存储。这个规范不建立pack文件膨胀只是时间问题。4.2 Docker容器内存占用过高的排查与调优GitLab默认配置对资源非常贪婪这是它有太多内部组件导致的Nginx、PostgreSQL、Redis、Puma、Sidekiq、Gitaly、Prometheus、Grafana、Mattermost一个都不少。先看当前占用docker stats gitlab内存占用动辄几个GB是很正常的。如果你只有8GB内存优化空间主要在这几项。修改/srv/gitlab/config/gitlab.rb在文件末尾追加prometheus_monitoring[enable] false puma[worker_processes] 2 puma[min_threads] 2 puma[max_threads] 4 sidekiq[max_concurrency] 5 postgresql[shared_buffers] 128MB然后执行docker exec gitlab gitlab-ctl reconfigurereconfigure会按新配置重建服务进程。实测在8GB内存的机器上优化后GitLab整体内存占用能从5到6GB降到3GB以内4GB内存的小机器也能勉强稳定运行。如果是个人学习环境还可以关掉一些不用的组件比如Mattermost但PostgreSQL、Redis、Gitaly这三个核心组件不建议动关掉它们轻则功能异常重则数据损坏。4.3 顺手把CI/CD跑起来私有仓库的另一大价值是自带CI/CD不用把流水线放在第三方平台。GitLab CI需要先注册Runner。在需要执行构建的机器上装好gitlab-runner然后注册gitlab-runner register \ --url http://gitlab.example.com \ --token 你的注册token \ --executor docker \ --docker-image alpine:latest注册token在项目页面Settings - CI/CD - Runners。完成后项目里添加.gitlab-ci.yml就能触发流水线比如这样一个最简配置stages: - build - test build-job: stage: build script: - echo build project - make build test-job: stage: test script: - echo run tests - make test推送到仓库后在CI/CD - Pipelines页面就能看到流水线执行情况。如果你的团队还用到KubeSphere、KubeKey这类Kubernetes部署工具GitLab自带的Container Registry也可以当私有镜像仓库用把构建好的镜像或下载的离线包推到内部Registry统一管理整个交付链路都在内部闭环了。5. 高频报错速查与避坑清单5.1 登录、Token、权限相关报错这里集中说两个几乎每个用GitLab的人都会碰到的报错。第一个是IDE插件报错login failed. check api token or gitlab version. log in via git if the version...。看到这个提示大概率是JetBrains全家桶或VS Code的GitLab插件在连接时使用的Personal Access Token失效了或者GitLab版本和插件要求的API版本不匹配。解决办法是在GitLab后台生成新的Personal Access Token范围勾上api、read_repository、write_repository同时确认有效期不是空的。如果插件支持“Log in via Git”的方式认证优先用这个因为它走的是OAuth流程不依赖手工维护的Token省心很多。第二个报错是Your account is pending approval from your GitLab administrator。这种情况通常是管理员在后台开启了新用户注册审批机制。解决方式分两种身份普通用户去找管理员点批准如果你是管理员去Admin Area - Settings - Sign-up Restrictions关闭Require admin approval for new sign-ups。5.2 其他常见问题速查表现象常见原因处理方式首次访问页面502容器还在初始化等待3到5分钟docker exec gitlab gitlab-ctl status查看服务状态页面能打开SSH拉不了代码SSH端口映射不一致或密钥未配置确认gitlab_shell_ssh_port的值本地SSH config加端口push代码报HTTP 413Nginx对上传体积限制过小gitlab.rb里调整nginx[client_max_body_size]并reconfigure磁盘空间持续上涨备份文件堆积或pack文件膨胀清理旧备份重写仓库历史并执行仓库清理服务器重启后容器没起来容器启动策略未设置docker update --restart always gitlab页面频繁502或503内存不足、Puma或Sidekiq配置过高缩减worker数关掉Prometheus等非必要组件5.3 还有几条实在经验从最开始部署GitLab到现在我最大的感受是不要一开始就追求全功能。先把HTTP跑通、备份做好、成员管理理顺这三个地基打牢之后再去折腾HTTPS、CI Runner、镜像仓库这些进阶功能。gitlab.rb这个配置文件每次修改前先复制一份备份改完必须执行gitlab-ctl reconfigure才会生效。这个习惯帮我避免了好几次配置改坏无法回滚的局面。版本锁定用固定tag而不是latest这句话我每次都要强调。Docker部署GitLab最怕的不是配置复杂而是某个凌晨容器重建后直接跳到不兼容的新版本。如果想做仓库代码量和注释率统计GitLab没有直接给出现成页面但可以自己拉代码下来统计一条命令就能看个大概git ls-files *.py *.java *.js | xargs wc -l想按提交者统计代码量用git log --prettyformat:%an | sort | uniq -c这些数据虽然粗糙但做个周报绰绰有余。GitLab私有仓库的价值不在于功能多炫而在于代码和数据真正握在自己手里把备份和权限管好这套系统能安安稳稳用很多年。