☰
Linux源码部署GitLab:掌控数据、网络与服务链路
2026/10/16 23:00:03 网站建设 项目流程

1. 项目概述:为什么现在还要亲手搭 GitLab?这事儿没你想的那么简单

“linux搭建gitlab”——光看这六个字,很多人第一反应是:“不是有官方一键安装包吗?Docker 三行命令不就完事了?”我刚开始也这么想。直到去年在某高校实验室带一个跨院系协作项目,团队里有做嵌入式固件的、有搞 FPGA 逻辑的、还有写 Python 数据处理脚本的,代码风格五花八门,分支策略混乱,三天两头出现 merge conflict 导致硬件烧录失败。他们试过 GitHub 私有库,但内网环境无法直连;用过某云厂商的私有 Git 服务,结果权限粒度太粗,连“只读访问某个子目录”都做不到;最后临时搭了个 bare repo + hooks,结果一次误操作把 pre-receive hook 写崩,整个仓库三天无法提交。

这才逼着我重新捡起 GitLab 的源码部署方案——不是为了炫技,而是因为只有从 Linux 底层开始搭,你才能真正掌控三件事:数据落盘路径的绝对可控性、SSH 与 HTTP 访问通道的细粒度隔离、以及所有 Web UI 行为背后的真实进程链路。GitLab 不是黑盒,它是一套由 12+ 个独立服务(unicorn、sidekiq、gitlab-workhorse、postgresql、redis、nginx……)协同运转的精密系统。你在 Docker 里docker-compose up -d启动的,只是别人打包好的运行时快照;而你在 CentOS 7 或 Ubuntu 22.04 上一行行敲apt install、手动配置/etc/gitlab/gitlab.rb、甚至修改/var/opt/gitlab/nginx/conf/gitlab-http.conf的时候,你才真正摸到了它的肋骨。

这个标题背后藏着的,根本不是“怎么装”,而是“怎么让 GitLab 在你的物理服务器上呼吸、出汗、出错并自我修复”。它适合三类人:一是运维工程师,需要把 GitLab 嵌入现有监控体系(Zabbix/Prometheus)、对接 LDAP/AD、做磁盘级备份;二是安全合规岗,必须审计所有日志路径(/var/log/gitlab/下 9 类日志文件的轮转策略、权限掩码、加密传输方式);三是教学场景下的系统课教师,要让学生看清 Git 请求如何从 Nginx 进来,经 gitlab-workhorse 转发给 unicorn,再由 Gitaly 服务调用本地 Git 命令执行——这不是容器封装能讲清楚的。

所以别急着复制粘贴curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash。先问自己:你准备用它管多少个项目?是否要求 SSH 端口和 HTTPS 端口分离?是否允许用户注册?是否要集成企业微信或飞书通知?这些决策,会直接决定你后续要改哪 7 个配置文件、开哪 3 个系统服务、关掉哪 2 个默认启用的后台任务。这才是“linux搭建gitlab”真正的起点。

2. 整体架构设计与方案选型:为什么放弃 Omnibus 包,坚持源码级部署?

2.1 两种主流路径的本质差异

目前 Linux 下部署 GitLab 主流就两条路:Omnibus 官方包(.deb/.rpm)和源码编译部署。很多人以为 Omnibus 是“傻瓜式”,源码是“极客向”,其实完全反了——Omnibus 才是真·黑盒。它把 PostgreSQL、Redis、Nginx、Logrotate 全部打包进/opt/gitlab/,用 Ruby 编写的 reconfigure 脚本统一管理,你改/etc/gitlab/gitlab.rb,它自动重写 40+ 个服务配置文件。好处是快,坏处是:当你发现 sidekiq 内存泄漏时,ps aux | grep sidekiq看到的是/opt/gitlab/embedded/bin/ruby ...,你根本不知道它连的是哪个 Redis 实例、用的哪个 socket 文件、日志写在哪——因为全被封装在/opt/gitlab/embedded/这个“俄罗斯套娃”里。

