RUNBOOK — Harbor 内网镜像仓库(harbor-01)¶
服务地址:
https://192.168.31.31| 主机:harbor-01(192.168.31.31) 日常运维就照这份做;变更历史看SOP.md,当前进度与遗留项看HARBOR-RESULT.md。
1. 服务概览¶
| 项 | 内容 |
|---|---|
| 用途 | 5 个海外镜像源的内网代理缓存:ghcr.io、registry.k8s.io、quay.io、registry.gitlab.com、docker.io |
| 组成 | Harbor v2.15.2,9 个容器:harbor-core / harbor-portal / harbor-jobservice / registry / registryctl / harbor-db(PostgreSQL) / redis / nginx / harbor-log |
| 部署方式 | Docker CE + compose,安装目录 /opt/harbor;不是 k8s 内工作负载 |
| 数据 | /data/harbor(数据盘 /dev/vdb,200 G);Docker 镜像层在 /data/docker |
| 依赖 | 上游 5 个公网源;出网走 Harbor 主机默认路由(→ 旁路由 192.168.31.2 Clash)。香港跳板 SOCKS5 隧道已于 2026-10-01 下线(见 SOP.md) |
| 消费者 | 4 台 k8s 节点(.10/.13/.14/.15)经 containerd certs.d 接入 |
| 异常时的业务影响 | 节点拉取新镜像会回落直连海外(慢);已在节点本地的镜像不受影响,运行中 Pod 不受影响 |
| 重要性 | 中——影响部署与扩容速度,不影响存量业务运行 |
重要认知:Harbor 只影响「拉新镜像」。节点上的既有镜像与运行中 Pod 与它无耦合。
2. 接入与凭据¶
| 项 | 值 |
|---|---|
| Web / API | https://192.168.31.31(自签证书,浏览器需信任;curl 用 -k) |
| 账号 | admin(唯一账号) |
| 口令位置 | 192.168.31.31:/root/.secrets/harbor-admin.pass(600);数据库口令同在 /root/.secrets/harbor-db.pass |
| API 免口令调用 | 192.168.31.31:/root/.secrets/harbor.netrc(600),curl -sk --netrc-file <该文件> |
| 取用口令(不打印) | ssh root@192.168.31.31 'cat /root/.secrets/harbor-admin.pass'(仅在需要的终端里用,不要贴进任何文档或日志) |
| Harbor 主机登录 | 操作机 root 免密 ssh |
节点侧客户端配置片段(已就位,此处供新建节点参考):
# /etc/containerd/certs.d/ghcr.io/hosts.toml
server = "https://192.168.31.31"
[host."https://192.168.31.31"]
capabilities = ["pull", "resolve"]
ca = "/etc/containerd/certs.d/ghcr.io/ca.crt"
override_path = "/v2/ghcr.io"
ca.crt 同目录,内容为 Harbor 主机的 /data/harbor/certs/ca.crt。
3. 日常速查表¶
# 1) Harbor 整体状态(预期 9 行 Up (healthy))
ssh root@192.168.31.31 'docker ps --format "{{.Names}}\t{{.Status}}"'
# 2) Harbor API 存活(预期 200)
curl -sk -o /dev/null -w '%{http_code}\n' https://192.168.31.31/api/v2.0/ping
# 3) 5 个 proxy 项目是否齐(预期 5 行)
curl -sk --netrc-file <(ssh root@192.168.31.31 'cat /root/.secrets/harbor.netrc') \
'https://192.168.31.31/api/v2.0/projects?page_size=100' | python3 -c 'import sys,json;print("\n".join(p["name"] for p in json.load(sys.stdin)))'
# 4) 上游出网(预期:隧道单元已删除、1080 无监听)
ssh root@192.168.31.31 '[ -f /etc/systemd/system/harbor-upstream-tunnel.service ] && echo "隧道单元仍在(不期望)" || echo "隧道已下线(正常)"; ss -lnt | grep -c ":1080 "'
# 5) 4 台节点接入文件是否齐(预期每台 5 个 yes)
for ip in 192.168.31.10 192.168.31.13 192.168.31.14 192.168.31.15; do
echo -n "$ip: "; ssh root@$ip 'ls /etc/containerd/certs.d/ 2>/dev/null | tr "\n" " "; echo'
done
# 6) Harbor 存储占用
ssh root@192.168.31.31 'du -sh /data/harbor/registry; df -h /data | tail -1'
# 7) Harbor 主机磁盘
ssh root@192.168.31.31 'df -h / /data'
# 8) 各上游源已缓存多少仓库
ssh root@192.168.31.31 'for p in ghcr.io quay.io registry.k8s.io registry.gitlab.com docker.io; do echo -n "$p: "; ls /data/harbor/registry/docker/registry/v2/repositories/$p 2>/dev/null | wc -l; done'
# 9) 重启 Harbor(不影响节点已有镜像)
ssh root@192.168.31.31 'cd /opt/harbor && docker compose restart'
# 10) 停 / 起 Harbor
ssh root@192.168.31.31 'cd /opt/harbor && docker compose down' # 停
ssh root@192.168.31.31 'cd /opt/harbor && docker compose up -d' # 起
4. 日常操作¶
4.1 状态检查¶
见速查表 1~5。判据:9 个容器全 healthy、ping=200、5 个 project 齐、隧道已下线(1080 无监听)。
4.2 启动 / 停止 / 重启¶
ssh root@192.168.31.31 'cd /opt/harbor && docker compose ps'
ssh root@192.168.31.31 'cd /opt/harbor && docker compose restart harbor-core'
ssh root@192.168.31.31 'cd /opt/harbor && docker compose logs -f --tail=100 harbor-core'
4.3 进入容器 / 主机排障¶
ssh root@192.168.31.31 'docker exec -it harbor-db psql -U postgres -d registry' # 数据库
ssh root@192.168.31.31 'docker logs harbor-core --since 10m 2>&1 | tail -50' # core 日志
ssh root@192.168.31.31 'tail -50 /data/harbor/logs/registry/registry.log' # registry 日志
harbor-core/registry是 distroless 镜像,没有 shell,不能docker exec进去。
4.4 导入新镜像 / 预热¶
日常运维不需要主动导入:Harbor 是代理缓存,节点首次拉取会自动从上游取并缓存。仅在两个场景下需要预热:
# 场景:helm hook 镜像或节点重建前,想先把镜像"喂"进 Harbor
cd /home/rain/Documents/services/harbor
vim config/warmup-images.txt # 加入 <registry>/<repo>:<tag>,每行一条
.venv/bin/python main.py warmup # 幂等:已入库的自动跳过
4.5 扩容(新增节点)¶
新节点接入只需两份文件:
NEW=192.168.31.16
for reg in ghcr.io registry.k8s.io quay.io registry.gitlab.com docker.io; do
ssh root@$NEW "install -d -m 755 /etc/containerd/certs.d/$reg"
ssh root@192.168.31.31 "cat /data/harbor/certs/ca.crt" | ssh root@$NEW "cat > /etc/containerd/certs.d/$reg/ca.crt"
ssh root@$NEW "printf 'server = \"https://192.168.31.31\"\n\n[host.\"https://192.168.31.31\"]\n capabilities = [\"pull\", \"resolve\"]\n ca = \"/etc/containerd/certs.d/$reg/ca.crt\"\n override_path = \"/v2/$reg\"\n' > /etc/containerd/certs.d/$reg/hosts.toml"
done
# 把新节点加进 config/default.yaml 的 nodes.k8s 后重跑:
cd /home/rain/Documents/services/harbor && .venv/bin/python main.py deploy-nodes
4.6 删除节点接入(回滚)¶
ssh root@192.168.31.13 'rm -rf /etc/containerd/certs.d/{ghcr.io,registry.k8s.io,quay.io,registry.gitlab.com,docker.io}'
5. 配置管理¶
| 配置 | 位置 | 修改流程 | 生效方式 |
|---|---|---|---|
| Harbor 主配置 | 192.168.31.31:/opt/harbor/harbor.yml(由工程渲染,勿手改) |
改工程 config/default.yaml → 重跑 main.py deploy-harbor |
重新 install.sh(会重建容器) |
| 工程配置 | /home/rain/Documents/services/harbor/config/default.yaml |
直接编辑;环境差异另建 config/env.yaml 覆盖 |
下次运行子命令即生效 |
| 上游源清单 | config/default.yaml 的 registries |
加/改条目后重跑 deploy-harbor(projects 步骤) |
按条目幂等收敛 |
| 预热清单 | config/warmup-images.txt |
直接编辑 | 下次 warmup 生效 |
| 节点接入内容 | 工程按配置渲染 | 同上 | 重跑 deploy-nodes,内容变化即更新 |
| Docker 守护配置 | 192.168.31.31:/etc/docker/daemon.json |
改工程 harbor.docker_data_root / harbor.docker_dns 后重跑 |
内容变化自动重启 docker;容器 DNS 变化需重建容器(deploy-harbor 会做) |
| 上游出网 | 由 harbor.upstream_proxy.enabled 控制(当前 false,组件走默认路由) |
改该开关后重跑 main.py deploy-harbor |
true:建隧道单元并重建容器;false:停服务 + 删单元 + 撤防火墙放行 |
易错点:
harbor.yml的proxy.no_proxy必须是逗号分隔字符串,写成 YAML 列表会让prepare直接报错(仅在重新启用代理时才会渲染该段)。upstream_proxy.enabled: false时不要手删 unit——重跑deploy-harbor会一致地关闭(停服务 + 删单元 + 撤放行),手删会与幂等判定冲突。- 改工程配置后不要手工编辑远端文件,以免与幂等判定冲突(下次运行会覆盖回去)。
- 容器 DNS 判定读的是容器
resolv.conf的ExtServers——不要改回docker inspect .HostConfig.Dns:该字段在「daemon 级默认 DNS」下为null,会让harbor步恒判 pending、每次重跑都重建容器(2026-10-01 实测踩到)。
6. 备份与恢复¶
| 对象 | 备份策略 | 位置 |
|---|---|---|
| Harbor 数据(镜像层 + 数据库) | 无自动备份。升级 Harbor 前必须手工备份 | /data/harbor(数据盘) |
harbor.yml |
每次变更前自动 | /opt/harbor/harbor.yml.bak.<ts> |
节点 certs.d |
每次变更前自动打包 | <节点>:/root/harbor-nodeaccess-backup/<ts>/certs.d.tar.gz |
| repo / daemon.json | 变更前自动 | 同目录 .bak.<ts> |
| 本次变更总台账 | — | backup/README.md |
手工备份 Harbor 数据:
ssh root@192.168.31.31 'cd /opt/harbor && docker compose stop'
ssh root@192.168.31.31 'tar -czf /data/harbor-backup-$(date +%Y%m%d).tgz -C /data harbor'
ssh root@192.168.31.31 'cd /opt/harbor && docker compose start'
恢复:停栈 → 解包覆盖 /data/harbor → 起栈 → 用速查表 1、2 验证。
恢复演练尚未做过;首次恢复前建议先在非生产时段验证一次。
7. 监控与告警¶
| 关注对象 | 查看入口 | 建议阈值 |
|---|---|---|
| Harbor 存活 | 速查表 2(/api/v2.0/ping) |
非 200 持续 2 分钟 → 告警 |
| 容器健康 | 速查表 1 | 任一容器非 healthy |
| 磁盘 | 速查表 7 | /data 使用率 >85% |
| 上游出网 | 速查表 4 | 隧道应为「已下线」;若被重新启用,则要求 active 且 1080 在听 |
| 上游健康 | docker logs harbor-core --since 10m \| grep unhealthy |
出现 current registry is unhealthy |
接入监控的既有路线:本环境已有 textfile → node_exporter:9100 采集方式,Harbor 指标可沿用该路线(本次未接,见 HARBOR-RESULT.md 遗留项 7)。
日志位置:
ssh root@192.168.31.31 'docker logs harbor-core --since 30m 2>&1 | tail -100' # 应用
ssh root@192.168.31.31 'tail -100 /data/harbor/logs/registry/registry.log' # 仓库
ssh root@192.168.31.31 'journalctl -u harbor-upstream-tunnel -n 50 --no-pager' # 隧道(仅重新启用后才存在)
8. 故障处置¶
场景 1:节点拉镜像仍然很慢 / 仍走海外¶
症状:节点上 ctr -n k8s.io images pull ghcr.io/... 耗时数分钟;docker.io 等仍超时。
诊断
# 1) 节点上接入文件在不在、内容对不对
ssh root@192.168.31.13 'cat /etc/containerd/certs.d/ghcr.io/hosts.toml'
# 预期含 override_path = "/v2/ghcr.io";缺这行就是原因
# 2) containerd 是否读到了(配置目录必须在 config.toml 里声明)
ssh root@192.168.31.13 'grep config_path /etc/containerd/config.toml'
# 预期 config_path = "/etc/containerd/certs.d"
# 3) Harbor 是否活着
curl -sk -o /dev/null -w '%{http_code}\n' https://192.168.31.31/api/v2.0/ping # 预期 200
# 4) Harbor 里到底有没有这个制品
curl -sk --netrc-file <(ssh root@192.168.31.31 'cat /root/.secrets/harbor.netrc') \
'https://192.168.31.31/api/v2.0/projects/ghcr.io/repositories/cloudnative-pg%252Fpostgresql/artifacts/17.6'
# 200=已缓存;404=未缓存,Harbor 会去上游拉(首次必然慢)
处置
| 诊断结论 | 动作 |
|---|---|
hosts.toml 缺失或内容不对 |
cd /home/rain/Documents/services/harbor && .venv/bin/python main.py deploy-nodes |
| 制品 404(首次入库) | 正常现象。想提前喂:在 Harbor 主机 docker pull 192.168.31.31/<project>/<repo>:<tag> |
| Harbor 不可用 | 见场景 3 |
验证:节点上 ctr -n k8s.io images pull <ref>(先 images rm 掉本地)应在数十秒内完成。
场景 2:Harbor 拉不到上游,日志报 registry unhealthy¶
症状:docker logs harbor-core | grep unhealthy 有输出;节点拉未缓存的镜像报 not found。
最常见成因:刚重建过容器。
install.sh重建 Harbor 后,5 个上游 registry 会被重新健康检查, 期间一律标记unhealthy,proxy cache 直接返回 404(并非上游真的不可达)。后台健康检查 每 5 分钟一轮,通常下一轮自动恢复;恢复前不要跑warmup——会连报失败并按失败阈值提前停止 (2026-10-01 实测踩到)。
诊断
# 1) 5 个 registry 健康状态(预期全 healthy)
curl -sk --netrc-file <(ssh root@192.168.31.31 'cat /root/.secrets/harbor.netrc') \
'https://192.168.31.31/api/v2.0/registries?page_size=100' | \
python3 -c 'import sys,json;[print(" id=%s %-22s status=%s"%(r["id"],r["name"],r["status"])) for r in json.load(sys.stdin)]'
# 2) 容器内到上游是否可达(走默认路由 → 旁路由 Clash)
ssh root@192.168.31.31 'docker exec harbor-core curl -s -o /dev/null -m 12 -w "%{http_code}\n" https://ghcr.io/v2/'
# 预期 401(可达);000=不通
# 3) 容器 DNS 是否为公共 DNS(不能解析成 Clash fake-ip 的 198.18.x.x)
ssh root@192.168.31.31 'docker exec harbor-core cat /etc/resolv.conf | grep ExtServers'
# 预期 [223.5.5.5 119.29.29.29]
# 4) 宿主侧上游带宽(默认路由 → Clash)
ssh root@192.168.31.31 'curl -s -m 20 -o /dev/null -w "%{speed_download} B/s\n" https://speed.cloudflare.com/__down?bytes=10485760'
处置
| 诊断结论 | 动作 |
|---|---|
刚重建过容器、registry 全 unhealthy |
等一轮(≤5 分钟)后复查即可;期间不要跑 warmup |
等待后仍 unhealthy,但容器内到上游可达 |
重启 harbor-core 触发新一轮检查:cd /opt/harbor && docker compose restart harbor-core |
| 容器内到上游不通 | 查默认路由与旁路由:ssh root@192.168.31.31 'ip route get 8.8.8.8'(应为 via 192.168.31.2) |
| 带宽显著低于 1 MB/s | 到旁路由 192.168.31.2 看 OpenClash 主组所选节点与 /root/speed_select.log |
| 曾重新启用过隧道且未恢复 | 确认 harbor.upstream_proxy.enabled: false 并重跑 deploy-harbor |
验证:docker logs harbor-core --since 2m | grep unhealthy 不再出现;拉一个未缓存镜像成功。
场景 3:Harbor 容器起不来 / 页面打不开¶
症状:docker ps 有容器非 healthy,或浏览器无法访问 https://192.168.31.31。
诊断
ssh root@192.168.31.31 'cd /opt/harbor && docker compose ps'
ssh root@192.168.31.31 'docker logs harbor-core --tail=50 2>&1'
ssh root@192.168.31.31 'df -h / /data' # 磁盘满是最常见原因
ssh root@192.168.31.31 'ls -Zd /data/harbor' # 预期含 container_file_t
ssh root@192.168.31.31 'curl -sk -o /dev/null -w "%{http_code}\n" https://192.168.31.31/api/v2.0/ping'
处置
| 诊断结论 | 动作 |
|---|---|
/data 使用率满 |
清理旧缓存(Harbor 控制台按项目删制品),或扩 /dev/vdb |
| SELinux 上下文丢失 | semanage fcontext -a -t container_file_t '/data/harbor(/.*)?'; restorecon -R /data/harbor 后 docker compose restart |
| 配置被手改坏 | 重跑 main.py deploy-harbor(会用工程模板重新渲染 harbor.yml) |
| 以上都不是 | cd /opt/harbor && docker compose down && ./install.sh |
验证:速查表 1、2 恢复正常。注意:Harbor 停机期间节点拉新镜像会回落直连(慢),存量 Pod 不受影响。
场景 4:节点 containerd 异常或 Pod 起不来¶
症状:ImagePullBackOff;systemctl status containerd 异常。
诊断
ssh root@192.168.31.13 'systemctl is-active containerd; ls -l /etc/containerd/certs.d/*/'
kubectl get pods -A | grep -Ev 'Running|Completed'
处置:先回滚接入文件(这是最可能的变更点),确认恢复后再排查:
ssh root@192.168.31.13 'rm -rf /etc/containerd/certs.d/{ghcr.io,registry.k8s.io,quay.io,registry.gitlab.com,docker.io}'
ssh root@192.168.31.13 'systemctl restart containerd' # 仅在 containerd 确实异常时才重启
验证:systemctl is-active containerd = active;受影响 Pod 恢复正常。
9. 安全与权限¶
| 项 | 说明 |
|---|---|
| Harbor 账号 | 仅 admin;proxy cache 项目为 public,节点侧拉取无需凭据 |
| 凭据存放 | 192.168.31.31:/root/.secrets/(700),三个文件均 600;不入工程目录、不入文档、不打终端 |
| CA 私钥 | /data/harbor/certs/ca.key(600),不出 Harbor 主机 |
| 上游出网 | 当前无隧道:组件经 Harbor 主机默认路由出网(旁路由 Clash)。曾用隧道监听 0.0.0.0:1080(firewalld 仅放行 172.16.0.0/12 与 127.0.0.1),2026-10-01 已下线 |
| 隧道凭据(仅重新启用时相关) | Harbor 主机 /root/.ssh/id_ed25519 → 香港跳板 authorized_keys;/usr/local/sbin/harbor-ssh 保留未删 |
| 证书轮换 | 见 SOP.md 第 3 节:重跑 deploy-harbor(证书步骤)→ 再重跑 deploy-nodes 分发新 CA |
| 访问控制 | 节点侧无新增账户;操作需 root(SSH 免密) |
10. 容量与性能¶
| 项 | 当前值 |
|---|---|
/data 容量 / 余量 |
200 G / 约 198 G |
| Harbor 存储占用 | 4.9 GB(58 条清单已入库 57 条) |
| k8s 集群镜像总量参考 | 单节点 containerd 存储约 15 G |
| 上游拉取速度(2026-10-01 实测) | 经默认路由(Clash)各源镜像层:ghcr 4.40、registry.k8s.io 5.70、quay 6.86、docker.io 1.28 MB/s;同日香港隧道仅 2.4 KB/s(已下线) |
| 内网拉取速度 | 节点 ← Harbor 命中缓存后 40~47 MB/s |
扩容触发条件:/data 使用率 >85% → 删旧缓存或扩盘。
已知瓶颈:上游链路,速度取决于旁路由 Clash 当前所选节点(由 speed_select.rb 每 5 分钟巡检选优)。可选手段:Harbor 侧并发拉取(warmup.concurrency,默认 4)。若未来重新启用香港隧道:其瓶颈在「家宽 → 香港」这条链路(2026-10-01 实测丢包 13.3%、仅 2.4 KB/s),不在香港主机性能上(该主机直连 cloudflare 实测 41 MB/s)。
11. 附录¶
| 文件 | 说明 |
|---|---|
SOP.md |
变更实施、验证与回滚(含全部踩坑) |
HARBOR-RESULT.md |
结果回填与遗留项 |
backup/README.md |
备份台账 |
/home/rain/Documents/services/harbor/ |
实现体工程;main.py --help 看子命令 |
HARBOR-HANDOFF.md |
原始需求规格书 |
修订记录
| 日期 | 变更 |
|---|---|
| 2026-10-01 | 首次建立(随 Harbor 部署与节点接入变更交付) |
| 2026-10-01 | 上游加速隧道下线、组件改走默认路由:修订第 1(依赖)、3(速查表 4)、4.1(判据)、5(配置管理与易错点)、7(监控与日志)、8(场景 2 重写)、9(安全)、10(容量与性能)各节 |
来源:
/home/rain/Documents/k8s_ins/ghcr-slow-20261001/RUNBOOK.md| 最后更新:2026-10-01 14:33(由build/sync.py同步,请勿在站点侧修改)