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. 目标¶
- Harbor 组件的上游出网不再经隧道,改走 Harbor 主机默认路由(旁路由 Clash);
- 隧道残留被清理干净:systemd 单元删除、服务停止、1080/tcp 防火墙放行撤销;
- 该结论可被复跑验证:
deploy-harbor二次执行零变更(幂等); - 存量镜像预热(58 条)完成到「仅上游确实不可得者除外」;
- 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同步,请勿在站点侧修改)