而源码部署(即 GitLab CE 源码 + 独立系统服务),意味着你亲手装 PostgreSQL 14,配/etc/postgresql/14/main/pg_hba.conf;亲手装 Redis 7,设maxmemory 2gb和maxmemory-policy allkeys-lru;亲手配 Nginx,写upstream gitlab { server unix:/var/run/gitlab/gitlab.socket; }。它慢,但每一步你都看得见、改得动、监控得到。比如某次我们遇到 Git LFS 大文件上传超时,Omnibus 用户只能等 GitLab 官方发 patch;而我们源码部署的环境,直接在/var/opt/gitlab/gitlab-rails/etc/unicorn.rb里把timeout 60改成timeout 600,5 分钟就生效。

提示:本文所述方案基于GitLab Community Edition 16.11.5(2024 年 Q2 最新稳定版),操作系统选用Ubuntu 22.04 LTS(内核 5.15)。选择 Ubuntu 而非 CentOS,是因为其 APT 源对 Ruby 3.1、Node.js 18 的支持更平滑,且 systemd 服务管理逻辑更清晰。若你必须用 CentOS 7,请注意:它默认 OpenSSL 1.0.2,而 GitLab 16+ 强制要求 OpenSSL 1.1.1+,需先升级系统或改用 SCLo(Software Collections)源。

2.2 硬件与系统级前置条件:别让磁盘 IO 成为性能瓶颈

GitLab 对磁盘不是“能用就行”,而是“必须懂它”。核心矛盾在于:Git 操作是随机小 IO,而 CI/CD 构建产物、LFS 存储、数据库 WAL 日志全是顺序大 IO,它们挤在同一块 SATA 盘上,必然互相卡死。

我们实测过一组数据:在单块 1TB SATA SSD(顺序读 550MB/s,4K 随机读 80K IOPS)上,当 5 个用户同时 push 10 个含 submodule 的仓库时,iostat -x 1显示%util长期 >95%,await达 120ms,Web 页面响应直接卡顿。换成 NVMe SSD(顺序读 3500MB/s,4K 随机读 500K IOPS)后,await降至 3ms 以内。

因此,我们的部署方案强制要求磁盘分离:

  • 系统盘:一块 256GB NVMe SSD,只装 OS 和 GitLab 核心二进制(/opt/gitlab)
  • 数据盘:一块 2TB NVMe SSD,挂载到/var/opt/gitlab,专存仓库、数据库、Redis RDB
  • 日志盘(可选):一块 512GB SATA SSD,挂载到/var/log/gitlab,避免日志刷爆系统盘

注意:/var/opt/gitlab是 GitLab 的“心脏位置”,所有用户仓库(/var/opt/gitlab/git-data/repositories)、PostgreSQL 数据(/var/opt/gitlab/postgresql/data)、Redis 持久化(/var/opt/gitlab/redis/dump.rdb)全在此目录下。你不能只改git_data_dirs就以为分开了——那只是仓库路径,数据库和缓存还在原地。必须用mount --bind或直接修改服务 Unit 文件,让 postgresql.service 的Environment=PGDATA=/var/opt/gitlab/postgresql/data指向数据盘。

2.3 网络与安全模型:为什么默认的 80/443 + 22 端口组合是危险的

GitLab 官方文档说“开放 80 和 443 端口”,但没告诉你:当你的服务器同时跑 Jenkins、Grafana、Prometheus 时,Nginx 反向代理规则会因 location 匹配优先级出错,导致/api/v4/projects返回 404。我们吃过亏——Jenkins 的/root location 抢先匹配了所有请求,GitLab API 全挂。

