上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器中设置并结合 Kubernetes 与大带宽,为跨境研发团队实现分布式 CI/CD 环境?

发布人:Minchunlin 发布时间:2025-09-09 11:04 阅读量:762


夜里 1 点 40,我在香港葵涌的机房,45U 机柜从上到下排着刚上架的 R7525,前面板的指示灯一排排跳,ToR 的 Arista 7050 在做 MLAG 收敛,端口灯以太鼓点一样闪。纤缆托盘里,几根标着 “VLAN30/Ingress”“VLAN20/NAT” 的双芯跳纤刚压好卡扣,Mellanox CX5 的链路协商到 25G;我在控制台里跑了遍 iperf3,链路轻松过 9.4Gbps,可 Slack 依然在叮咚——北京同事说 iOS 的流水线卡死在拉镜像,Node 的依赖像黏在墙上的口香糖,怎么也下来。那一刻我意识到,问题不在于“管子够不够粗”,而是该把计算、镜像、依赖和缓存统统搬到香港,让数据就地解决:用 Kubernetes 把算力铺开,用大带宽把跨境的控制面与必要制品拉通,用 Harbor/Nexus/S3 缓存把 CI/CD 的“热路径”牢牢留在机房里。接下来的几个小时,我要做的不仅是点亮一个集群,而是把一条跨境研发的高速公路,在这片嘈杂里,按下真正的通车键

1. 目标与约束

目标

  • 在香港机房落地一个 分布式 CI/CD 平台,支撑跨境团队(内地 + 海外)并行构建、自动化部署。
  • 充分利用 1–10 Gbps 级别的专线/大带宽,把镜像拉取、依赖下载、制品上传的时间压到最低。
  • 体系可扩、可观测、可回滚,新手能照做,老手能优化。

关键约束

  • 业务 OS 偏好 CentOS 7(公司历史原因)。
  • 构建任务偏重:Android/iOS/Node/Java/Go,镜像层多、依赖大。
  • 跨境访问:内地办公网 ↔ 香港 DC;海外雇员 ↔ 香港 DC。

2. 目标架构(ASCII 拓扑)

结构:
                  海外开发者
                      │  HTTPS/VPN
                      ▼
                ┌───────────────┐
                │  香港 DC (HK) │ 10/40G 上联
                └───────────────┘
      ┌───────────────────────────────────────────────────┐
      │                   Kubernetes 集群                 │
      │  [ingress] NGINX | MetalLB VIP  | egress NAT GW   │
      │             │                  │                  │
      │  ┌──────────▼───┐    ┌─────────▼────────┐   ┌────▼─────┐
      │  │ Harbor 镜像仓 │    │ GitLab + Runner  │   │  MinIO   │
      │  │ S3 远端备份   │    │ ARC/GHA 备选      │   │ Cache/S3 │
      │  └──────────────┘    └───────────────────┘   └──────────┘
      │        │ (registry cache/buildx)                 │
      │        ├───────────┬─────────────────────────────┤
      │   ┌────▼────┐  ┌───▼────┐   ┌────────┐     ┌─────▼────┐
      │   │ Nexus   │  │ Squid  │   │ Tekton │ ... │ Argo CD  │
      │   │ Maven/N │  │ APT/YUM│   │/GitLab │     │ 持续交付  │
      │   └─────────┘  └────────┘   │ CI      │     └──────────┘
      │                              └────────┘
      └───────────────────────────────────────────────────┘
                      ▲                       ▲
                      │ WireGuard/IPSec       │ Prometheus/Loki
          内地办公室───┘         备线:CN2/CMI └── Grafana/Alert

3. 硬件与网络选型(实配参数)

3.1 服务器/交换机/线路

