跳转至

SOP:关闭 Harbor 上游加速隧道,改走旁路由 Clash 默认路由(2026-10-01)

变更类型:C 类(生产变更) 实现体:本工程(入口 main.py,业务模块 utils/harbor_host.py) 目标主机:192.168.31.31(harbor-01) 交付物目录:upstream-direct-20261001/(本文件所在目录)


1. 背景

Harbor 部署时(同日早先)为绕过「家宽直连海外极慢」引入了香港跳板 SOCKS5 隧道, harbor.upstream_proxy.enabled: true,Harbor 的 core / jobservice / registry 组件经 192.168.31.31:1080 出网。当时实测「直连 24 KB/s vs 隧道 1.0~2.4 MB/s」。

本次复测推翻该结论,触发关闭:

路径 本次实测 说明
Harbor 主机 → 香港 rainchen.top:22(原始 TCP,50 MB) 32 KB/s 30 次 ping 丢包 13.3%;PMTU 受限(payload 1472 不通、1414 通)
Harbor 主机经隧道(socks5://127.0.0.1:1080)拉 10 MB 2.4 KB/s 隧道把上面的链路劣势放大
Harbor 主机经默认路由(→ 旁路由 192.168.31.2 Clash)拉 10 MB 5.89 MB/s 旁路由选优改造后此路已快
同上,经 Clash 拉各上游镜像层 ghcr 4.40、registry.k8s.io 5.70、quay 6.86、docker.io 1.28 MB/s 全部达标
香港本机直连 cloudflare 41 MB/s、GitHub CDN 1.08 MB/s 跳板本身不慢,慢的是内网→香港这一段

结论:该隧道已由「加速通道」变成「瓶颈通道」,且其劣势根源(家宽 → 香港丢包) 不在本方可控范围。故关闭隧道,让组件走默认路由。

2. 目标

  1. Harbor 组件的上游出网不再经隧道,改走 Harbor 主机默认路由(旁路由 Clash);
  2. 隧道残留被清理干净:systemd 单元删除、服务停止、1080/tcp 防火墙放行撤销;
  3. 该结论可被复跑验证:deploy-harbor 二次执行零变更(幂等);
  4. 存量镜像预热(58 条)完成到「仅上游确实不可得者除外」;
  5. A1(缓存命中后节点拉取速率)达标。

3. 前置条件

项 要求
权限 可免密 SSH 到 192.168.31.31(root)
旁路由 192.168.31.2 的 OpenClash 正常运行,且主组指向带宽达标节点
Harbor 9 个容器 running,API /api/v2.0/ping 返回 200
窗口 重建 Harbor 容器时服务短暂中断,窗口约 4 分钟
交付物 本目录 backup/(台账见 backup/README.md)

4. 影响范围

维度 说明
主机 仅 192.168.31.31 一台
是否停服 是:install.sh 会重建 Harbor 容器,约 1~2 分钟不可用;4 台 k8s 节点在此期间拉镜像会失败一次,重试即恢复
数据 无:/data/harbor(含已缓存镜像与数据库)不动
网络 Harbor 出网由「隧道→香港」改为「默认路由→Clash」,开始消耗机场节点流量
回滚 配置改回 enabled: true 后重跑 deploy-harbor,隧道按模板重建

5. 分步操作

实现体:utils/harbor_host.py(_remove_upstream_proxy / _render_harbor_yml / _step_states), 模板:utils/templates/{harbor_yml.tmpl,probe.sh}。以下为等价手工序列,以实现体为准。

步骤 1:关闭配置开关

harbor.upstream_proxy.enabled 由 true 改 false。此开关同时控制 harbor.yml 是否渲染 proxy: 段、以及是否建立隧道。

# 工程内:config/default.yaml
#   harbor.upstream_proxy.enabled: false

步骤 2:模板改为整块渲染(否则禁用后会留下空的 proxy 配置)

原模板把 proxy: 段写死、只替换值,禁用时会产生 http_proxy:(空值)。 改为整个段落由实现体生成:启用时输出完整 proxy 块,禁用时只输出一行注释。

# utils/templates/harbor_yml.tmpl:把 proxy 段替换为
# _version: __HARBOR_YML_VERSION__
# __PROXY_BLOCK__

步骤 2b:修复 harbor 步的幂等缺陷(容器 DNS 判定)

坑(本次实测暴露):harbor 步的 DNS 判定原先读 docker inspect ... HostConfig.Dns。 在「DNS 由 daemon.json 作为 daemon 级默认」的场景下该字段返回 null,于是 got_dns=[] 恒不等于期望值 → 该步永远 pending,每次重跑 deploy-harbor 都会重建 Harbor 容器(实测:一次复跑白重建一次,耗时 202 s,服务再中断一次)。

修法:改读运行中容器 resolv.conf 里 Docker 实际使用的外部 DNS(# ExtServers: 行), 即「容器真正在用的 DNS」。

# 探测(utils/templates/probe.sh)
docker exec harbor-core cat /etc/resolv.conf | sed -n 's/^# ExtServers: \[\(.*\)\]/\1/p' | tr ' ' ','
# 预期:223.5.5.5,119.29.29.29

步骤 3:清理隧道(幂等,本步为高危动作)

停服务、删单元、撤防火墙放行、杀残留监听。全部动作可重复执行。

# [高危] 删除 systemd 单元属不可逆动作,重新启用需重跑 deploy-harbor 重建
systemctl stop harbor-upstream-tunnel 2>/dev/null || true
systemctl disable harbor-upstream-tunnel 2>/dev/null || true
rm -f /etc/systemd/system/harbor-upstream-tunnel.service
systemctl daemon-reload
systemctl reset-failed harbor-upstream-tunnel 2>/dev/null || true
firewall-cmd --permanent --remove-rich-rule="rule family='ipv4' source address='172.16.0.0/12' port port='1080' protocol='tcp' accept" || true
firewall-cmd --permanent --remove-rich-rule="rule family='ipv4' source address='127.0.0.1' port port='1080' protocol='tcp' accept" || true
firewall-cmd --reload
fuser -k 1080/tcp 2>/dev/null || true
ss -lnt | grep -q ':1080 ' && echo PORT_STILL_UP || echo TUNNEL_REMOVED

预期:输出 TUNNEL_REMOVED。 失败判断:输出 PORT_STILL_UP → 本步失败(工程按失败处理,不进入下一步)。

步骤 4:重建 Harbor 容器(渲染新的 harbor.yml)

坑:install.sh 同时承担首装与升级;它会重建全部容器,Harbor 短暂不可用。 坑:harbor.yml 的 no_proxy 必须是逗号分隔字符串,写成 YAML 列表会让 prepare 在 no_proxy_config.split(',') 处抛 AttributeError(本次禁用后该段不再渲染,风险仅存在于重新启用时)。

cd /opt/harbor
cp -a harbor.yml harbor.yml.bak.$(date +%Y%m%d-%H%M%S)     # 高危动作前置备份
# harbor.yml 由实现体渲染(含口令替换)后写入,再执行:
./install.sh
# 就绪判定:轮询 API 而非固定 sleep
for i in $(seq 1 60); do
  [ "$(curl -sk -o /dev/null -m 10 -w '%{http_code}' https://192.168.31.31/api/v2.0/ping)" = "200" ] && { echo HARBOR_READY; break; }
  sleep 5
done

预期:HARBOR_READY=1,9 个容器 running。 失败判断:60 次轮询后仍非 200 → 第 7 章回滚。

步骤 5:一键执行(推荐)

cd /home/rain/Documents/services/harbor
.venv/bin/python main.py deploy-harbor --dry-run   # 预演:只列变更,不改状态
.venv/bin/python main.py deploy-harbor             # 正式执行
.venv/bin/python main.py deploy-harbor             # 复跑一次,验证幂等(应全跳过)

预期:首次 [变更] upstream_proxy + [变更] harbor,其余步骤 [跳过];复跑全部 [跳过]。

6. 验证

# 1) 容器内不再有代理(值为空即等价于不启用)
docker inspect harbor-core --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -E '^(HTTP|HTTPS)_PROXY='
docker inspect registry    --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -E '^(HTTP|HTTPS)_PROXY=' || echo "registry: 无代理 env"

# 2) 隧道残留为零
[ -f /etc/systemd/system/harbor-upstream-tunnel.service ] && echo unit-still-there || echo unit-gone
systemctl is-active harbor-upstream-tunnel 2>/dev/null || echo svc-gone
ss -lnt | grep -c ':1080 '        # 期望 0

# 3) Harbor 自身健康
curl -sk -o /dev/null -w '%{http_code}\n' https://192.168.31.31/api/v2.0/ping    # 期望 200

# 4) 各上游源经默认路由的层下载速率(操作机执行)
#    期望:ghcr ≥4 MB/s、registry.k8s.io ≥5 MB/s、quay ≥6 MB/s
curl -s -m 25 -o /dev/null -w '%{speed_download} B/s\n' 'https://speed.cloudflare.com/__down?bytes=10485760'

# 5) 存量镜像预热(幂等,已入库自动跳过)
.venv/bin/python main.py warmup

# 6) A1:节点从 Harbor 命中缓存的拉取速率(k8s-master-01 执行,期望远超 1 MiB/s)
#    见本次实测:40.4 / 46.9 MB/s

本次实测结果

项 结果
隧道清理 TUNNEL_REMOVED;单元删除、服务停止、1080 无监听
容器代理 env harbor-core 三项为空字符串,registry 无代理 env
Harbor 健康 /ping = 200,9 容器 running
deploy-harbor 耗时 3 分 49 秒(13:32:35 → 13:36:24,EXIT=0)
幂等复跑 修复 DNS 判定后:done=[全部 12 步] pending=[],变更动作 0 条,EXIT=0。(修复前复跑会把 harbor 判为 pending 并重建容器 202 s —— 即本次暴露的幂等缺陷)
存量镜像预热 25 分 45 秒(1544.82s),缓存 269 MB → 4.9 GB;58 条中 57 条入库
唯一未入库 quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z —— 集群实际在用该 tag,但上游已不可得(直连 quay.io 亦 401/不存在),属源侧问题,非本链路
A1 节点 ← Harbor:kube-controller-manager 40.4 MB/s、traefik 46.9 MB/s

7. 回滚

7.1 备份位置

本目录 backup/,台账见 backup/README.md;远端另有 192.168.31.31:/opt/harbor/harbor.yml.bak.20261001-133301。

7.2 回滚触发判据(量化,任一命中即回滚)

  • deploy-harbor 后 /api/v2.0/ping 连续 60 次(≈5 分钟)非 200;
  • 9 个容器未全部 running;
  • 切回默认路由后,上游拉取速率低于原隧道实测值(2.4 KB/s)且持续两轮测量;
  • 4 台节点拉取在切换后 30 分钟内出现持续性失败(非偶发重试)。

7.3 回滚步骤

# ① 工程内回滚配置与实现体(按台账恢复备份文件)
cd /home/rain/Documents/services/harbor
cp -a upstream-direct-20261001/backup/config/default.yaml.bak.20261001-133053 config/default.yaml
cp -a upstream-direct-20261001/backup/utils/harbor_host.py.bak.20261001-133053 utils/harbor_host.py
cp -a upstream-direct-20261001/backup/utils/templates/probe.sh.bak.20261001-133053 utils/templates/probe.sh
cp -a upstream-direct-20261001/backup/utils/templates/harbor_yml.tmpl.bak.20261001-133113 utils/templates/harbor_yml.tmpl

# ② 重新启用隧道并重建容器(会重建 /etc/systemd/system/harbor-upstream-tunnel.service)
.venv/bin/python main.py deploy-harbor

# ③ 恢复远端 harbor.yml(仅当需要回到切换前的确切配置时)
ssh root@192.168.31.31 'cp -a /opt/harbor/harbor.yml.bak.20261001-133301 /opt/harbor/harbor.yml'

7.4 回滚后验证

ssh root@192.168.31.31 'systemctl is-active harbor-upstream-tunnel; ss -lnt | grep -c ":1080 "'
ssh root@192.168.31.31 'docker inspect harbor-core --format "{{range .Config.Env}}{{println .}}{{end}}" | grep HTTPS_PROXY'
curl -sk -o /dev/null -w '%{http_code}\n' https://192.168.31.31/api/v2.0/ping    # 期望 200

8. 风险说明

风险点 说明 缓解
🔴 重建容器导致 Harbor 短暂不可用 install.sh 重建全部容器,节点拉取会失败一次 已在本窗口执行完毕;节点侧 containerd 自动重试,无需干预
🟠 出口改为 Clash 后开始消耗机场流量 隧道原走自建香港 VPS,不占机场套餐 已向用户说明;可由 upstream_proxy.enabled: true 改回
🟠 Clash 主组若切到低带宽节点,上游会变慢 主组由 speed_select.rb 每 5 分钟巡检 本次期间实测发生一次自动切换(泰国-HY2-48 不可达 → 美国-HY2-35),切换后各源仍 4~7 MB/s
🟡 禁用后若不做清理,会留下孤儿 systemd 单元 旧实现禁用时把该步骤判为 done,不清理 已在实现体中改为「有残留即判 pending 并清理」,并新增 tunnel_unit 探测项
🟡 harbor 步 DNS 判定读 HostConfig.Dns 会恒判 pending daemon 级默认 DNS 下该字段为 null,导致每次重跑都重建容器 已改读容器 resolv.conf 的 ExtServers;修复后复跑零变更
🟡 Harbor 远端 registry 重建后短暂 unhealthy 重建期间健康检查失败被标记,proxy cache 直接 404 ~ 5 分钟 后台健康检查每 5 分钟一轮,自动恢复;不要在恢复前跑 warmup(会连报失败触发阈值停止)
🟡 quay.io/minio/minio 该 tag 上游不可得 集群实际在用,但上游已删/不可访问 已记录为已知例外;集群节点本地已有该镜像,不影响运行

附录:交付物清单

文件 用途
SOP.md 本文件:变更实施、验证与回滚
deploy-harbor.log 正式执行的完整日志
warmup.log 预热执行日志(含 51 成功 / 3 失败明细)
backup/README.md 备份台账
backup/** 本次改动的原文件备份

来源:/home/rain/Documents/services/harbor/upstream-direct-20261001/SOP.md | 最后更新:2026-10-01 14:31(由 build/sync.py 同步,请勿在站点侧修改)