3、代码仓库搭建:自建GitLab服务器、仓库权限管理、Webhook与CI/CD触发

说到代码仓库,很多团队直接就用GitHub或Gitee了。但嵌入式开发有个特点——代码量大、二进制文件多、有时候还涉及硬件描述文件。我个人的习惯是,只要团队超过5个人,或者有保密需求,就果断自建GitLab。

为什么?说白了,自建GitLab给你的是完全的控制权。你想想看,嵌入式固件里经常包含芯片厂商的SDK,这些东西上传到公有仓库,心里总有点不踏实。我在一个车规级项目里就遇到过,客户审计时明确要求代码必须托管在内网服务器上。嗯,那时候幸好我们提前搭好了GitLab。

3.1 自建GitLab服务器的硬件与系统准备

先说说硬件。GitLab其实挺吃资源的,尤其是内存。我踩过这个坑——第一次用一台2核4G的虚拟机跑,结果跑个CI直接卡死。

团队规模 推荐配置 说明
1-10人 4核 / 8G内存 / 100G SSD 够用,CI并发建议≤2
10-50人 8核 / 16G内存 / 500G SSD 可以跑4-6个并发CI
50人以上 16核 / 32G内存 / 1T SSD 建议拆分Runner到独立机器

系统我推荐Ubuntu 20.04 LTS或22.04 LTS。CentOS虽然稳定,但GitLab官方对Ubuntu的支持更及时。安装过程其实很简单,官方提供了Omnibus一键安装包。

# 更新系统
sudo apt update && sudo apt upgrade -y

# 安装依赖
sudo apt install -y curl openssh-server ca-certificates

# 添加GitLab仓库并安装
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo EXTERNAL_URL="http://gitlab.yourcompany.com" apt install gitlab-ce

# 安装完成后重新配置
sudo gitlab-ctl reconfigure

这里有个小细节——EXTERNAL_URL一定要设对。我曾经因为写成了IP地址,后面改域名时折腾了半天。建议一开始就用域名,哪怕内网DNS解析也行。

我的经验:安装完成后,先跑一次 sudo gitlab-rake gitlab:check 检查所有组件状态。这一步能提前发现很多潜在问题,比如Redis没启动、PostgreSQL权限不对等。

3.2 仓库权限管理——别让所有人都能push主干

仓库搭好了,接下来就是权限管理。嵌入式项目里,我见过最乱的情况是——实习生不小心把未编译的中间文件push上去了,结果仓库体积暴涨到几个G。

GitLab的权限模型分五个级别:Guest、Reporter、Developer、Maintainer、Owner。我一般这样分配:

  • Guest:只读权限,适合给项目经理或测试看代码
  • Reporter:可以看代码、提Issue,但不能push
  • Developer:可以push到功能分支,但不能push到主干
  • Maintainer:可以合并MR、管理分支保护规则
  • Owner:完全控制,一般只给1-2个人

重点来了——分支保护。我建议至少保护maindevelop分支。设置方法很简单:

# 通过GitLab Web界面操作:
Settings -> Repository -> Protected Branches

# 或者用API设置
curl --request POST \
  --header "PRIVATE-TOKEN: your_token" \
  --data "name=main&push_access_level=40&merge_access_level=40" \
  "http://gitlab.yourcompany.com/api/v4/projects/1/protected_branches"

这里push_access_level=40表示只有Maintainer才能push。Developer只能通过Merge Request来合并代码。我曾经在一个项目中,因为没设这个保护,有人直接往main分支push了一个未测试的驱动,结果整条产线都停了。嗯,从那以后我再也不敢偷懒了。

注意:嵌入式项目经常有子模块(submodule)或者大文件(LFS)。记得在仓库设置里开启Git LFS支持,否则二进制文件会让仓库变得臃肿不堪。

3.3 Webhook与CI/CD触发——让代码提交自动干活

Webhook这东西,说白了就是一个回调。当仓库发生push、merge、tag等事件时,GitLab会向指定的URL发送一个HTTP POST请求。我一般用它来做两件事:触发CI流水线、通知协作平台(比如飞书或钉钉)。

先看一个最简单的Webhook配置——触发Jenkins构建:

# 在GitLab项目设置中:
Settings -> Webhooks -> Add new webhook

# URL填写Jenkins的触发地址
URL: http://jenkins.yourcompany.com/generic-webhook-trigger/invoke?token=my_token

# 勾选触发事件:
☑ Push events
☑ Merge request events
☑ Tag push events

# 点击Add webhook,然后测试一下
Test -> Push events

测试时如果返回200,说明通了。如果返回500,多半是Jenkins那边没配好。我遇到过最坑的一次是——Jenkins的CSRF保护没关,Webhook死活调不通。折腾了两小时才发现。

再说说GitLab自带的CI/CD。其实GitLab CI比Jenkins轻量多了,尤其适合嵌入式项目。你只需要在项目根目录放一个.gitlab-ci.yml文件:

stages:
  - build
  - test
  - deploy

build_firmware:
  stage: build
  image: embedded-toolchain:latest
  script:
    - make clean
    - make all
  artifacts:
    paths:
      - build/*.bin
      - build/*.hex
    expire_in: 1 week

run_unit_tests:
  stage: test
  script:
    - make test
  only:
    - merge_requests

deploy_to_production:
  stage: deploy
  script:
    - scp build/firmware.bin prod-server:/firmware/
  only:
    - tags
  when: manual

这个配置里我用了几个技巧:

  • artifacts:把编译产物保存下来,方便测试人员直接下载
  • only: merge_requests:单元测试只在MR时跑,节省资源
  • when: manual:部署到生产环境需要手动确认,防止误操作
核心思路:CI/CD不是为了自动化而自动化。嵌入式项目里,编译一次可能就要10分钟,如果每次push都全量编译,团队效率反而会下降。我建议按分支策略来——开发分支只做语法检查,MR时做全量编译和单元测试,打tag时做发布部署。

3.4 避坑指南——我踩过的那些坑

最后分享几个实战中遇到的坑,希望能帮你省点时间:

  • 磁盘空间爆炸:GitLab的CI日志和artifacts会占用大量磁盘。记得设置自动清理策略——Settings -> CI/CD -> Artifacts -> Expire artifacts,我一般设7天。
  • Runner注册失败:注册GitLab Runner时,记得用--executor "docker"参数。我一开始用shell executor,结果环境不一致,本地能编译通过,CI上就报错。
  • Webhook重复触发:如果你同时配了push和merge request事件,一个MR合并可能会触发两次构建。解决方案是在CI脚本里加一个CI_PIPELINE_SOURCE判断。
  • 大文件上传超时:嵌入式固件经常有几十MB的hex文件。GitLab默认的nginx上传大小限制是10MB,记得改一下:sudo gitlab-rake gitlab:env:set GITLAB_UPLOAD_MAX_SIZE=100M

嗯,差不多就这些。代码仓库是整个DevOps流程的基石,搭好了后面的事情就顺了。下一章我们会聊持续集成环境的搭建,到时候会用到今天配的这些基础设施。