角色 数量 机型/CPU 内存 磁盘 网卡 备注
Control Plane/etcd 3 Dell R6525 / AMD 7313 (16C) 128GB SATA SSD 480G ×2 (RAID1) 2×10G (Intel X710) kube-apiserver/etcd
Worker (构建型) 6 Dell R7525 / AMD 7443P (24C) 256GB NVMe U.2 3.2TB ×4 (RAID10/软阵列) 2×25G (Mellanox CX5) 高 IO/高网络
存储/缓存 2 Supermicro 2U 128GB HDD 12TB ×12 + NVMe 1.6TB ×2 (cache) 2×25G MinIO/Nexus
ToR 交换机 2 Arista 7050 系列 48×10/25G + 6×40/100G MLAG
出口带宽 1 1 Gbps 专线 + 10 Gbps 共享 BGP(CN2/CMI/GIA) 可升配

经验:构建 Worker 上 NVMe 的随机读写比你想象的重要,Android/Node 的依赖展开 + Docker 层缓存能吃满 IOPS。网卡尽量上 CX4/CX5,驱动稳定、RDMA/多队列好调。

3.2 IP/VLAN/MTU

网络 VLAN MTU 用途
K8s Pod 网 200 1450 VXLAN/Calico,避免 1500 上的封装碎片
Node 管理网 10 9000 机房内复制/对象存储加速
出口 NAT 网 20 1500 互联网访问
业务 Ingress 30 1500 HTTPS 对外/对内入口

4. 带宽与并发容量规划

估算公式(保守)

  • 单 Job 平均镜像层 + 依赖传输量:D (GB)
  • 并发 Job 数:N
  • 链路利用率:η(0.6~0.8)
  • 需要的出口带宽(Gbps):B = (D × 8 × N) / (Job 时长 / 60) × 1/1000 / η

示例

  • Android/Node 混合构建:每 Job 平均 2.5 GB 进出,Job 时长 12 分钟,并发 20,η=0.7
  • B ≈ (2.5×8×20)/(12/60)/1000/0.7 ≈ 2.86 Gbps

这就是我们为什么起步就要 1 Gbps 专线 + 10 Gbps 共享出口,并在仓库/依赖代理上尽量 “就地命中”,减少外网流量。

5. 系统与内核准备(CentOS 7,实操细节)

CentOS 7 自带 3.10 内核对 eBPF/容器网络支持一般,建议上 ELRepo 的 5.x LTS,BBR/FQ 改善长肥管道吞吐。

# 1) 升级内核(ELRepo)
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-ml-5.10.219-1.el7.elrepo
grub2-set-default 0 && reboot

# 2) 关闭 swap(kubeadm 必需)
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab

# 3) 基础内核优化
cat >/etc/sysctl.d/99-k8s.conf <<'EOF'
net.core.rmem_max=134217728
net.core.wmem_max=134217728
net.ipv4.tcp_rmem=4096 87380 67108864
net.ipv4.tcp_wmem=4096 65536 67108864
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
net.ipv4.ip_forward=1
fs.inotify.max_user_watches=1048576
fs.inotify.max_user_instances=8192
vm.max_map_count=262144
EOF
sysctl --system

# 4) 时间同步(极易踩坑)
yum install -y chrony && systemctl enable --now chronyd
timedatectl set-ntp yes

坑 1(血泪): NTP 漂移会让 etcd 选主反复超时、Runner 证书校验失败。上线前跑一遍 chronyc sources -v,确保抖动 <5ms。

6. 容器运行时与 kubeadm

选择 containerd,避免 Docker shim 的历史包袱。

# containerd 安装
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y containerd.io

mkdir -p /etc/containerd
containerd config default >/etc/containerd/config.toml

# Systemd cgroup,配合 kubelet
sed -ri 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

systemctl enable --now containerd

# kubeadm/kubelet/kubectl
cat >/etc/yum.repos.d/kubernetes.repo <<'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
EOF
yum install -y kubeadm-1.29.* kubelet-1.29.* kubectl-1.29.*
systemctl enable kubelet

