前面已经部署了 SonarQube,用来做代码质量检测。SonarQube 单独使用也可以,但它和 GitLab 配合起来价值更大:
GitLab 负责代码托管、Merge Request、CI/CD
SonarQube 负责代码质量扫描、漏洞检查、质量门禁
GitLab CI 在提交或合并时触发 sonar-scanner
扫描结果回传 SonarQube,再决定是否允许继续发布
这篇文章记录在 Kubernetes 集群里部署 GitLab 的路线。当前集群已有:
Kubernetes 三主节点
Longhorn 存储
ingress-nginx
MetalLB
cert-manager
CloudNativePG
SonarQube
计划使用的访问域名:
https://gitlab.jihw.top
https://registry.jihw.top
其中:
gitlab.jihw.top GitLab Web / Git over HTTP
registry.jihw.top GitLab Container Registry
GitLab 适合放在 K8s 里吗
可以放,但要先知道它比 SonarQube 重很多。
GitLab 不只是一个 Web 服务,它包含:
WebService / Rails
Sidekiq 后台任务
Gitaly Git 仓库存储服务
Toolbox 备份和维护工具
Container Registry
KAS / GitLab Agent Server
PostgreSQL
Redis / Valkey
对象存储
GitLab Runner
所以 GitLab 的 Kubernetes 部署更像一个平台,而不是一个普通 Deployment。
如果只是个人或很小的团队,最省心的方式其实是单独一台虚拟机安装 GitLab Omnibus。
如果已经有稳定的 Kubernetes、Ingress、证书、存储、备份体系,再用 Helm Chart 部署 GitLab 才比较合适。
和 SonarQube 如何配合
GitLab 和 SonarQube 通常这样配合:
1. 开发者 push 代码到 GitLab
2. GitLab CI 触发流水线
3. 流水线执行构建、测试、sonar-scanner
4. 扫描结果上传到 SonarQube
5. SonarQube 计算 Quality Gate
6. GitLab Merge Request 里展示检查结果
GitLab 侧需要:
项目仓库
.gitlab-ci.yml
CI/CD Variables
Runner
SonarQube 侧需要:
项目
Project Token
Quality Gate
GitLab ALM Integration
先部署 GitLab,后面再把 SonarQube 接进去。
部署方式选择
GitLab 官方提供 Helm Chart:
helm repo add gitlab https://charts.gitlab.io/
helm repo update
helm search repo gitlab/gitlab
需要注意:GitLab Helm Chart 近几年变化比较大。GitLab 19.0 起,官方更推荐 Gateway API;同时 PostgreSQL、Redis、对象存储这类依赖不应该再依赖 chart 内置组件做长期运行。
当前集群已经有 ingress-nginx,所以本文选择:
关闭 GitLab chart 自带的 Ingress Controller
关闭 GitLab chart 内置 cert-manager
沿用现有 ingress-nginx
沿用现有 cert-manager
使用 Longhorn 做持久化存储
外部 PostgreSQL / Redis / 对象存储按长期部署规划
这比“一条 helm install 装全部”麻烦一些,但边界更清楚,也更容易备份和维护。
资源建议
GitLab 很吃资源。学习环境建议至少:
CPU: 4 core 起步
Memory: 8Gi 起步
Disk: 100Gi 起步
长期使用建议:
CPU: 8 core+
Memory: 16Gi+
Disk: 按仓库、镜像、构建产物容量规划
如果节点资源不够,GitLab 会表现为:
Pod Pending
Pod OOMKilled
Web 页面很慢
Sidekiq 堆积
Git push / clone 超时
当前不考虑 GitLab 高可用,只准备放在一台专用 worker 上。推荐这台虚拟机配置:
最低能跑:4 vCPU / 8GB RAM / 200GB SSD
推荐配置:8 vCPU / 16GB RAM / 500GB SSD
如果 Runner 也跑在这台机器上:8 vCPU+ / 24GB~32GB RAM / 500GB~1TB SSD
我更推荐直接给:
8 vCPU
16GB RAM
500GB SSD
这样 GitLab 本体、Gitaly、Registry 和少量后台任务会舒服很多。GitLab Runner 后续最好单独规划,避免构建任务和 GitLab 本体抢 CPU、内存和磁盘 IO。
单独 worker 节点方案
这次目标是不做 GitLab 高可用,只让 GitLab 跑在一台专用 worker 节点上。
整体步骤:
1. 准备一台新虚拟机
2. 安装 containerd、kubeadm、kubelet
3. 加入当前 Kubernetes 集群
4. 给节点打 label
5. GitLab values 里用 nodeSelector 固定调度
6. 使用 longhorn-retain 保存数据
准备虚拟机
建议系统:
Ubuntu Server 22.04 LTS 或 24.04 LTS
建议磁盘:
/var/lib/containerd 镜像和容器数据
/var/lib/kubelet Pod 挂载目录
/var/lib/longhorn Longhorn 磁盘目录
如果只有一块 500GB 系统盘,也可以先全部放在同一块盘上。后续如果仓库和镜像增长很快,再给 Longhorn 单独加数据盘。
基础设置:
swapoff -a
sed -i '/ swap / s/^/#/' /etc/fstab
cat <<'EOF' >/etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
cat <<'EOF' >/etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables=1
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-ip6tables=1
EOF
sysctl --system
安装 containerd、kubeadm、kubelet 的步骤要和现有集群版本保持一致。可以先在当前集群查看版本:
kubectl get nodes
kubectl version
加入 Kubernetes 集群
在任意控制平面节点上生成 join 命令:
kubeadm token create --print-join-command
它会输出类似:
kubeadm join 192.168.3.217:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
在新 worker 节点上执行这条 join 命令。
回到控制平面确认:
kubectl get nodes -o wide
这里实际加入的 GitLab worker 是 192.168.3.218,Kubernetes 节点名是:
k8s-worker1
给节点打 label
回到 master 节点,或者任意一台已经配置好 kubeconfig 的机器,给这台 worker 打专用标签:
kubectl label node k8s-worker1 workload=gitlab --overwrite
确认:
kubectl get nodes --show-labels | grep gitlab
后面 GitLab values 里会使用:
global:
nodeSelector:
workload: gitlab
这样 GitLab chart 里的主要组件都会被调度到这个节点。
暂时不要加 taint
如果想让这台节点只跑 GitLab,理论上可以加 taint:
kubectl taint node k8s-worker1 dedicated=gitlab:NoSchedule
但第一次部署不建议马上加。原因是 GitLab chart 组件很多,外部依赖也多,如果 toleration 没有覆盖到所有 Pod,容易出现部分组件一直 Pending。
更稳的做法:
1. 先只加 label
2. 用 global.nodeSelector 固定 GitLab 到这台节点
3. 部署成功并观察稳定
4. 确认所有 GitLab 组件 tolerations 配置后,再考虑 taint
当前目标只是让 GitLab 跑在单独 worker 上,nodeSelector 已经够用。
创建命名空间
kubectl create namespace gitlab
准备 StorageClass
GitLab 里最重要的数据是 Git 仓库,也就是 Gitaly 的数据卷。它不应该使用 Delete 回收策略。
前面部署 SonarQube 时已经创建过:
longhorn-retain
如果还没有,可以创建:
cat <<'EOF' | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-retain
provisioner: driver.longhorn.io
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: Immediate
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "30"
fsType: ext4
EOF
确认:
kubectl get storageclass
外部依赖规划
GitLab 长期运行建议把这些依赖拆出来:
PostgreSQL GitLab 元数据、用户、权限、CI 状态
Redis/Valkey 缓存、队列、会话
对象存储 artifacts、LFS、uploads、packages、registry、backup
本文先把部署路线写清楚。实际生产时建议:
PostgreSQL 使用 CloudNativePG
Redis/Valkey 使用独立 Helm Chart 或专门的 Redis/Valkey 集群
对象存储 使用 MinIO、Garage、Ceph RGW 或云厂商 S3
不要让 GitLab、业务系统、SonarQube 共用同一个 database 和 user。最低要求是独立 database、独立 user;更好的方式是独立 PostgreSQL Cluster。
PostgreSQL 准备思路
如果用 CloudNativePG,可以单独给 GitLab 创建 PostgreSQL:
cat <<'EOF' | kubectl apply -f -
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: gitlab-postgresql
namespace: gitlab
spec:
instances: 1
storage:
size: 50Gi
storageClass: longhorn-retain
bootstrap:
initdb:
database: gitlabhq_production
owner: gitlab
EOF
等待 Ready:
kubectl -n gitlab get cluster
kubectl -n gitlab get pods -l cnpg.io/cluster=gitlab-postgresql -o wide
kubectl -n gitlab get secret | grep gitlab-postgresql
GitLab 连接地址类似:
gitlab-postgresql-rw.gitlab.svc.cluster.local:5432
注意:GitLab 新版本对数据库拆分、CI database、连接池等有更多配置项。正式部署前要按当前 chart 版本的官方文档确认 PostgreSQL 配置。
Redis / Valkey 准备思路
Redis 或 Valkey 用来支撑缓存、队列和会话。
可以单独部署一个 Redis:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm upgrade --install gitlab-redis bitnami/redis \
-n gitlab \
--set architecture=standalone \
--set auth.enabled=true \
--set auth.password='change-this-redis-password' \
--set master.persistence.enabled=true \
--set master.persistence.storageClass=longhorn-retain \
--set master.persistence.size=10Gi
长期环境不要把密码直接写在命令里,应该使用 Secret 或 values 文件管理。
Redis 地址类似:
gitlab-redis-master.gitlab.svc.cluster.local:6379
对象存储准备思路
GitLab 有很多文件类数据:
CI artifacts
LFS 对象
用户上传文件
Packages
Container Registry
Terraform state
备份文件
这些更适合放对象存储,而不是全部压在 Pod 本地卷里。
可以使用:
MinIO
Garage
Ceph RGW
阿里云 OSS / AWS S3 / 其他 S3 兼容存储
对象存储至少要规划这些 bucket:
gitlab-artifacts
gitlab-lfs
gitlab-uploads
gitlab-packages
gitlab-registry
gitlab-backups
gitlab-tmp
GitLab Chart 的对象存储配置比较长,建议单独维护 gitlab-object-storage.yaml,不要把 access key 写进博客或 Git 仓库。
准备 GitLab root 密码
先创建初始 root 密码 Secret:
kubectl -n gitlab create secret generic gitlab-root-password \
--from-literal=password='change-this-root-password'
后续登录:
username: root
password: 上面 Secret 里的 password
准备 values.yaml
创建 gitlab-values.yaml。
下面是这次实际安装使用的基础骨架。当前复用 new-api 命名空间里已有的 PostgreSQL 和 Redis,所以 GitLab 只单独创建自己的数据库、数据库密码 Secret、root 密码 Secret。
因为当前还没有独立对象存储,先临时启用 chart 内置 MinIO,并使用 Longhorn 持久化。Registry 暂时关闭,等后面对象存储规划好后再打开。
global:
edition: ce
nodeSelector:
workload: gitlab
hosts:
domain: jihw.top
https: true
gitlab:
name: gitlab.jihw.top
registry:
name: registry.jihw.top
ingress:
enabled: true
class: nginx
configureCertmanager: false
annotations:
cert-manager.io/cluster-issuer: letsencrypt-alidns-prod
nginx.ingress.kubernetes.io/proxy-body-size: "0"
tls:
enabled: true
initialRootPassword:
secret: gitlab-root-password
key: password
psql:
host: newapi-postgres-rw.new-api.svc.cluster.local
port: 5432
database: gitlabhq_production
username: gitlab
password:
secret: gitlab-postgresql
key: password
redis:
host: redis-master-proxy.new-api.svc.cluster.local
port: 6379
auth:
enabled: true
secret: gitlab-redis
key: redis-password
minio:
enabled: true
kas:
enabled: false
upgradeCheck:
enabled: false
gatewayApi:
enabled: false
nginx-ingress:
enabled: false
installCertmanager: false
certmanager:
install: false
prometheus:
install: false
gitlab-runner:
install: false
postgresql:
install: false
redis:
install: false
minio:
persistence:
storageClass: longhorn-retain
size: 20Gi
registry:
enabled: false
gitlab:
kas:
enabled: false
minReplicas: 1
maxReplicas: 1
gitlab-shell:
minReplicas: 1
maxReplicas: 1
gitlab-pages:
enabled: false
webservice:
minReplicas: 1
maxReplicas: 1
workerTimeout: 120
resources:
requests:
cpu: 300m
memory: 1Gi
sidekiq:
minReplicas: 1
maxReplicas: 1
resources:
requests:
cpu: 200m
memory: 768Mi
gitaly:
persistence:
storageClass: longhorn-retain
size: 20Gi
resources:
requests:
cpu: 200m
memory: 512Mi
toolbox:
replicas: 1
几个重点:
global.edition=ce 使用 Community Edition
global.nodeSelector 固定 GitLab 组件到专用 worker 节点
gatewayApi.enabled=false 沿用现有 ingress-nginx
nginx-ingress.enabled=false 不安装 chart 自带 Ingress Controller
installCertmanager=false 不安装 chart 自带 cert-manager
postgresql.install=false 使用 new-api 命名空间已有的 PostgreSQL
redis.install=false 使用 new-api 命名空间已有的 Redis
global.minio.enabled=true 临时启用 chart 内置 MinIO 作为对象存储
registry.enabled=false 暂时不启用 Container Registry
global.kas.enabled=false 暂时不启用 KAS,降低单 worker 资源压力
gitaly.persistence 使用 longhorn-retain,保护 Git 仓库数据
cert-manager.io/cluster-issuer 要写成当前集群真实存在的 ClusterIssuer:
kubectl get clusterissuer
当前集群可用的是:
letsencrypt-alidns-prod
letsencrypt-alidns-staging
安装 GitLab
helm upgrade --install gitlab gitlab/gitlab \
-n gitlab \
-f gitlab-values.yaml
GitLab 组件很多,第一次启动会比较久:
kubectl -n gitlab get pods -w
确认 GitLab Pod 是否都调度到了专用 worker:
kubectl -n gitlab get pods -o wide
正常情况下,GitLab 相关 Pod 的 NODE 应该都是:
k8s-worker1
如果有 Pod 没有调度到这个节点,检查:
kubectl get node k8s-worker1 --show-labels
kubectl -n gitlab describe pod <pod-name>
重点看是否有:
node(s) didn't match Pod's node affinity/selector
Insufficient cpu
Insufficient memory
pod has unbound immediate PersistentVolumeClaims
查看 Ingress:
kubectl -n gitlab get ingress
kubectl -n gitlab get certificate
查看 PVC:
kubectl -n gitlab get pvc
kubectl get pv | grep gitlab
配置 DNS
Ingress/MetalLB 当前入口地址是:
192.168.3.230
DNS 需要解析:
gitlab.jihw.top -> 192.168.3.230
registry.jihw.top -> 192.168.3.230
测试:
curl -I https://gitlab.jihw.top
curl -I https://registry.jihw.top/v2/
Container Registry 未登录时返回 401 Unauthorized 是正常的,说明入口已经通了。
首次登录
打开:
https://gitlab.jihw.top
账号:
username: root
password: gitlab-root-password Secret 中的 password
查看密码:
kubectl -n gitlab get secret gitlab-root-password \
-o jsonpath='{.data.password}' | base64 -d
首次登录后建议马上:
1. 修改 root 密码
2. 创建自己的管理员账号
3. 关闭公开注册
4. 配置 SMTP 邮件
5. 配置备份策略
关闭公开注册:
Admin Area -> Settings -> General -> Sign-up restrictions
安装 GitLab Runner
GitLab 本身负责代码托管和 CI 管理,真正跑流水线的是 Runner。
可以单独部署 Runner:
helm repo add gitlab https://charts.gitlab.io/
helm repo update
helm upgrade --install gitlab-runner gitlab/gitlab-runner \
-n gitlab \
--set gitlabUrl=https://gitlab.jihw.top \
--set runnerToken='你的 Runner Token'
Runner Token 在 GitLab 页面获取:
Admin Area -> CI/CD -> Runners
或者项目级:
Project -> Settings -> CI/CD -> Runners
长期使用建议给 Runner 单独写 gitlab-runner-values.yaml,限制并发、资源和可用命名空间。
接入 SonarQube
GitLab 部署好后,可以在项目里加 .gitlab-ci.yml。
示例:
stages:
- test
sonarqube:
stage: test
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner
-Dsonar.projectKey="${CI_PROJECT_PATH_SLUG}"
-Dsonar.projectName="${CI_PROJECT_PATH}"
-Dsonar.sources=.
-Dsonar.host.url="${SONAR_HOST_URL}"
-Dsonar.token="${SONAR_TOKEN}"
在 GitLab 项目里配置 CI/CD Variables:
SONAR_HOST_URL=https://sonarqube.jihw.top
SONAR_TOKEN=SonarQube 项目 Token
如果 SonarQube 和 GitLab 都在同一个内网 Kubernetes 集群里,也可以用内网地址:
SONAR_HOST_URL=http://sonarqube-sonarqube.sonarqube.svc.cluster.local:9000
但对人类查看和回调集成来说,建议仍然配置 SonarQube 的公网或内网 HTTPS 域名。
常用排查
Pod 没启动
kubectl -n gitlab get pods -o wide
kubectl -n gitlab describe pod <pod-name>
kubectl -n gitlab logs <pod-name>
重点看:
资源不足
PVC 未 Bound
镜像拉取失败
PostgreSQL 连接失败
Redis 连接失败
对象存储配置错误
证书未签发
如果使用专用 worker,还要检查 Pod 是否被 nodeSelector 卡住:
kubectl get node k8s-worker1 --show-labels
kubectl -n gitlab describe pod <pod-name> | grep -A5 -i events
如果看到:
node(s) didn't match Pod's node affinity/selector
说明节点标签和 values 里的 global.nodeSelector 对不上。
Ingress 不通
kubectl -n gitlab get ingress
kubectl -n gitlab describe ingress
kubectl -n gitlab get certificate
kubectl -n ingress-nginx get svc ingress-nginx-controller -o wide
curl -kI https://gitlab.jihw.top
如果证书不签发,先确认 ClusterIssuer:
kubectl get clusterissuer
Git push 慢或失败
检查:
kubectl -n gitlab get pods | grep gitaly
kubectl -n gitlab get pvc | grep gitaly
kubectl -n gitlab logs <gitaly-pod>
Gitaly 是 Git 仓库的核心组件。它的 PVC 不要随便删。
PVC 回收策略
GitLab 的关键数据建议使用 Retain:
kubectl -n gitlab get pvc
kubectl get pv | grep gitlab
kubectl get pv <pv-name> -o jsonpath='{.spec.persistentVolumeReclaimPolicy}'
如果发现重要 PV 是 Delete:
kubectl patch pv <pv-name> \
-p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
备份建议
GitLab 备份比普通应用复杂,要覆盖:
PostgreSQL 数据库
Gitaly Git 仓库数据
对象存储 bucket
Secrets
Helm values
建议:
1. CloudNativePG 定期备份 PostgreSQL
2. Longhorn Snapshot / Backup 保护 Gitaly PVC
3. 对象存储开启版本控制或单独备份
4. 保存 gitlab-values.yaml
5. 保存关键 Secret
6. 升级 GitLab 前先备份
这次虽然不考虑 GitLab 高可用,只部署在一台 worker 上,但备份仍然必须做。单 worker 只是不做应用层高可用,不代表可以接受数据丢失。
如果这台 worker 故障:
GitLab 服务会不可用
Longhorn 如果有健康副本,数据还有机会在其他节点恢复
如果没有备份,误删、数据库损坏、对象存储丢失仍然很难救
不要把 Retain 当成备份。Retain 只能降低误删 PVC 时的数据清理风险,不能替代数据库备份和对象存储备份。
升级建议
GitLab 升级要比 SonarQube 更谨慎。
建议:
1. 阅读当前版本到目标版本的升级说明
2. 先备份数据库、Gitaly、对象存储
3. 先在测试环境验证 chart values
4. 小版本逐步升级,不要跨太多版本
5. 升级后检查 migrations、Sidekiq、Gitaly、Registry
查看当前 Helm release:
helm -n gitlab list
helm -n gitlab get values gitlab
升级:
helm repo update
helm -n gitlab upgrade gitlab gitlab/gitlab \
-f gitlab-values.yaml
参考
- GitLab Helm Chart 文档:
https://docs.gitlab.com/charts/ - GitLab Helm Chart 安装文档:
https://docs.gitlab.com/charts/installation/ - GitLab 外部 Ingress 文档:
https://docs.gitlab.com/charts/advanced/external-ingress/ - GitLab 外部 PostgreSQL 文档:
https://docs.gitlab.com/charts/advanced/external-db/ - GitLab 外部 Redis 文档:
https://docs.gitlab.com/charts/advanced/external-redis/ - GitLab 外部对象存储文档:
https://docs.gitlab.com/charts/advanced/external-object-storage/ - GitLab Runner Helm Chart 文档:
https://docs.gitlab.com/runner/install/kubernetes/ - Kubernetes Ingress 文档:
https://kubernetes.io/docs/concepts/services-networking/ingress/