一、 背景与痛点

在进行大规模 AI 平台开发(如构建大模型智能底座或 SaaS 平台)时,集群运维与研发团队通常面临以下挑战:

  1. 外网带宽与重连压力:AI 相关镜像(如 sglangPyTorchCUDA)体积巨大,动辄 10GB-30GB。在国内环境下,从 Docker Hub 拉取极易遇到 TLS handshake timeout 或速度过慢的问题。
  2. 重复带宽浪费:在一个多节点的 GPU 集群中,如果每台服务器都去外网拉取同一个镜像,不仅浪费出口带宽,还极易触发公网 Docker Hub 的访问频率限制(Rate Limit)。
  3. 磁盘空间危机:随着项目的快速迭代,由于 Docker 镜像的分层特性,那些被覆盖的旧标签(Untagged/Dangling Images)会堆积在本地仓库中,迅速耗尽物理机的高速磁盘。

二、 架构设计:读写分离与智能路由

由于 Docker 客户端 API 的设计机制,Nexus 的 Group(组合)仓库不支持直接发起 docker push 请求。因此,我们采用业界标准的**“读写端口分离”**架构:

  • 端口 5000 (Hosted 仓库):作为**“私有写入口”**。仅用于研发人员推送(Push)内部自研镜像,数据绝对安全保密。
  • 不暴露端口 (Proxy 仓库):作为**“外部缓存器”**。透明代理公网 Docker Hub,自动下载并本地缓存公网的大模型镜像。
  • 端口 5001 (Group 仓库):作为**“全集群统一读入口”**。将 Hosted 和 Proxy 组合在一起。节点拉取(Pull)镜像时只需访问 5001 端口,Nexus 会智能按照 私有库 -> 本地缓存 -> 公网 的顺序寻找并瞬间返回镜像。

三、 服务端部署与配置 (集群管理员操作)

1. 编写 docker-compose.yml 核心编排文件

在主管理节点创建基础设施目录,并编写纯净版的 Nexus 启动配置文件。

version: '3.8'

services:
  # Nexus 3 制品库核心
  nexus:
    image: sonatype/nexus3:latest
    container_name: dgx-nexus
    restart: always
    ports:
      - "8081:8081" # Web 管理界面
      - "5000:5000" # 私有镜像推送端口 (Write/Hosted)
      - "5001:5001" # 组合镜像拉取端口 (Read/Group)
    volumes:
      # 数据目录务必挂载到大容量的高速 RAID 阵列上
      - ./nexus-data:/nexus-data
    environment:
      # 针对大内存服务器,建议分配至少 4GB 堆内存
      - INSTALL4J_ADD_VM_PARAMS=-Xms4g -Xmx4g -XX:MaxDirectMemorySize=4g

2. 目录权限修正与服务启动

Nexus 容器内部服务默认以 UID 200 运行,必须在宿主机手动赋予数据卷权限,否则会导致 Permission denied 报错。

mkdir -p ./nexus-data
sudo chown -R 200:200 ./nexus-data
docker compose up -d

3. Nexus 仓库初始化与高级路由配置

  1. 登录:访问 http://服务器IP:8081。获取初始密码:sudo cat ./nexus-data/admin.password
  2. 第一步:创建私有库 (Hosted)
  • 进入 Settings -> Repositories -> Create repository -> docker (hosted)
  • Name 设为 dgx-private,并在 HTTP Connector 勾选并填入 5000
  1. 第二步:创建代理缓存库 (Proxy)
  • 创建 docker (proxy)。Name 设为 docker-hub-proxy
  • HTTP Connector:留空(不填写)
  • Remote Storage 填写 https://registry-1.docker.io,并勾选 Use Docker Hub
  1. 第三步:创建大一统组合库 (Group)
  • 创建 docker (group)。Name 设为 dgx-unified-group
  • **HTTP Connector:勾选并填入 5001**
  • Member repositories:将 dgx-privatedocker-hub-proxy 移入右侧列表。
  • ⚠️ 核心关键:使用上下箭头,确保 dgx-private 排在第一位!这决定了 Nexus 会优先从内网私有库匹配镜像。

4. 核心优化:配置 5001 端口免密拉取 (匿名访问)

为了让集群内的所有节点在部署时无需繁琐地输入密码(特别利于自动化脚本),我们需要开启 Docker 的匿名拉取权限。请放心,私有的 5000 写入端口依然受到密码保护。

  1. 激活 Docker 令牌机制:进入 Security -> Realms,将 Docker Bearer Token Realm 移到右侧的 Active 列表中,保存。
  2. 开启全局匿名访问:进入 Security -> Anonymous,勾选 Allow anonymous users to access the server,保存。
  3. 放行仓库拉取权限:进入 Repository -> Repositories,分别点开刚才创建的 dgx-unified-groupdocker-hub-proxydgx-private 三个仓库。在它们的设置页面下方找到 Docker Registry API Support,勾选 Allow anonymous docker pull,保存。

四、 集群节点接入配置(跨服务器访问)

为了避免后续主服务器 IP 变动导致整个集群的脚本失效,我们统一使用内部域名 dgx-hub.local 代替 IP。在集群中的每一台计算服务器上,执行以下配置:

1. 配置本地 DNS (Host)