HA 入口:使用 keepalived + haproxy 给 kube-apiserver 一个 VIP 10.0.30.10:6443。

kubeadm-config.yaml(示例):

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.29.6
controlPlaneEndpoint: "10.0.30.10:6443"
networking:
  podSubnet: "10.244.0.0/16"
  serviceSubnet: "10.96.0.0/12"
  dnsDomain: "cluster.local"
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
nodeRegistration:
  criSocket: "unix:///run/containerd/containerd.sock"

初始化与加入:

kubeadm init --config kubeadm-config.yaml
# worker
kubeadm join 10.0.30.10:6443 --token <...> --discovery-token-ca-cert-hash sha256:<...>

7. CNI 与网络(Calico,MTU 调优)

Calico VXLAN,MTU 1450,避免外网路径 1500 + 封装碎片。

# 安装 Calico(示例,需替换版本)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml

# MTU 调整(ConfigMap)
kubectl -n kube-system edit configmap calico-config
# 设置 veth_mtu: "1450"

坑 2: 只改 Pod MTU 不够,Node to Node 的 jumbo(9000)别乱开,否则跨网段 egress NAT 会碎包。统一在 K8s 层用 1450。

8. Ingress/Egress 与负载均衡

Ingress:ingress-nginx + MetalLB 提供裸机 VIP。

Egress:专设 NAT GW DaemonSet(或边界路由)统一出网,便于 QoS/Shaping/审计。

MetalLB(L2 模式)示例:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata: { name: prod-pool, namespace: metallb-system }
spec:
  addresses: [ "10.0.30.100-10.0.30.120" ]
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata: { name: prod-l2, namespace: metallb-system }
spec: {}

9. 镜像仓库与缓存(Harbor + S3/远端备份)

为什么放香港? 大多数构建发生在香港集群内,本地命中拉取;跨境只走制品与少量元数据。

Harbor(Helm)values.yaml 关键点:

  • 开启 notary/扫描(可选)
  • externalURL 指向香港 VIP
  • 后端 s3(MinIO 或公有云对象存储)
  • GC 计划任务,保留最近 N 版本
persistence:
  persistentVolumeClaim:
    registry:
      size: 2Ti
jobservice:
  maxJobWorkers: 8
registry:
  upload_purging:
    enabled: true
    age: 168h
    interval: 24h
    dryrun: false
storageService:
  s3:
    region: us-east-1
    bucket: harbor-prod
    accesskey: xxx
    secretkey: yyy
    secure: true

坑 3: Docker Hub 的 rate limit 经常打爆。Harbor 里配置 上游 proxy cache,并给 CI 用 registry.mydomain/hub-cache/library/node:18 这种前缀。

10. 依赖加速:Nexus + Squid + 语言代理

  • Nexus:Maven、npm、PyPI、Gradle 代理仓统一命中。
  • Squid:APT/YUM/ CocoaPods 私有镜像(配上 ACL + 缓存目录 SSD)。
  • Android:Gradle ~/.gradle/gradle.properties 指向 Nexus;org.gradle.parallel=true。
  • Node:.npmrc 指向 https://nexus/repository/npm-group/。

11. CI 平台落地:GitLab + Runner(或 GitHub ARC)

11.1 GitLab Runner on K8s(推荐参数)

values.yaml 片段(关键位):

gitlabUrl: https://gitlab.example.com/
runnerRegistrationToken: "<TOKEN>"
rbac:
  create: true

runners:
  privileged: true            # BuildKit/DIND 需要
  requestConcurrency: 40
  concurrent: 80              # 集群总并发上限
  cpuLimit: "8"
  memoryLimit: "16Gi"
  cache:
    type: s3
    s3ServerAddress: "minio.minio.svc.cluster.local:9000"
    s3BucketName: "gitlab-cache"
    s3BucketLocation: "us-east-1"
    s3CacheInsecure: true
  builds:
    cpuRequests: "2"
    memoryRequests: "4Gi"
  services:
    cpuRequests: "500m"
    memoryRequests: "1Gi"

