
1. 项目概述与安装方案选型我最早开始折腾GitLab是在团队从SVN迁移到Git的时候。那个时候公司没有现成的代码托管平台GitHub私有仓库又受限于数量和成员数Gitee在企业内网部署又总觉得差点意思。试了一圈下来最后选了GitLab自托管。原因很简单它既能当代码仓库又能做CI/CD的入口还内置了Issue、Wiki、Code Review这些协作功能一个软件把开发流程的前半段全包了。这篇教程我会从零开始把GitLab的安装、初始化设置、日常使用、常见坑一次性讲透。不管你是个人开发者想在自己服务器上搭一个私有仓库还是小团队需要一套内网代码托管平台都可以照着往下走。Docker安装、Linux离线部署、SSH配置、Jenkins集成、CI/CD流程这些常见场景我都会覆盖到。在正式动手之前先花一分钟聊聊方案选型。GitLab的安装方式主流的有三种Omnibus包安装官方推荐的安装方式把Ruby、PostgreSQL、Redis、Nginx、Sidekiq等组件全部打包在一起一条命令装完。适合生产环境稳定性最好升级也方便。Docker容器部署一条docker run命令就能拉起一个实例适合快速验证、资源隔离、或者是个人开发机。我日常折腾环境最常用这种方式。源码安装从GitHub拉源码手动编译配置。除非你要二次开发GitLab本身否则不建议在生产环境使用太耗时间了。如果你问我的意见生产环境用Omnibus包本地测试直接上Docker千万别在生产环境走源码安装。这篇教程里我会重点讲Docker方式因为它是目前最省心的也最适合新手复现。后面会单独提一嘴Linux离线部署的思路给有内网环境要求的朋友作参考。2. 安装前的环境准备与硬件要求GitLab是个资源消耗大户这一点很多人第一次装的时候没预料到。官方给出的最低配置是4GB内存但说句实话4GB跑起来只能说是“能开机”一旦有几个人同时推送代码、跑Pipeline机器直接卡成PPT。我自己的实测经验是小型团队5~10人使用8GB内存是比较舒服的底线个人开发者自己用4GB勉强可以但建议给它单独分配至少2GB的swap空间。CPU方面2核起步4核更稳。磁盘的话系统盘建议至少留50GB以上空间因为你后面还要存Docker镜像、Git仓库数据、备份文件这些加起来很快就超过20GB了。操作系统我建议使用Ubuntu 20.04 LTS或22.04 LTSDebian 11/12也可以。CentOS 7已经停止维护除非你公司还在用老环境否则不建议新装。Windows的话虽然GitLab官方出了Windows版但跟Linux版完全不是一回事很多功能都不完整除非你真的没有Linux机器否则别碰。还有一个细节很多人会忽略确认服务器的80和443端口没有被占用。GitLab默认会在启动时绑定这两个端口如果Nginx或者别的Web服务已经占用了80端口启动会直接报错。如果你服务器上已经跑了一个Nginx要么先把服务停掉要么在配置里把GitLab的外部端口改掉。顺便提醒一下如果你打算用Docker方式安装先把Docker Engine装好。国内用户如果在拉镜像时遇到网络问题可以在/etc/docker/daemon.json里配置一个国内镜像加速器这个后面会细说。3. Docker方式安装GitLab完整实操3.1 拉取镜像与关键环境变量解析Docker安装GitLab其实就是一个命令的事但要把参数理解透了再动手。我先把完整的启动命令写出来然后逐项解释sudo docker run --detach \ --hostname gitlab.example.com \ --publish 443:443 \ --publish 80:80 \ --publish 22:22 \ --name gitlab \ --restart always \ --volume $GITLAB_HOME/config:/etc/gitlab \ --volume $GITLAB_HOME/logs:/var/log/gitlab \ --volume $GITLAB_HOME/data:/var/opt/gitlab \ --shm-size 256m \ gitlab/gitlab-ce:latest逐个拆开说--hostname这个参数决定了GitLab实例的访问域名。如果你没有域名可以暂时用localhost或者服务器的IP地址但强烈建议后面通过配置文件改成真实域名。这个参数会影响到你在GitLab页面上看到的克隆地址、CI/CD回调地址、Webhook地址如果一开始设置错了后面改起来要费一番功夫。--publish 22:22官方默认把22端口映射给SSH协议使用。这里有个大坑如果你宿主机本身还在用22端口做SSH登录那就会冲突。解决办法是改成--publish 2222:22把GitLab的SSH端口映射到宿主机的2222端口上。这个我在后面的“常见问题”里会详细讲。--volume $GITLAB_HOME/config:/etc/gitlab配置文件目录。GitLab的所有核心配置都放在这里包括gitlab.rb这个主配置文件。--volume $GITLAB_HOME/logs:/var/log/gitlab日志目录。排查问题的时候production.log、exceptions.log都在这里面。--volume $GITLAB_HOME/data:/var/opt/gitlab数据目录。所有的Git仓库、数据库文件、上传的附件都存在这里。这三个卷映射一定要做否则容器一删数据全没了。--shm-size 256m这是很多人会忽略的参数。GitLab内部用到了PostgreSQL默认的共享内存太小会导致数据库启动异常。如果是生产环境建议直接设成1g。我用的是latest标签也就是最新稳定版。如果你追求稳定可以锁定一个具体的版本号比如gitlab/gitlab-ce:17.0.0这样后期不会因为意外升级导致兼容性问题。3.2 容器启动与初始化等待命令执行完后容器会在后台启动。这里是第一次安装最容易焦虑的时候docker ps看到的还是starting状态需要等好几分钟才能访问页面。GitLab首次启动要做的事非常多包括初始化数据库、编译前端静态资源、启动Nginx和Sidekiq等。我用一个实际经验告诉你等待时间4核8GB内存的机器大约需要3~5分钟2核4GB的机器可能需要8分钟甚至更久你可以在服务器上执行sudo docker logs -f gitlab跟踪启动日志看到gitlab Reconfigured!字样时就说明服务已经起来了。我的建议是等待期间不要去频繁重启容器否则可能会触发数据库初始化异常。耐心等第一次启动慢一点是正常的。4. 初始化配置从浏览器登录到安全加固4.1 获取root密码并完成首次登录GitLab启动后浏览器访问你配置的hostname第一次会看到一个“change your password”页面。新版本的GitLab默认会在首次访问时要求你设置root用户的密码如果页面没有弹出说明系统自动生成了一组随机密码存储在容器内部的文件里。你需要进入容器查看sudo docker exec -it gitlab grep Password: /etc/gitlab/initial_root_password这个文件会在密码被修改后自动删除如果登录成功后你想找初始密码可能已经找不到了。登录账号是root密码就是你设置的初始密码。登录成功后第一件事建议到“Admin Area - Users”里创建一个自己的管理员账号之后日常操作尽量别用root。原因很简单root权限太大一旦在页面上误操作影响范围是整个实例。4.2 关闭注册功能与安全加固GitLab默认是允许任何人注册账号的。这对一个对外暴露的服务器来说非常危险因为任何人都可以注册账号、创建项目、甚至把你的服务器当成免费的代码托管站点用。我见过有人因为没有关闭注册服务器被塞满了垃圾项目垃圾项目里全是挖矿脚本和恶意代码。关闭路径Admin Area - Settings - General - Sign-up restrictions把“Sign-up enabled”取消勾选。如果你的团队需要从外部邀请成员用“邀请”功能单独发链接就行没必要放开公共注册。除此之外我还建议顺手做这几件事开启两步验证2FA在用户设置里给管理员账号强制开启。修改默认SSH端口如果你的宿主机22端口被占用已经把GitLab映射到了2222那克隆地址会自动带上端口没关系。配置域名和HTTPS证书如果你有域名建议配置好证书后再投入使用。GitLab在配置了HTTPS之后很多安全限制会自动开启比如Secure cookies、严格的CSP策略等。4.3 域名与克隆地址的正确配置方式关于域名配置这里有个非常高频的问题GitLab clone with HTTP怎么设置为域名而不是机器ID很多人在安装时把hostname写成了服务器的IP或者写成了localhost。等后面接上域名了发现页面上的克隆URL还是IP怎么改都改不过来。原因很简单安装时的hostname参数会被写进/etc/gitlab/gitlab.rb里的external_url配置。如果你一开始IP装错了后面要改正确的方式是修改external_url然后重新配置。在容器里执行sudo docker exec -it gitlab /bin/bash # 进入容器后编辑 /etc/gitlab/gitlab.rb # 找到 external_url改成你的域名地址 external_url http://gitlab.example.com # 保存后执行重新配置 gitlab-ctl reconfigure重新配置的时间大约是1~3分钟期间GitLab可能会短暂不可用属正常现象。改完之后页面上的克隆URL就会变成你设置的域名了。如果你想让克隆URL同时支持HTTP和HTTPS而且已经有了证书文件还可以继续在gitlab.rb里配置nginx[ssl_certificate]和nginx[ssl_certificate_key]这两个参数。不过这些不是必须的HTTP在内网环境下也够用。5. 日常使用的核心操作从SSH密钥到项目协作5.1 SSH密钥配置一次配置长久免密GitLab最常用的操作就是通过SSH协议拉取和推送代码。SSH的好处是一次配置密钥之后所有项目都能免密访问比每次输密码省心太多。第一步在本地生成密钥对。如果你已经生成过可以跳过ssh-keygen -t rsa -b 4096 -C your_emailexample.com生成的文件默认在~/.ssh/id_rsa和~/.ssh/id_rsa.pub。我一直用默认路径没有在这里装个性因为很多工具默认就从这个路径读取。第二步把公钥内容复制到剪贴板cat ~/.ssh/id_rsa.pub然后到GitLab页面上右上角头像 - Preferences - SSH Keys把公钥内容粘贴进去填上标题保存即可。这里有一个我想单独说的小技巧如果你有多台设备比如一台办公室电脑、一台家用笔记本、一台个人台式机每台设备都要生成各自的密钥并添加到同一个GitLab账号下因为GitLab是根据SSH Key来识别身份的不是根据设备名。5.2 同时配置GitHub和GitLab的多账号方案有开发者在网上问过这么一个问题本地Git客户端如何同时配置既能拉取GitHub项目又能拉取公司本地GitLab项目这个问题很典型。默认情况下~/.ssh/id_rsa只有一个而GitLab和GitHub都要求你自己的SSH密钥如果你用同一对密钥放在两个平台上GitHub会报“Key already in use”因为GitHub不允许同一个公钥绑定到不同账号实际上是可以的但如果你有两个不同的账号一个GitHub一个GitLab那就行。最干净的解决方案是为每个平台生成一对独立的密钥然后通过SSH config做分流。第一步生成两对密钥ssh-keygen -t rsa -b 4096 -C githubexample.com -f ~/.ssh/id_rsa_github ssh-keygen -t rsa -b 4096 -C gitlabexample.com -f ~/.ssh/id_rsa_gitlab第二步在~/.ssh/config里做分流Host github.com HostName github.com User git IdentityFile ~/.ssh/id_rsa_github Host gitlab.example.com HostName gitlab.example.com User git IdentityFile ~/.ssh/id_rsa_gitlab注意Host后面的名字决定了你克隆仓库时的域名。如果你的GitLab是通过gitlab.example.com访问的那配置里的Host就和它保持一致。如果你的GitLab通过IP访问Host也要写IP否则SSH不知道用哪个密钥去匹配。配置完后测试一下ssh -T gitgithub.com ssh -T gitgitlab.example.com能分别收到欢迎信息就算成功。还有一个比较隐蔽的问题如果你把GitLab的SSH端口改成了2222那在config文件里还需要加一行Port 2222否则SSH默认走22端口连通性测试会失败。5.3 创建项目、上传代码与页面文件操作在GitLab上创建项目很简单左上角“”号 - New project可以创建空项目也可以从GitHub导入还可以把你本地的仓库推上去。我一般推荐在页面上先创建空项目然后把本地代码推上去流程更直观。步骤顺序在GitLab新建项目项目名建议全小写用连字符分隔单词比如my-first-project。项目名会成为仓库路径的一部分后期改起来很麻烦。本地初始化仓库如果还没有git init git remote add origin gitgitlab.example.com:yourname/my-first-project.git git branch -M main git add . git commit -m Initial commit git push -u origin main如果你遇到推送失败、权限报错先检查你的SSH密钥是否添加成功或者直接用git remote set-url origin把远程地址改成HTTPS形式再试。另外有些朋友习惯直接在GitLab页面上传文件这个功能也支持进入项目仓库点击文件列表右上角的“”号选择“Upload file”或者直接在线编辑文件。在线编辑适合改文档、改配置文件这种小改动但如果涉及代码还是建议在本地开发环境修改后push上去这样版本历史更清晰。6. 与Jenkins集成及GitLab CI/CD的自动化部署6.1 Jenkins连接GitLab配置与login failed排查很多团队用GitLab存代码用Jenkins做持续集成。两个工具之间最常见的连接方式是在GitLab上创建一个Personal Access Token然后在Jenkins里配置。先说正确步骤在GitLab页面右上角头像 - Preferences - Access Tokens创建一个token。填写名称勾选API权限有效期按需设置即可。生成后马上复制保存因为token只会显示一次。然后在Jenkins里Manage Jenkins - Configure System - GitLab填上GitLab的URL和token测试连接。能显示success说明配置成功。我见过最多的报错是Login failed. Check API token or GitLab version. Log in via Git if the version...这个报错按经验来排查基本就三种原因Token权限不够创建token的时候没勾选API权限。重新建一个把API勾上就行了。网络不通Jenkins服务器访问不到GitLab地址。如果在同一个内网检查防火墙策略如果跨网段检查路由和代理配置。GitLab版本兼容性老版本GitLab的API格式和新版本Jenkins插件不兼容。解决办法是升级GitLab或者在Jenkins插件里调整API版本设置。还有一个小细节Jenkins配置GitLab时URL结尾不要加/。比如http://gitlab.example.com是正确的http://gitlab.example.com/有时候会因为多了一个斜杠导致测试连接失败。6.2 GitLab Runner与.gitlab-ci.yml的自动化实践除了JenkinsGitLab自己也有内置的CI/CD功能核心组件是GitLab Runner和一个.gitlab-ci.yml文件。我把这套流程的基本原理讲清楚你就能根据自己的项目灵活扩展。GitLab Runner是一个独立安装的应用程序它负责轮询GitLab服务器发现有Pipeline任务就拉取下来在Runner的机器上执行。Runner可以安装在与GitLab同一台机器上也可以安装在不同的服务器上。团队项目推荐用独立的Runner机器避免因为构建任务太多把GitLab主服务拖垮。安装Runner的常用方式还是Dockersudo docker run -d --name gitlab-runner --restart always \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest安装完成后需要把Runner注册到GitLab实例上。注册过程中会让你填GitLab URL和Registration Token这个Token在GitLab的Admin Area - CI/CD - Runners页面可以看到。注册命令sudo docker exec -it gitlab-runner gitlab-runner register注册完成后接下来就是写.gitlab-ci.yml文件。这是放在仓库根目录下的一个YAML格式文件GitLab会根据它来定义Pipeline的阶段、任务和依赖。一个最简单的自动化部署示例stages: - build - deploy build-job: stage: build script: - echo Building the project... - docker build -t my-app:latest . deploy-job: stage: deploy script: - echo Deploying the project... - docker push registry.example.com/my-app:latest - ssh server.example.com docker pull registry.example.com/my-app:latest docker restart my-app这个文件定义了两个阶段build构建镜像deploy推送镜像并远程重启容器。也就是说只要代码推送到仓库GitLab Runner会自动执行这套流程不需要人工干预。6.3 Docker镜像构建与自动化部署的完整链路说完Jenkins和Runner我再把整个自动化的链路串起来讲一遍这样你能理解每个环节的作用。典型的场景是你有一个Web服务代码存在GitLab上部署环境是一台独立的服务器。传统方式是把代码打包后手动上传到服务器再手动重启服务整个过程耗时且容易出错。使用GitLab CI/CD后流程变成了开发者在本地写代码推送到GitLab的指定分支比如main或developGitLab检测到推送事件创建一个Pipeline执行任务Runner拉取代码在构建阶段执行docker build生成镜像镜像被推送到仓库GitLab自带的Container Registry或第三方镜像仓库部署阶段通过SSH连接到目标服务器拉取新镜像停止旧容器启动新容器。这套流程的好处是全自动可追踪每次构建都有日志记录。而且每个阶段的执行状态在GitLab页面上都能直观看到哪一步失败了点进去就能看到详细日志排查问题非常方便。如果你第一次接触这套流程我建议先从一个最简单的echo任务跑通Pipeline确认Runner注册和.gitlab-ci.yml解析都没有问题后再逐步增加构建、测试、部署这些阶段。一次想全上容易遇到一堆连锁报错到时候排查起来反而让人崩溃。7. 没有.gitlab-ci.yml依然触发Runner的场景排查有一个问题特别有意思就是“没有gitlab yaml依然触发 runner 是否可行”。我先直接说结论如果仓库根目录下没有.gitlab-ci.yml文件正常情况下GitLab是不会创建Pipeline的。但实际工作中确实会遇到没有该文件、Runner却被反复触发的情况我用亲身踩坑经历告诉你这是为什么。原因通常是这两个仓库中存在其他位置或不同命名的CI配置文件。比如config/.gitlab-ci.yml、.gitlab-ci.yaml注意扩展名是yaml而不是yml只要在仓库的任意位置存在这种命名的文件GitLab就可能会识别到。解决办法是在仓库里全局搜索一下把这种文件删掉或改名。GitLab页面配置了Pipeline Schedules。有些项目在CI/CD - Schedules里设置了定时任务即使没有CI配置文件到点也会触发一个Pipeline任务Runner收到任务后执行会报配置文件缺失的错误。如果你不想被触发去把定时调度任务停掉就行。另外还有一种情况是Runner注册了多个项目有些项目正常有.gitlab-ci.yml有些项目没有。当你在Runner的机器上查看日志时可能会看到来自不同项目的任务交替执行误以为没有配置文件的项目也被触发了实际是别的项目触发的。遇到这种情况最稳妥的排查方式是在GitLab的项目页面 - CI/CD - Pipelines里看具体是哪一次的提交触发的Pipeline。点进去看会明确显示触发的来源是“Push”还是“Schedule”还是“API”。确定来源后再去对应的配置里处理比瞎猜快很多。8. 常见问题排查与运维心得8.1 GitLab高占用与卡顿优化GitLab被吐槽最多的一点就是吃内存太狠了。刚启动完1GB内存瞬间没了。如果你是小团队使用可以通过修改gitlab.rb来降低一些组件的资源占用。常用的优化配置# 减少数据库与后台进程的内存占用 postgresql[shared_buffers] 512MB puma[worker_processes] 2 sidekiq[max_concurrency] 5这些配置改完后执行gitlab-ctl reconfigure生效。需要注意的是配置改太小会影响性能比如Puma的worker进程数如果低于2Web页面可能就会变慢。建议按照团队规模来调整10人以下的团队上面的参数就够用。如果你用的是Docker方式部署还有一个优化点docker stats看一下容器实际占用的内存如果长期超过80%建议给宿主机加内存或者把Runner迁移到独立机器上别让构建任务和GitLab主进程抢资源。8.2 高危漏洞修复与版本升级思路GitLab因为功能复杂历史上确实暴露过一些高危漏洞。如果你用的版本比较老建议留意官方安全公告及时升级。升级前务必先做一次完整备份。这里我给一个我个人实践下来的安全升级流程在Admin Area - Settings - Backup里查看当前版本用gitlab-backup create命令生成完整备份确认备份文件存在于/var/opt/gitlab/backups目录后才进行下一步升级Docker镜像sudo docker pull gitlab/gitlab-ce:新版本然后重建容器启动后访问页面确认数据完整、Runner连接正常。在备份这一步我可以负责任地提醒升级前不备份等于拿生产数据做实验。一旦升级过程出现问题想回滚都没有退路。所以胆大心细先把备份做好。8.3 容器重启后数据丢失的防呆设计用Docker跑GitLab最怕的一件事就是你做完系统更新后执行docker rm清掉旧容器所有数据跟着一起没了。这个问题的根源就是启动容器时没有挂载数据卷。正确的方式是在启动GitLab容器之前先建好数据目录比如mkdir -p /srv/gitlab/config /srv/gitlab/logs /srv/gitlab/data然后启动命令里用--volume把这三个目录分别映射到容器内的对应路径。之后再重建容器只要把同样三个挂载参数带上数据都还在。如果你是老用户之前启动时没挂载数据目录现在容器还在运行状态那就先通过docker cp把容器内数据拷贝到宿主机目录然后重新用挂载方式启动容器。注意docker cp拷贝较大数据时会比较耗时过程中不要中断避免数据不完整。8.4 从GitHub同时拉取本地与远端仓库的配置细节这个问题的核心还是SSH配置和Git全局配置的配合。除了前面讲的SSH key分流之外还有一个容易踩的坑Git的user.name和user.email默认是全局配置如果你在GitHub上用的邮箱和在公司GitLab上用的邮箱不一样提交记录里的作者信息就会混乱。解决办法是在每个仓库的根目录下覆盖全局配置# 在这个项目目录下单独设置 git config user.name Your Name git config user.email privateexample.com当然如果你GitHub和GitLab用的是同一个邮箱那就没有这个问题。建议所有平台的Git账号信息尽量保持一致省得在多个项目之间切换时老是记错身份。还有一个实用的小技巧如果你频繁在多个项目之间切换而且每个项目都要改邮箱可以考虑在~/.gitconfig里用includeIf按路径匹配来设置不同的Git配置。比如公司项目统一放在~/work/目录下个人项目放在~/personal/下可以在全局配置里分别指定不同的用户信息。9. 服务器运维上的进一步建议GitLab作为一个常驻后台的服务日常运维有一些细节需要注意。整理几个我实际踩过的点定期重启一下GitLab实例。这不是我胡说GitLab跑久了之后Puma或Sidekiq偶尔会进入一种“半死不活”的状态页面能打开但操作特别卡。执行一次gitlab-ctl restart往往就恢复流畅了。时刻关注磁盘空间。GitLab的仓库数据是增量保存的一旦团队活跃度高仓库体积增长非常快。加上构建产物的存储比如Container Registry镜像磁盘告急是很常见的。建议用Cron定期清理日志和旧构建产物。可以查看/srv/gitlab/data的占用情况及时扩容。合理配置外部nginx反代。有些用户喜欢自己在宿主机上再套一层Nginx做域名转发和HTTPS终止。如果你这么做记得把GitLab自带的Nginx监听端口改掉避免端口冲突。配置方法是在gitlab.rb里找到nginx[listen_port]和nginx[enable]相关参数进行修改。关于离线部署。如果你所在的环境是纯内网没有外网访问条件GitLab的安装在思路上也很明确找一台能联网的机器下载GitLab的RPM或者DEB安装包以及所有依存的软件包拷贝到内网机器上安装。GitLab的依赖比较多手动逐个解决依赖确实麻烦建议直接用官方提供的离线仓库方式。Docker方式离线部署也一样先把gitlab/gitlab-ce镜像保存成tar文件在内网机器上docker load导入即可。10. 写在最后的经验分享做完这套操作你手里应该已经有一台能正常工作的GitLab服务器了能创建项目、能SSH推送代码、能跟Jenkins联动、能跑简单的CI/CD流程。我在实际使用GitLab的过程中最大的感受是这个软件的功能非常强大强大到一开始你会觉得无从下手。但一旦把核心链路跑通了它会成为团队效率的极大助力。很多操作比如分支保护、Merge Request审批、代码质量检查其实都是在这个基础之上慢慢加功能加出来的。你现在把地基打牢后面想扩展什么都很方便。最后再分享一个实用的小心得如果你是用Docker方式部署GitLab建议把启动命令写成一个docker-compose.yml文件而不是每次手动敲一长串docker run。把所有参数固化在文件里后期维护、文档分享都方便得多。我第一次部署完没有做这件事后来换服务器的时候重新查命令、翻文档浪费了不少时间。好的运维习惯就是从这种细节开始建立的。