第 18/19 天
引言:从”能跑”到”扛得住”的距离

走过前面十七天,我们的 DevOps 管道已经能完成「代码提交 → 构建 → 静态检查 → GitOps 部署 → 灰度 → 监控 → 告警」的完整闭环。但如果你把这套环境直接搬到生产,第一个上班日大概率会收到 PagerDuty 的连环夺命 call——单点 Harbor 重启后镜像拉不下来、GitLab 的内置 PostgreSQL 卡在 I/O 饱和、Argo CD 的 Redis OOM 导致全集群同步停滞……这些坑的共同点是:组件本身没问题,是部署形态不够生产化。
今天这一篇不引入新组件,只回答一个问题:怎样把 Harbor、GitLab、Argo CD、SonarQube 四个核心平台,从 PoC 级别改造到生产级的高可用(HA)形态。内容覆盖存储后端、数据库高可用、副本扩缩、备份恢复、证书与域名、性能调优六条主线,最后给出一套可直接套用的 Helm values 与运维 runbook。
核心概念:高可用不是”多副本”四个字
可用性的三个层次
生产化加固要分层来看,不能一把梭:
- 接入层高可用:负载均衡 + 多实例,避免单 Pod 宕机导致服务不可用;
- 数据层高可用:数据库主从、对象存储多副本、Redis 哨兵或集群模式,避免数据丢失;
- 运维层高可用:定时备份、异地容灾、DNS 故障切换,保证故障发生后能快速恢复。
很多同学只做了第一层(多副本),就以为生产化了,结果数据库一崩直接回到解放前。今天每个组件我们都会同时关注三层。
通用加固 Checklist
在动手前,记住这张通用的加固清单,后面四大组件都按这个顺序推进:
- 有状态组件(数据库、Redis、存储卷)优先做高可用;
- 无状态组件(API、Web 前端)扩副本数 + 设置 PDB;
- 配置与密钥脱离镜像,走 Secret/ConfigMap + 外部 KMS;
- 启用 TLS 与 RBAC,控制面与数据面分离;
- 备份策略 + 演练,没演练过的备份等于没备份。
实战一:Harbor 高可用方案
瓶颈分析
Harbor 的核心服务(core、jobservice、registry、portal)是无状态的,可以多副本。真正的瓶颈在两个地方:
- Redis:存储会话与任务队列,单点 Redis 一挂,登录态全丢;
- 存储后端:默认用 PVC 存镜像层,单 PVC 绑死单节点,节点故障即不可用。
所以 Harbor 高可用的关键是:Redis 走外部集群 + 存储走对象存储(S3/OSS/MinIO)。
Helm values 高可用配置
# harbor-ha-values.yaml
expose:
type: ingress
tls:
enabled: true
certSource: secret
secret:
secretName: harbor-tls
ingress:
className: nginx
host: harbor.stellardata.top
externalURL: https://harbor.stellardata.top
# 核心服务多副本
core:
replicas: 2
resources:
requests: { cpu: 500m, memory: 512Mi }
limits: { cpu: 1, memory: 1Gi }
jobservice:
replicas: 2
resources:
requests: { cpu: 250m, memory: 256Mi }
registry:
replicas: 2
# 关键:改用 S3 后端,避免单 PVC
storage:
s3:
bucket: harbor-registry
region: us-east-1
accesskey: "${S3_ACCESS_KEY}"
secretkey: "${S3_SECRET_KEY}"
# Redis 走外部集群,不再用内置单点
redis:
type: external
external:
addr: redis-ha.stellardata-top.svc.cluster.local
port: 26379
sentinelName: mymaster
sentinelAuth: "${REDIS_SENTINEL_AUTH}"
password: "${REDIS_PASSWORD}"
# 数据库走外部高可用 PG
database:
type: external
external:
host: pg-ha.stellardata-top.svc.cluster.local
port: 5432
username: harbor
password: "${PG_PASSWORD}"
coreDatabase: registry
# Trivy 与 ChartMuseum 也走外部存储
trivy:
replicas: 2
storageClass: nfs-client
persistence:
persistentVolumeClaim:
registry:
size: 50Gi
storageClass: ""
部署命令:
# 添加并更新 Harbor 仓库
helm repo add harbor https://helm.goharbor.io
helm repo update
# 先注入密钥到 Secret,避免明文落盘
kubectl create secret generic harbor-secrets
–from-literal=S3_ACCESS_KEY=AKIAxxxxx
–from-literal=S3_SECRET_KEY=$(openssl rand -hex 16)
–from-literal=PG_PASSWORD=$(openssl rand -hex 16)
-n harbor
# 部署
helm upgrade –install harbor harbor/harbor
-f harbor-ha-values.yaml
-n harbor –create-namespace –version 1.14.0
# 验证副本数
kubectl get deploy -n harbor -o wide
PDB 与反亲和
光扩副本还不够,升级时两个 Pod 一起被驱逐会瞬间不可用。务必加 PodDisruptionBudget:
# harbor-pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: harbor-core-pdb
namespace: harbor
spec:
minAvailable: 1
selector:
matchLabels:
component: core
—
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: harbor-registry-pdb
namespace: harbor
spec:
minAvailable: 1
selector:
matchLabels:
component: registry
配合 topologySpreadConstraints 把副本打散到不同节点:
topologySpreadConstraints:
– maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
component: core
实战二:GitLab CE 高可用方案
架构取舍
GitLab 官方提供了 Omnibus 一体包,部署简单但天花板是单机。要做真正的 HA,必须切到 GitLab Helm Chart + 外部组件 架构:
- GitLab Rails(Webservice/Sidekiq)→ 多副本无状态;
- Gitaly(Git 仓库存储)→ 存储层,需配合 NFS 或 Gitaly Cluster;
- PostgreSQL → 外部 CloudNativePG 主从;
- Redis → 外部 Redis Sentinel;
- Runner → 独立 Chart,与 GitLab 解耦。
Gitaly Cluster 是关键
Gitaly 是唯一真正有状态的组件,单机 Gitaly 一旦磁盘损坏,仓库数据全没。生产环境必须上 Gitaly Cluster:
# gitaly-cluster.yaml(简化示意)
gitlab:
gitaly:
persistence:
storageClass: ceph-rbd
size: 200Gi
# 三副本 + 故障自动切换
replicas: 3
topologySpreadConstraints:
– maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: gitaly
# 通过 Praefect 代理实现透明故障切换
praefect:
enabled: true
replicas: 3
database:
host: pg-ha.stellardata-top.svc.cluster.local
name: praefect
Gitaly Cluster 通过 Praefect 这一前置代理,把写请求路由到主副本,读请求可分散到副本,主节点故障时自动切换。这是 GitLab HA 的真正底气。
Webservice 与 Sidekiq 横向扩展
gitlab:
webservice:
minReplicas: 3
maxReplicas: 10
hpa:
enabled: true
targetCPU: 70
workerTimeout: 60
resources:
requests: { cpu: 900m, memory: 2Gi }
limits: { cpu: 2, memory: 4Gi }
sidekiq:
replicas: 3
# 不同队列用不同 Sidekiq 进程池隔离
queues:
– name: default
workers: 2
– name: mailers
workers: 1
备份策略
GitLab 备份要走对象存储而非本地 PVC:
# 1. 备份配置
kubectl exec -n gitlab deploy/gitlab-toolbox —
backup-utility –env-file /etc/gitlab-backup/env
–backend s3 –parameters "Bucket=gitlab-backup"
# 2. 通过 CronJob 每天 02:00 自动备份
cat > /tmp/gitlab-backup-cron.yaml << 'EOF'
apiVersion: batch/v1
kind: CronJob
metadata:
name: gitlab-backup
namespace: gitlab
spec:
schedule: "0 18 * * *" # UTC 18:00 = 北京 02:00
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
– name: backup
image: registry.gitlab.com/gitlab-org/build/cng/gitlab-toolbox
command:
– /bin/sh
– -c
– |
backup-utility –backend s3
–parameters "Bucket=gitlab-backup,Region=us-east-1"
EOF
实战三:Argo CD 高可用方案
内置 HA vs 外部化
Argo CD 是四个组件里 HA 改造最省心的——官方 Helm Chart 自带 server HA 模式,开启一个开关就能多副本。但同样要注意 Redis 这一层。
# argocd-ha-values.yaml
server:
replicas: 3
# 自动水平扩缩
autoscale:
enabled: true
minReplicas: 3
maxReplicas: 8
targetCPUUtilizationPercentage: 70
# 反亲和避免同节点
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
– weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: argocd-server
topologyKey: kubernetes.io/hostname
repoServer:
replicas: 3
applicationSet:
replicas: 2
# Redis 走外部 HA 集群,禁用内置单点
redis:
enabled: false
externalRedis:
host: redis-argocd-ha.stellardata-top.svc.cluster.local
port: 26379
sentinel: true
sentinelName: argocd
password: "${ARGOCD_REDIS_PASSWORD}"
# 控制器也要多副本(leader 选举)
controller:
replicas: 3
# 多副本时通过 leader election 保证只有一份工作
env:
– name: ARGOCD_CONTROLLER_REPLICAS
value: "3"
# 关键:开启 PDB,升级时不被一锅端
server:
pdb:
enabled: true
minAvailable: 2
ApplicationSet 与 Git Generator
生产环境最大的痛点是「几十上百个微服务要同步」——纯 Application 资源手写到崩溃。改用 ApplicationSet 的 Git Generator 可以一条配置生成 N 个 Application:
# appset-all-services.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: all-services
namespace: argocd
spec:
generators:
– git:
repoURL: https://gitlab.stellardata.top/infra/manifests.git
revision: main
directories:
– path: services/*
template:
metadata:
name: '{{path.basename}}'
spec:
destination:
namespace: '{{path.basename}}'
server: https://kubernetes.default.svc
project: default
source:
repoURL: https://gitlab.stellardata.top/infra/manifests.git
targetRevision: main
path: '{{path}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
– CreateNamespace=true
这样仓库目录每加一个服务,Argo CD 自动生成对应 Application,无需人工干预,这才是 GitOps 的真正生产力。
实战四:SonarQube 高可用方案
两个真正的瓶颈
SonarQube 在生产里最容易出问题的两个地方:
- Elasticsearch:SonarQube 把代码扫描结果、Issue、安全热点全塞进 ES,单节点 ES 内存一满就 OOM;
- Compute Engine:扫描分析后台任务,单线程跑会积压。
SonarQube 官方从 9.9 起提供 Data Center Edition 才支持真正的多节点,社区版只能做”伪 HA”——通过外部化存储和数据库来降低单点风险。
Helm values 高可用配置
# sonarqube-ha-values.yaml
edition: "community" # 企业版才支持 datacenter
# 数据库走外部高可用 PG
postgresql:
enabled: false
externalPostgresql:
host: pg-ha.stellardata-top.svc.cluster.local
port: 5432
database: sonar
username: sonar
password: "${PG_PASSWORD}"
# 关键:给 ES 足够内存,避免 OOM
sonarQube:
resources:
requests:
cpu: 2
memory: 4Gi
limits:
cpu: 4
memory: 8Gi
env:
– name: SONAR_SEARCH_JAVA_OPTS
value: "-Xms2g -Xmx2g -XX:+UseG1GC"
– name: SONAR_CE_JAVA_OPTS
value: "-Xms1g -Xmx2g"
# 关闭内置 ES 的 swap,避免被驱逐
podAnnotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"
# PVC 改用 Ceph RBD,提供块级别冗余
persistence:
enabled: true
storageClass: ceph-rbd
accessMode: ReadWriteOnce
size: 20Gi
用 Webhook 把扫描结果回推
光部署高可用还不够,要保证 GitLab CI 能拿到扫描结果。配置 SonarQube 的 Webhook:
# 在 SonarQube 管理后台配置 Webhook
# Administration → Configuration → Webhooks
# URL: https://gitlab.stellardata.top/api/v4/projects/<id>/pipelines/<pipeline_id>
# 通过 GitLab CI 配合质量门禁
cat > .gitlab-ci.yml << 'YAML'
sonarqube-check:
stage: analyze
image: sonarsource/sonar-scanner-cli:latest
script:
– sonar-scanner
-Dsonar.projectKey=$CI_PROJECT_PATH
-Dsonar.sources=src
-Dsonar.qualitygate.wait=true # 关键:等质量门结果
rules:
– if: $CI_PIPELINE_SOURCE == "merge_request_event"
YAML
qualitygate.wait=true 会让 CI 任务阻塞,直到 SonarQube 把这次扫描结果跟门禁规则比对完,门禁不过则 CI 失败,把低质量代码挡在合并之前。
常见问题与踩坑
问题 1:升级后 Harbor 启不来,报 “schema mismatch”
根因:内置 PG 在升级时没执行 migration。解法:要么走外部 PG 让运维统一管理 migration,要么升级前先 helm rollback 回到上一版本,再用 migrate.sh 单独跑 schema 升级。永远不要跨大版本直接升级 Harbor,先在 staging 走一遍。
问题 2:Argo CD 同步卡在 OutOfSync 死循环
根因:用了 selfHeal: true 但 Helm values 里有 {{ }} 模板变量没渲染。解法:把模板变量改成 Kustomize 的 patches 或 Argo CD 的 ignoreDifferences:
syncOptions:
– RespectIgnoreDifferences=true
ignoreDifferences:
– group: apps
kind: Deployment
jsonPointers:
– /spec/replicas # HPA 在管副本数,忽略差异
问题 3:GitLab Sidekiq 任务堆积,Webhook 不触发
根因:mailers 队列挤占 default 队列。解法:按前面配置把 mailers 独立成 worker pool,再给 default 单独分配 workers。同时给 Sidekiq 加上 maxConcurrency 上限避免线程饿死:
# 监控队列长度
kubectl exec -n gitlab deploy/gitlab-sidekiq —
sidekiq-stats -r redis://redis-ha:6379
问题 4:SonarQube 扫描 OOM,Compute Engine 重启
根因:扫描大仓库时 CE 堆内存不够。解法:调大 SONAR_CE_JAVA_OPTS 到 2-4g,并在 CI 里把大仓库拆成多 module 扫描,避免一次性加载。
问题 5:备份恢复了,但镜像拉不下来
根因:Harbor 的 registry 数据在 S3,但 core 的 ConfigMap 里有缓存。解法:恢复后强制 kubectl rollout restart deploy/harbor-core -n harbor 清空缓存,再重新登录验证。
总结:高可用是一条流水线,不是一次动作
今天的四个组件看起来各自独立,但它们的高可用改造其实是一条流水线:
- 数据层:PG 走 CloudNativePG、Redis 走 Sentinel、对象存储走 MinIO/S3;
- 接入层:每个组件 replicas ≥ 2 + PDB + topologySpread;
- 运维层:CronJob 定时备份 + 异地副本 + 每月演练。
最容易忽视的是第三层。备份脚本写好了,但只有真演练过才知道能不能恢复。没演练过的备份等于没备份——这句话送给每一个做过 HA 的同事。
明天是系列的收官篇,我们用 Trivy 做镜像漏洞扫描、用 cosign 给镜像签名、用 Kyverno 在 K8s 准入层验签,把整条 DevOps 管道的安全闭环补上最后一环——供应链安全。
下期预告
明天将发布 第 19 天:供应链安全闭环:Trivy 扫描 + cosign 签名 + Kyverno 验签,给今天的四大平台画上安全句号,把容器供应链从「能跑」推到「可信」。
系列大纲
- 全链路架构总览:从代码提交到灰度发布的完整管道设计
- K8s 集群与网络基础:kubeadm 离线部署与 Cilium 网络插件
- Harbor 私有镜像仓库部署与验证:Helm 安装与镜像推送
- GitLab CE 自托管部署:代码仓库与项目管理平台
- GitLab Runner 配置:K8s Executor 与 RBAC 权限设置
- GitLab CI 流水线搭建:.gitlab-ci.yml 与 Kaniko 无守护进程构建
- Harbor 镜像版本管理与 Tag 策略配置
- Argo CD 部署与 GitOps 配置仓库创建
- CI 与 CD 联动:从代码提交到部署的全自动闭环
- SonarQube 代码质量检查:SonarQube 部署与 GitLab CI 集成
- Prometheus 部署:kube-prometheus-stack 与 ServiceMonitor 指标采集
- Grafana 看板搭建:K8s 标准面板与自定义业务看板
- 告警规则与 Alertmanager:钉钉/邮件通知渠道配置
- Argo Rollouts 安装与基础概念:CRD 与 Rollout 资源定义
- 金丝雀发布实战:setWeight 流量分割与 pause 步骤
- AnalysisTemplate 指标分析与自动回滚机制
- 全链路端到端演示:代码提交→构建→代码检查→灰度→监控→告警完整流程
- 生产化加固要点:Harbor/GitLab/Argo CD/SonarQube 高可用方案(今天)
- 供应链安全闭环:Trivy 扫描 + cosign 签名 + Kyverno 验签

















暂无评论内容