构建优化

用 BuildKit + buildx,开启 跨 Job 层缓存:

docker buildx create --use
docker buildx build \
  --cache-from=type=registry,ref=harbor/cache/myapp:buildcache \
  --cache-to=type=registry,ref=harbor/cache/myapp:buildcache,mode=max \
  -t harbor/prod/myapp:${CI_COMMIT_SHA} .

iOS 打包 Runner 用 macOS 金丝雀机 通过 WireGuard 接入(ARC 也可)。

11.2 备选:GitHub Actions Self-Hosted(actions-runner-controller)

如果公司偏 GH,部署 actions-runner-controller,思路与缓存完全一致。

12. 持续交付:Argo CD 基线

  • 每个服务一个 Application,GitOps 推进。
  • 使用 kustomize overlays 区分 dev/stage/prod。
  • 镜像标签锁定 SHA,回滚迅速。

示例 Application:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: myapp-prod, namespace: argocd }
spec:
  project: default
  source:
    repoURL: 'https://git.example.com/ops/myapp-deploy.git'
    targetRevision: main
    path: overlays/prod
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: myapp
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

13. 跨境连接:WireGuard / IPSec + 分流

  • 内地办公网 <-> 香港 DC:WireGuard 站点到站点,仅开发/CI 必需网段走隧道;其余走本地互联网。
  • DNS Split-Horizon:harbor.prod.local 在内地解析到 北京边界 VIP,再由边界路由转发到 HK,或直接解析 HK VIP(看策略)。
  • SNI 透传:避免中间代理插证书,移动端签名校验敏感。

WireGuard 端点(HK):

[Interface]
Address = 172.22.0.1/24
ListenPort = 51820
PrivateKey = <HK-PRIVATE>

[Peer]
PublicKey = <BJ-PUB>
AllowedIPs = 10.8.0.0/16, 10.0.0.0/16
PersistentKeepalive = 25

14. QoS 与大带宽利用(别“糟蹋”链路)

出口 GW 用 tc fq_codel 或 cake,避免队首阻塞。

为 CI 子网设置优先级(容许 80% 峰值),预留 20% 给线上服务/管理面。

# 简化示例:ifb 入向整形 + fq_codel
tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:10 htb rate 8gbit ceil 9gbit
tc class add dev eth0 parent 1: classid 1:20 htb rate 1gbit ceil 1gbit
# 把线上 VIP 段标到 1:20,CI 段到 1:10(配合 iptables --set-class)

15. 可观测性与告警

Prometheus + Grafana:关注 container_network_transmit_bytes_total、registry_request_latency、runner_job_duration_seconds。

Loki:Runner/Harbor/Ingress 的关键日志索引。

告警:

  • Harbor proxy cache 命中率 < 70%
  • Runner Pending > 20 (持续 5 分钟)
  • 出口带宽 5 分钟均值 > 85%(考虑扩容)

16. 备份与演练

  • etcd:etcdctl snapshot save 每日,异地存储。
  • Harbor:镜像复制到二存(另一家对象存储/另一 Region)。
  • MinIO:版本化 + 生命周期。
  • 集群应用:Velero 备份命名空间级资源与 PV。

17. 验证与量化结果

上线后一周我们做了对比(真实统计口径):

指标 上线前(内地构建) 上线后(HK 构建) 变化
平均镜像拉取 14.8 分钟 2.3 分钟 ↓ 84%
Node 构建(npm 依赖) 9.5 分钟 3.1 分钟 ↓ 67%
Android 构建 37 分钟 22 分钟 ↓ 41%
每日可交付构建数 180 520 ↑ 188%
出口外网峰值 2.6 Gbps
Harbor 命中率 78%

关键并不在“更粗的管子”,而在“把数据留在香港”:镜像/依赖/缓存尽量本地命中,跨境只走必要的控制面与制品归档。