# 将 192.168.x.x 替换为主仓库节点的实际内网 IP
sudo sh -c 'echo "192.168.x.x   dgx-hub.local" >> /etc/hosts'

2. 配置 Docker 引擎信任列表

编辑 /etc/docker/daemon.json,信任我们的私有域名和端口:

{
  "insecure-registries": [
    "dgx-hub.local:5000",
    "dgx-hub.local:5001"
  ]
}

执行重启使配置生效:sudo systemctl daemon-reload && sudo systemctl restart docker


五、 开发者使用规范与实战演练

接入完成后,请团队成员严格遵守**“5000 推,5001 拉”**的核心工作流。

场景 A:通过 5001 端口免密加速拉取外部镜像

5001 端口已配置为匿名拉取,任何节点可直接获取镜像。

⚠️ 核心避坑指南(关于官方顶层镜像):
当我们通过 Nexus 代理拉取 Docker Hub 的官方顶层镜像(如 nginxubuntu 等名字里没有斜杠的镜像)时,必须手动补全 library/ 命名空间,否则会报错 manifest unknown

1. 拉取官方基础镜像(需加 library/):

# 第一次拉取 Nexus 会去外网下载并缓存,后续拉取将跑满内网带宽实现秒传
docker pull dgx-hub.local:5001/library/nginx:latest

2. 拉取第三方开源/AI 镜像(直接拉取,无需修改):
对于带有组织名的镜像(如大模型底座),直接使用即可:

docker pull dgx-hub.local:5001/lmsysorg/sglang:dev-cu13

场景 B:将自研业务推送到 5000 私有仓库

为了保证数据安全,5000 端口严禁匿名写入。向私有库推送镜像时,必须遵循“打标 -> 登录 -> 推送”的标准三步走流程。以内部系统(如 System-Mate 或 Cloudlong 业务)为例:

第 1 步:为镜像打上 5000 端口专属标签
告诉 Docker 我们要将这个镜像送往安全的写入端口:

docker tag system-mate-backend:v1 dgx-hub.local:5000/system-mate-backend:v1

第 2 步:登录私有金库(需进行身份校验)
直接 push 会报 unauthorized 错误,该机器首次推送前必须登录:

$ docker login dgx-hub.local:5000
Username: admin
Password: [输入管理员密码]
# 提示 Login Succeeded 即代表授权成功

第 3 步:正式推送入库
携带凭证后,即可极速推送到私有仓库:

$ docker push dgx-hub.local:5000/system-mate-backend:v1
The push refers to repository [dgx-hub.local:5000/system-mate-backend]
...
latest: digest: sha256:... size: 1778

场景 C:其他节点部署内部应用

你在 A 节点推送到 5000 端口后,B 节点拉取部署时,依然使用 5001 读端口(无需鉴权):

docker pull dgx-hub.local:5001/system-mate-backend:v1


六、 最佳实践:Nexus 镜像清理与空间释放策略 (Garbage Collection)

许多开发者在使用 Nexus 时会发现,即使在 UI 界面手动删除了某个镜像,服务器的物理磁盘空间依然没有减少。这是因为 Nexus 的 Docker 垃圾回收机制必须遵循特定的工作流。为了防止高频迭代项目的旧镜像撑爆磁盘,管理员必须在 Nexus 中按顺序设置以下三大自动化任务:

1. 创建清理策略 (Cleanup Policies)

策略用于界定“哪些镜像被视为过期可以删除”。

  1. 进入 Settings -> Repository -> Cleanup Policies
  2. 点击 Create Cleanup Policy。Format 选择 docker
  3. 设定规则:例如,填写 Published Before: 15 days(清理 15 天前发布的版本),同时可以在 Regex 中填写白名单排除特定标签(例如 latest )。
  4. 回到 dgx-private 仓库设置中,将这个策略应用到该仓库上。

2. 配置自动化调度任务 (Tasks)

仅有策略是不够的,您必须在 Settings -> System -> Tasks 中创建以下三个定时任务,且执行顺序极其重要

  1. Admin - Cleanup repositories using their associated policies
  • 作用:根据第一步的策略,将过期的镜像执行“软删除”(Soft delete)。
  • 频率:建议设为每天凌晨 02:00
  1. Docker - Delete unused manifests and images
  • 作用:扫描 Docker 仓库,寻找那些不再被任何 Tag 引用的“悬空清单”(Dangling Manifests)并清理它们。
  • 频率:建议设为每天凌晨 03:00
  1. Admin - Compact blob store
  • 作用:这是唯一能真正把物理硬盘空间还给宿主机的终极步骤。它会遍历底层的 Blob 存储,物理擦除那些经过软删除后遗留下来的数据块。
  • 频率:建议设为每天凌晨 04:00

只有完成了这三步的完整闭环,您的自建 Docker 仓库才能实现真正的“自我瘦身”,一劳永逸地解决大模型镜像带来的存储压力。


七、 总结

通过上述方案,您的 GPU 集群不仅获得了内网极速的镜像分发能力,还依托 Nexus 的 Proxy 缓存机制成功跨越了拉取外部大模型镜像时的网络鸿沟。结合域名解析、读写分离的规范流转,以及专业的 Garbage Collection 策略,整套基础设施真正达到了生产可用、免维护的状态。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