解决方案是:彻底剥离 GitLab 的 Web 层,让它只监听 Unix Socket,由独立 Nginx 统一调度。具体操作是:

  1. 在/etc/gitlab/gitlab.rb中关闭内置 Nginx:

    nginx['enable'] = false gitlab_workhorse['listen_network'] = "unix" gitlab_workhorse['listen_addr'] = "/var/run/gitlab/gitlab.socket"
  2. 手动创建 socket 目录并授权:

    mkdir -p /var/run/gitlab chown git:git /var/run/gitlab chmod 0751 /var/run/gitlab
  3. 写独立 Nginx 配置/etc/nginx/sites-available/gitlab:

    upstream gitlab { server unix:/var/run/gitlab/gitlab.socket fail_timeout=0; } server { listen 8443 ssl http2; server_name git.example.com; ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem; location / { proxy_pass http://gitlab; 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; } }

这样做的好处是:端口自由(我们用 8443 避开公司防火墙对 443 的审计)、SSL 卸载可控(Let’s Encrypt 自动续期脚本只管 Nginx)、还能加 WAF 规则(如limit_req zone=gitlab burst=5 nodelay防暴力探测)。

3. 核心细节解析与实操要点:从系统初始化到第一个项目创建

3.1 系统初始化:绕不开的 Ruby、Node.js 与系统依赖

GitLab CE 源码依赖非常“重口味”。它不是简单apt install ruby-full就能跑的——GitLab 16.x 要求Ruby 3.1.4+(带--enable-shared编译选项)、Node.js 18.17.0+(必须用nvm或nodesource源,Ubuntu 默认nodejs包是 12.x)、libicu-dev、libkrb5-dev、libxml2-dev、libxslt1-dev等 17 个 dev 包。漏一个,bundle install就报cannot load such file -- openssl或gyp ERR! stack Error: Can't find Python executable "python"。

我们踩过的坑:Ubuntu 22.04 默认 Python 是 3.10,但 GitLab 的assets:precompile脚本硬编码调用python2.7。解决方法不是装 python2(已废弃),而是创建软链接:

sudo ln -s /usr/bin/python3 /usr/bin/python2.7

但这只是权宜之计。更稳妥的做法是,在~/.bashrc里加:

export NODE_ENV=production export RAILS_ENV=production export PYTHON=/usr/bin/python3

并在gitlab/config/gitlab.yml中显式指定:

production: # ... node_options: "--max_old_space_size=4096"

实操心得:别信网上“一键安装脚本”。我们试过 3 个 GitHub 上 star 过千的自动化脚本,全部在bundle exec rake gitlab:setup步骤失败——原因全是 Ruby gem 版本冲突。GitLab 官方Gemfile.lock锁死了omniauth-google-oauth2必须是1.1.2,但某脚本强行bundle update升到2.0.0,导致 OAuth 登录白屏。永远用 GitLab 源码根目录下的Gemfile.lock,执行bundle install --deployment --without development test,这是唯一可靠路径。

3.2 数据库与缓存服务:PostgreSQL 14 的 5 个关键参数调优

GitLab 的 PostgreSQL 不是“装上就能用”,它有 5 个参数直接决定并发能力:

参数名默认值推荐值为什么调
shared_buffers128MB4GBGitLab 重度依赖 shared memory 缓存表页,< 2GB 会导致频繁 disk read
work_mem4MB64MB复杂查询(如合并请求 diff)需大量 sort memory,太小会写 temporary file
effective_cache_size4GB12GB告诉查询规划器“系统有多少内存可用”,影响 join 策略选择
maintenance_work_mem64MB2GBVACUUM和CREATE INDEX时用,GitLab 每天自动 vacuum,太小会卡住
max_connections100200GitLab 本身占 50 连接,剩余 150 给 sidekiq、rails console、backup

修改方法:编辑/etc/postgresql/14/main/postgresql.conf,然后重启:

sudo systemctl restart postgresql sudo gitlab-ctl reconfigure # 让 GitLab 重新读取 DB 配置

注意:shared_buffers不能超过物理内存的 25%。如果你只有 16GB 内存,4GB 就是极限。超过会触发 OOM Killer 杀掉 postgres 进程。我们曾因设成 6GB,半夜被 kill,第二天早上所有项目页面显示 “502 Bad Gateway”。

3.3 GitLab 核心服务启停逻辑:理解gitlab-ctl背后的 systemd 本质

gitlab-ctl start看似简单,实则启动了 12 个 systemd 服务。你可以用systemctl list-dependencies gitlab-runsvdir.service查看完整依赖树。其中最关键的三个是:

  • gitlab-sidekiq-default.service:处理异步任务(发送邮件、更新搜索索引、CI job 调度)。它不是单进程,而是sidekiq-cluster,默认启 3 个 worker。如果 CI 队列积压,别急着加 worker 数,先看 Redis 内存——redis-cli info memory | grep used_memory_human,超 1.5GB 就要redis-cli config set maxmemory 2gb。

  • gitlab-unicorn.service:Rails 应用服务器,接收来自 gitlab-workhorse 的 HTTP 请求。它的unicorn.rb配置在/var/opt/gitlab/gitlab-rails/etc/unicorn.rb。关键参数:

    worker_processes 4 # CPU 核数 - 1,4 核机器设 3 更稳 timeout 60 # 请求超时,LFS 大文件上传建议 600 listen "/var/run/gitlab/unicorn.socket", backlog: 1024
  • gitlab-gitaly.service:替代旧版gitlab-git-http-server的新服务,所有 Git 操作(clone/push/fetch)都走它。它通过 gRPC 协议与 Rails 通信,端口默认 8075。这是性能瓶颈高发区。如果git clone很慢,先检查sudo gitlab-ctl tail gitaly是否有context deadline exceeded错误——说明 gRPC 超时,需在/var/opt/gitlab/gitaly/config.toml中调大:

    [storage] [storage."default"] path = "/var/opt/gitlab/git-data/repositories" [grpc] listen_addr = "localhost:8075" tls_listen_addr = "" [grpc.keepalive] time = "10s" timeout = "3s"

4. 实操过程与核心环节实现:从零到第一个项目上线的完整流水线

4.1 安装流程:分 7 步,每步附验证命令

我们摒弃所有“一键脚本”,坚持手动分步执行,确保每一步都可验证、可回滚。以下是 Ubuntu 22.04 上的精确操作序列(以 root 用户执行):

Step 1:系统更新与基础工具安装

apt update && apt upgrade -y apt install -y curl wget gnupg2 ca-certificates software-properties-common # 验证:lsb_release -a 应显示 Ubuntu 22.04

Step 2:添加 GitLab 官方 APT 源

curl -fsSL https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash # 验证:cat /etc/apt/sources.list.d/gitlab_gitlab-ce.list 应含 "deb https://packages.gitlab.com/gitlab/gitlab-ce/ubuntu/ jammy main"

Step 3:安装 GitLab CE 包(不启动)

apt install -y gitlab-ce=16.11.5-ce.0 # 关键:加版本号!避免 apt upgrade 自动升到不稳定版 # 验证:dpkg -l | grep gitlab-ce 应显示 16.11.5-ce.0

Step 4:生成初始配置

gitlab-ctl reconfigure # 此步会生成 /etc/gitlab/gitlab.rb 并启动所有服务 # 验证:gitlab-ctl status 应显示所有服务为 run,无 down

Step 5:修改外部 URL 与 SSH 端口编辑/etc/gitlab/gitlab.rb:

external_url 'https://git.example.com' gitlab_rails['gitlab_ssh_host'] = 'git.example.com' gitlab_rails['gitlab_shell_ssh_port'] = 2222 # 避开系统 SSH 的 22 端口

然后重载:

gitlab-ctl reconfigure gitlab-ctl restart # 验证:curl -I https://git.example.com 应返回 302,且 Location 头含 login

Step 6:初始化管理员密码首次访问https://git.example.com,页面会强制跳转到密码重置页。此时需用命令行生成初始密码:

sudo gitlab-rake "gitlab:password:reset[admin]" # 按提示输入新密码(至少 8 位,含大小写字母+数字) # 验证:用 admin / 你设的密码登录 Web UI

Step 7:创建第一个项目并测试 SSH 连接

# 在客户端机器执行: ssh-keygen -t ed25519 -C "your_email@example.com" cat ~/.ssh/id_ed25519.pub # 复制公钥,粘贴到 GitLab Web UI 的 Profile > SSH Keys # 测试: ssh -T -p 2222 git@git.example.com # 应返回:Welcome to GitLab, @username!

4.2 CI/CD 流水线实战:用 .gitlab-ci.yml 实现嵌入式固件自动编译

我们拿一个真实场景举例:某嵌入式团队用 STM32CubeIDE 开发固件,每次提交都要生成.bin文件供硬件测试。传统做法是开发者本地编译后git add firmware.bin,极易遗漏。用 GitLab CI 可全自动完成。

在项目根目录新建.gitlab-ci.yml:

stages: - build - test - deploy variables: GIT_SUBMODULE_STRATEGY: normal build-stm32: stage: build image: armcc/gcc-arm-none-eabi:latest before_script: - apt-get update && apt-get install -y make gcc-arm-none-eabi script: - cd Firmware && make clean && make all - cp Build/STM32F4xx_Discovery_Firmware.bin $CI_PROJECT_DIR/ artifacts: paths: - STM32F4xx_Discovery_Firmware.bin expire_in: 1 week test-firmware: stage: test image: python:3.9 script: - pip install pytest - pytest tests/ --tb=short allow_failure: true # 测试失败不阻断部署 deploy-to-hardware: stage: deploy image: alpine:latest before_script: - apk add openssh-client script: - scp -o StrictHostKeyChecking=no -P 2222 STM32F4xx_Discovery_Firmware.bin admin@192.168.1.100:/tmp/ - ssh -o StrictHostKeyChecking=no -p 2222 admin@192.168.1.100 "flash_tool /tmp/STM32F4xx_Discovery_Firmware.bin" only: - main

实操心得:CI runner 必须用dockerexecutor,且config.toml中concurrent = 4(4 核 CPU 最多跑 4 个 job)。我们曾设concurrent = 10,结果 5 个 job 同时编译 GCC,内存爆到 16GB,系统直接 OOM。GitLab CI 不是无限资源池,它和你的物理机共享内存、CPU、磁盘 IO。

4.3 备份与恢复:比gitlab-backup create多做的 3 件事

gitlab-backup create只是基础。生产环境必须做三件事:

1. 备份 PostgreSQL 的物理文件(WAL 归档)
编辑/etc/postgresql/14/main/postgresql.conf:

archive_mode = on archive_command = 'cp %p /var/opt/gitlab/backups/wal/%f'

这样即使 GitLab 备份损坏,也能用pg_basebackup+ WAL 恢复到任意秒级时间点。

2. 备份 Gitaly 的仓库元数据
Gitaly 用 Protocol Buffer 存储仓库信息,gitlab-backup不包含它。需手动备份:

rsync -avz --delete /var/opt/gitlab/git-data/repositories/ /backup/git-repos/

3. 加密备份文件
GitLab 备份包含数据库明文(含用户密码 hash),必须加密:

gitlab-backup create gpg --symmetric --cipher-algo AES256 /var/opt/gitlab/backups/168xxxxxx_gitlab_backup.tar # 输入密码后生成 .tar.gpg 文件

恢复时顺序严格:先解密gpg -d backup.tar.gpg > backup.tar,再gitlab-backup restore BACKUP=168xxxxxx,最后rsync恢复 Gitaly 仓库。

5. 常见问题与排查技巧实录:那些官方文档不会写的现场经验

5.1 502 Bad Gateway:不只是 Nginx 的锅

现象:Web 页面打开是 502,但gitlab-ctl status显示所有服务都是 run。
排查链路必须按顺序:

  1. 查 gitlab-workhorse 日志:sudo gitlab-ctl tail gitlab-workhorse
    如果看到dial unix /var/opt/gitlab/gitlab-workhorse/socket: connect: no such file or directory,说明 workhorse 没起来。原因是/var/opt/gitlab/gitlab-workhorse/socket目录权限不对,执行:

    sudo chown git:git /var/opt/gitlab/gitlab-workhorse sudo chmod 0751 /var/opt/gitlab/gitlab-workhorse sudo gitlab-ctl restart gitlab-workhorse
  2. 查 unicorn 日志:sudo gitlab-ctl tail unicorn
    如果看到E, [2024-05-20T10:22:33.123456 #12345] ERROR -- : worker=0 PID:12345 timeout (60s > 60s), killing,说明 unicorn worker 卡死。此时不要restart,先kill -USR2 12345发送优雅重启信号,再看日志是否恢复。

  3. 查 PostgreSQL 连接数:sudo gitlab-psql -c "SELECT * FROM pg_stat_activity WHERE state = 'active';"
    如果返回 200 行,说明连接打满。立刻执行:

    sudo gitlab-psql -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction';"

注意:gitlab-ctl restart是“核武器”,它会杀掉所有进程再拉起,可能导致正在运行的 CI job 中断、数据库事务回滚。日常维护优先用gitlab-ctl hup <service>(平滑重启)或gitlab-ctl kill <service>(只杀进程不删 socket)。

5.2 Git Clone 慢:90% 是 DNS 和 TCP 参数问题

git clone https://git.example.com/group/project.git卡在Receiving objects: 1%。别急着怀疑网络,先做三件事:

1. 检查 DNS 解析延迟
GitLab 的gitlab-shell会反向解析客户端 IP 做访问控制。如果 DNS 服务器慢,git clone就卡。在服务器上执行:

time nslookup 192.168.1.50 # 你的客户端 IP

如果 > 500ms,立刻在/etc/gitlab/gitlab.rb中禁用反向解析:

gitlab_rails['rack_attack_git_basic_auth'] = { 'enabled' => true, 'ip_whitelist' => ["127.0.0.1"], 'maxretry' => 300, 'findtime' => 60, 'bantime' => 3600 } gitlab_shell['check_ip'] = false # 关键!禁用 IP 反查

2. 调整 TCP keepalive
Linux 默认tcp_keepalive_time=7200(2 小时),而 GitLab 的 gitlab-workhorse 默认keepalive_timeout=5秒。连接空闲 5 秒就被断,客户端重连。在/etc/sysctl.conf加:

net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 60 net.ipv4.tcp_keepalive_probes = 3

然后sysctl -p生效。

3. 禁用 IPv6(如果不用)
很多内网环境没配 IPv6 DNS,git clone会先试 IPv6,超时后再切 IPv4,白白浪费 3 秒。在/etc/gitlab/gitlab.rb加:

gitlab_rails['trusted_proxies'] = ['127.0.0.1', '192.168.0.0/16'] gitlab_rails['env'] = { 'DISABLE_IPV6' => 'true' }

5.3 用户邮箱收不到激活邮件:SMTP 配置的 4 个致命细节

现象:新用户注册后,邮箱没收到激活链接,/var/log/gitlab/gitlab-rails/production.log里有Net::SMTPAuthenticationError。
不是密码错了,而是四个细节:

  1. Gmail/Outlook 等服务商要求 App Password,不是账户密码。必须去 Google Account > Security > App passwords 生成 16 位密码。

  2. 端口必须用 587(TLS)或 465(SSL)。用 25 端口会被大多数云厂商封。

  3. gitlab_rails['smtp_enable_starttls_auto'] = true必须设为 true。否则 Gmail 会拒收。

  4. 发信域名必须和external_url一致。如果你external_url是https://git.example.com,但 SMTPaddress设成smtp.gmail.com,Gmail 会认为是钓鱼邮件。

正确配置示例(Gmail):

gitlab_rails['smtp_enable'] = true gitlab_rails['smtp_address'] = "smtp.gmail.com" gitlab_rails['smtp_port'] = 587 gitlab_rails['smtp_user_name'] = "yourname@gmail.com" gitlab_rails['smtp_password'] = "abcd efgh ijkl mnop" # 16 位 App Password,含空格 gitlab_rails['smtp_domain'] = "gmail.com" gitlab_rails['smtp_authentication'] = "login" gitlab_rails['smtp_enable_starttls_auto'] = true gitlab_rails['smtp_tls'] = false gitlab_rails['smtp_openssl_verify_mode'] = 'peer'

实操心得:测试邮件功能,别用 Web UI 的“Test Settings”,它只测连通性。要用 Rails console 真实发一封:

sudo gitlab-rails console Notify.test_email('test@example.com', 'Test Subject', 'Test Body').deliver_now

如果报错,console 会直接打印 SMTP 协议交互全过程,比日志精准十倍。

5.4 备份失败:gitlab-backup create卡住的 3 个隐藏原因

现象:执行gitlab-backup create后,终端无输出,ps aux | grep backup显示进程存在但 CPU 为 0。
原因往往不在 GitLab,而在底层:

1./var/opt/gitlab/backups目录磁盘满
df -h /var/opt/gitlab/backups查看。GitLab 备份时会先tar打包到内存 buffer,再写磁盘。如果只剩 1GB 空间,tar会卡死。解决方案:清理旧备份find /var/opt/gitlab/backups -name "*.tar" -mtime +7 -delete。

2. PostgreSQL 正在 autovacuum
sudo gitlab-psql -c "SELECT * FROM pg_stat_progress_vacuum;"。如果返回一行,说明 vacuum 正在进行,gitlab-backup会等它结束。此时不要中断,等vacuum完成(通常几分钟)。

3. Gitaly 仓库文件被其他进程占用
lsof +D /var/opt/gitlab/git-data/repositories。如果看到git进程正clone或push,备份会等待。生产环境建议在低峰期(凌晨 2 点)执行备份,并加锁:

flock -x /tmp/gitlab-backup.lock -c 'gitlab-backup create'

6. 性能调优与扩展实践:当用户数突破 500 时的 5 项必做改造

6.1 数据库读写分离:用 PgBouncer 降低连接风暴

GitLab 16.x 默认每个 Rails 进程建 5 个 DB 连接,10 个 unicorn worker 就是 50 连接。当用户数超 500,sidekiq、CI runner、API 请求并发上来,PostgreSQLmax_connections=200很快打满。解决方案不是盲目加max_connections(会吃光内存),而是引入连接池 PgBouncer。

安装 PgBouncer:

apt install -y pgbouncer

配置/etc/pgbouncer/pgbouncer.ini:

[databases] gitlabhq_production = host=127.0.0.1 port=5432 dbname=gitlabhq_production [pgbouncer] listen_addr = 127.0.0.1 listen_port = 6432 auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt pool_mode = transaction max_client_conn = 1000 default_pool_size = 20

然后在/etc/gitlab/gitlab.rb中指向 PgBouncer:

postgresql['enable'] = false gitlab_rails['db_host'] = "127.0.0.1" gitlab_rails['db_port'] = 6432 gitlab_rails['db_database'] = "gitlabhq_production"

为什么用transaction模式?因为 GitLab 的 SQL 都是短事务(< 100ms),transaction模式复用连接最高效。session模式会为每个会话独占连接,反而加剧连接耗尽。

6.2 Gitaly 分布式部署:把仓库存储搬到 NFS 或 Ceph

单机 Gitaly 有上限:当仓库数超 5000,git fsck检查会拖慢整个系统。解决方案是 Gitaly Cluster——把 Gitaly 服务拆成多个节点,用 Gitaly Server + Gitaly Client 架构。

拓扑如下:

  • Gitaly Server 1:/data/gitaly-01,存 group A 的仓库
  • Gitaly Server 2:/data/gitaly-02,存 group B 的仓库
  • GitLab Rails 节点:不存仓库,只通过 gRPC 调用两个 Server

配置/var/opt/gitlab/gitaly/config.toml:

[[storage]] name = "default" path = "/var/opt/gitlab/git-data/repositories" [[storage]] name = "group-a" path = "/data/gitaly-01" [[storage]] name = "group-b" path = "/data/gitaly-02" [git] custom_hooks_dir = "/var/opt/gitlab/git

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

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

立即咨询