18. 常见坑与现场解决

NTP 漂移:etcd 反复选主、Runner TLS 报过期。

解决:chrony 对齐 3 家上游 + 边界 PTP;Prometheus 做抖动告警。

MTU 不一致:Pod 1450,Node 9000,跨网段时碎包重传,速度像“拉闸”。

解决:K8s 全面 1450;管理网/存储网保留 9000,但不穿越 egress。

Docker Hub 限流:白天集中爆仓。

解决:Harbor Proxy Cache + 项目白名单镜像预拉取;夜间预热脚本。

npm/maven 污染/不可达:上游被墙或变更。

解决:Nexus 组仓 + 双上游(国外/国内镜像),健康探针自动切换。

kube-proxy ipvs + conntrack 累积:大流量 Ingress 下连接表爆。

解决:提高 nf_conntrack_max,并调短 tcp_tw_reuse/tcp_fin_timeout;或迁移 Cilium/BPF(需核对内核)。

Runner DIND 慢:overlay2 白名单/元数据开销大。

解决:改 BuildKit,跨 Job 层缓存到 Harbor;NVMe 专盘挂载 /var/lib/docker。

证书链:海外同事偶发 TLS 验证失败。

解决:统一由 ACME 颁发,集团内 CA 仅用于内网域名;确保 fullchain.pem。

19. 关键配置清单(可直接抄)

19.1 kubelet(cgroup/systemd)

cat >/etc/sysconfig/kubelet <<'EOF'
KUBELET_EXTRA_ARGS="--cgroup-driver=systemd --node-status-update-frequency=10s"
EOF
systemctl daemon-reload && systemctl restart kubelet

19.2 Ingress NGINX(大连接优化)

controller:
  config:
    use-forwarded-headers: "true"
    proxy-body-size: "0"
    proxy-buffer-size: "32k"
    proxy-read-timeout: "600"
    keep-alive: "120"

19.3 Nexus(npm 代理组)

npm-group = npmjs(国外) + cnpm(国内),路由失败自动降级。

带宽大时,开启 maximum concurrent connections 限制到 500,避免吞吐抖动。

19.4 Prometheus 告警(示例)

- alert: RunnerPendingHigh
  expr: sum(gitlab_runner_jobs{status="pending"}) > 20
  for: 5m
  labels: { severity: warning }
  annotations: { summary: "Pending jobs > 20 for 5m" }

20. 运维手册里的“红线”

  • 变更窗口:跨境路由/证书/Harbor GC 只在低峰执行。
  • 配额:团队/项目级并发上限,避免一个仓库独占带宽。
  • 一键回滚:Argo CD Sync Window + 镜像以 SHA 标识。
  • 演练:每月随机抽项目从头构建一次,验证缓存失效路径。

21. 尾声:那杯凉掉的美式

一周后,我们把统计图发到群里,大家都在刷“这次真快”。北京的 iOS 同学开玩笑说:“再也不用喝完一杯奶茶等镜像了。” 我回到那台 R7525 前,摸了摸还是热乎的散热片,心里想的却是下一步:把 1 Gbps 专线升到 5 Gbps,把海外办公室的接入也并回来。
机房的风还是大,但这次我把咖啡喝完了。

22. 总结(给新手与老手)

  • 新手照做:按本文硬件/网络/配置直接落地,优先把 Harbor + Nexus + Runner on K8s 跑起来。
  • 老手优化:关注 链路利用率、MTU 一致性、缓存命中率、BuildKit 层缓存,再考虑 Cilium/eBPF、RDMA、分层存储等高级优化。
  • 核心心法:跨境不等于“拉更粗的管子”,而是 把数据留在最近的地方,让链路只传真正必须的东西。

如果你也准备在香港落一套分布式 CI/CD,希望这份“带汗味儿”的实操能让你少踩几个坑。

目录结构
全文