社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  docker

Docker Hub 限速时代:Spegel 无状态缓存如何实现离线镜像共享

新一代智能化应用 • 2 月前 • 94 次点击  

TL;DR

Spegel[1] 是一个非常有意思的项目,可以帮助我们在 Kubernetes 集群中实现镜像共享,提高镜像拉取的速度,减少对外部镜像仓库的依赖。对于一些离线或者内网环境、带宽优化和成本控制、容灾和高可用等场景,尤其是绕过 Docker Hub 镜像拉取限制方面,Spegel 都是一个不错的选择。

背景

Docker Hub 的限制越来越严[2],从今天的 4 月 1 日起,未经身份验证的用户每小时最多拉取 10 次镜像,并且是基于 IP 地址或 IPv6 子网限制,这意味着在一个局域网中多个用户共用一个公网 IP 的情况下,这个限制会更加严格。如果收到 429 Too Many Requests 响应,说明已经超过了限制,拉取请求被限流。

Docker Hub 多年来一直免费,但是随着用户数量的增加,成本也在增加,为了保证服务的稳定性,Docker Hub 也需要收费,这是一个必然的趋势。毕竟,运营如此全球规模的服务,维护成本是非常高的。天下没有免费的午餐。

但是对于一些个人开发者或者小团队来说,这样的限制可能会影响到他们的日常开发。

如何避免被限流

1. 登录 Docker Hub

最简单的方法,登录 Docker Hub 可以提高拉取镜像的次数,每小时 100 次。这对于个人开发者来说应该是足够的了。

如果是 Kubernetes 用户,可以通过 创建 Docker Hub 的 Secret[3] 来实现。

kubectl create secret docker-registry dockerhub --docker-server=https://index.docker.io/v1/ --docker-username=USER_NAME --docker-password=PASSWORD

2. 使用其他的镜像仓库

Docker Hub 并不是唯一的镜像仓库,还有很多其他的镜像仓库也提供了免费的服务,但是可能不会有 Docker Hub 那么多的镜像。比如:

  • Quay.io[4]:Red Hat 出品的镜像仓库,安全性高,支持私有仓库,但镜像相对较少。
  • GHCR[5]:GitHub Container Registry,GitHub 提供的镜像仓库服务,可以通过 GitHub Actions 来构建镜像并推送到 GHCR。
  • 云厂商产品:比如阿里云、腾讯云、华为云、Azure、GCP、AWS 等,都提供了镜像仓库服务,可以通过这些镜像仓库拉取镜像,适合公有云上的生态。

3. 自建镜像仓库

自建镜像仓库是最好的方法,可以完全控制镜像的拉取和推送,不受限制。比如:

  • Harbor[6]:CNCF 项目,VMware 开源的企业级仓库,支持 RBAC、镜像扫描、审计日志等,功能强大,安全性高。
  • Nexus Repository:Sonatype 出品的仓库,功能强大,支持 Maven、Docker、NPM 等多种仓库。
  • Docker Registry:Docker 官方的镜像仓库,可以自建,但是功能较弱,没有 UI(可与 docker-registry-browser 配合提供可视化),非常适合个人开发和测试环境。

4. 避免使用 latest 标签

使用固定的版本,而非使用 latest tag,可以避免因为 latest tag 指向的镜像发生变化而导致的频繁拉取。

5. 非必要避免使用 Always 拉取策略

这里说的是 Kubernetes 下的 imagePullPolicy: Always,如果不是必要,可以改为 IfNotPresent 甚至是 Never,避免频繁拉取镜像。这也是正确的镜像使用姿势。

6. 使用集群本地缓存

使用集群本地镜像缓存是个折中的方案,既不脱离 Docker Hub,又可以避免频繁拉取镜像被限流。复用已拉取的镜像,可以避免频繁拉取镜像,还可以提高镜像的拉取速度。当 Docker Hub 不可用时(比如网络、服务中断等原因),本地缓存可以提供镜像,保证服务的连续性。

这也是我们今天要介绍的方案,使用 Spegel[7] 无状态镜像缓存绕过 Docker Hub 的新速率限制。

Spegel 无状态镜像缓存

什么是 Spegel

Spegel,瑞典语中的“镜子”,是一个专为 Kubernetes 集群设计的无状态本地 OCI 注册表镜像工具,旨在优化镜像拉取效率并减少对外部镜像仓库的依赖。使用 P2p 协议,支持集群内镜像共享机制,可以在集群内节点之间共享镜像,提高镜像拉取速度。适合离线或内网环境、带宽优化和成本控制、容灾和高可用等场景。

Spegel 的核心功能:

  1. 集群内镜像共享机制:Spegel 允许 Kubernetes 集群中的每个节点充当本地镜像仓库,当某个节点首次拉取镜像后,其他节点可直接从该节点获取镜像,无需重复访问外部仓库 。这种点对点(P2P)共享机制显著减少了跨网络拉取的开销,提升了工作负载的启动速度。
  2. 无状态设计与高兼容性:作为无状态服务,Spegel 不依赖持久化存储,仅通过节点间通信实现镜像分发。它兼容主流的容器运行时(如 Containerd)。
  3. 灵活镜像源配置:支持配置公共或私有镜像仓库的镜像共享。

Spegel 的工作原理

这里借用官方的一张图来说明 Spegel 的工作原理。

Speqel 有三个组件:注册表(Registry)、路由和发现组件以及广告机制。

  1. Spegel 不依赖中心化的镜像存储,而是利用集群中每个节点的本地存储作为临时缓存。当某个节点首次从外部仓库(如 Docker Hub)拉取镜像时,该镜像会缓存在本地,记录在注册表中。
  2. 节点通过分布式哈希表(DHT,Spegel 使用 Kademlia 分布式哈希表的 Go 实现[8] 广播注册表,并记录哪些节点缓存了特定镜像。当新节点请求镜像时,可以快速定位到可用节点进行拉取 。优先从集群内的节点拉取,如果没有则从外部仓库拉取。
  3. Spegel 与容器运行时(目前仅支持 containerd)集成。通过配置 Containerd 注册表镜像[9] 将 Spegel 注册为镜像仓库的“镜像端点”(mirror endpoint),从而拦截默认的镜像拉取请求。

Spegel 的兼容性

Spegel 已在以下 Kubernetes 发行版上进行了兼容性测试。绿色状态表示 Spegel 可立即使用,黄色表示需要额外配置,红色表示 Spegel 无法使用。

StatusDistribution
🟢AKS[10]
🟢Minikube[11]
🟡EKS[12]
🟡K3S[13] and RKE2[14]
🟡Kind[15]
🟡Talos[16]
🟡VKE[17]
🔴GKE[18]
🔴DigitalOcean[19]

K3s 与 Spegel

K3s 的内置注册表镜像[20] (Embedded Registry Mirror)是从 2024 年 1 月发布的实验性功能开始提供的:v1.26.13+k3s1、v1.27.10+k3s1、v1.28.6+k3s1、v1.29.1+k3s1,从 2024 年 12 月发布的版本开始提供 GA 版本:v1.29.12+k3s1、v1.30.8+k3s1、v1.31.4+k3s1。

K3s 内嵌了 Spegel 的功能,可以直接在 K3s 集群中使用 Spegel,无需额外安装部署。

启动 K3s server 时通过参数 --embedded-registry 启动,或者在配置文件中设置 embedded-registry: true。一旦开启,在所有的节点上都会开启两个端口 644350016443 作为本地 OCI 库的端口,5001 做节点间点对点广播可用镜像的端口。

可以通过环境变量 K3S_P2P_PORT 设置 5001 以外的端口。所有节点上的端口设置必须一致,不支持也不推荐在设置后再修改。

接下来,我们演示下在 K3S 集群上部署 Spegel,实现集群内镜像共享。

演示

演示需要至少两台虚拟机:1 台 server 节点(master),1 台 agent 节点(member1)。

1. 配置 Containerd 注册表镜像

在 K3s 上配置 Containerd 注册表镜像,通过 /etc/rancher/k3s/registries.yaml 文件配置。需要在所有的节点上配置。

在两个节点上都执行以下命令,所有节点都会优先从本地和其他节点拉取 docker.ioregistry.k8s.io 的镜像:

sudo mkdir -p /etc/rancher/k3s/
sudo tee /etc/rancher/k3s/registries.yaml <mirrors:   
  docker.io:  
  registry.k8s.io:
EOF

2. 创建集群

这里推荐我之前写的 1 分钟快速搭建指南[21],最新的脚本已经更新到了 k3s-cluster-automation[22]。将脚本下载到本地,执行以下命令:

export HOSTS="13.75.122.224 52.175.36.37"

setupk3s \
    --k3s-version v1.29.12+k3s1 \
    --embedded-registry \
    --mini

你可能会在 K3s 节点上看到如下日志,不过不用担心,这是因为 Kademlia DHT 需要至少 20 个节点才能形成稳定拓扑。

Mar 01 12:04:54 master k3s[21794]: 2025-03-01T12:04:54.302Z        WARN        dht        go-libp2p-kad-dht@v0.25.2/lookup.go:43        network size estimator track peers: expected bucket size number of peers

3. 准备测试镜像

在部署应用时,我们需要一个 Docker Hub 不存在的镜像,提前将其保存到 server 节点上。这里使用 nginx 镜像,将其 tag 成其他的名字:

sudo crictl pull docker.io/library/nginx:1.27.4
sudo ctr -n k8s.io images tag docker.io/library/nginx:1.27.4 docker.io/library/nginx-not-exist:1.27.4

检查镜像是否成功 tag:

# server 节点: master
sudo crictl img list | grep nginx-not-exist
docker.io/library/nginx-not-exist            1.27.4              b52e0b094bc0e       72.2MB

这个镜像只存在于 server 节点,在 agent 节点上并不存在

4. 部署应用

使用该镜像部署一个简单的 Deployment:



    
kubectl apply -f - <apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx-not-exist:1.27.4
        ports:
        - containerPort: 80 
EOF

查看 Pod 的状态,发现其被调度到 agent 节点 member1 上,并成功运行:

kubectl get po -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP          NODE      NOMINATED NODE   READINESS GATES
nginx-74ddfd8c7d-8cn4v   1/1     Running   0          11s   10.42.1.2   member1              

通过 kubectl describe 查看 Pod 的详细信息,可以看到因为是从 server 节点拉取的镜像,拉取非常快:

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  66s   default-scheduler  Successfully assigned default/nginx-74ddfd8c7d-8cn4v to member1
  Normal  Pulling    66s   kubelet            Pulling image "nginx-not-exist:1.27.4"
  Normal  Pulled     63s   kubelet            Successfully pulled image "nginx-not-exist:1.27.4" in 3.323s (3.323s including waiting)
  Normal  Created    63s   kubelet            Created container nginx
  Normal  Started    63s   kubelet            Started container nginx
参考资料
[1]

Spegel: https://spegel.dev/

[2]

Docker Hub 的限制越来越严: https://docs.docker.com/docker-hub/usage/

[3]

创建 Docker Hub 的 Secret: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/

[4]

Quay.io: https://quay.io/

[5]

GHCR: https://ghcr.io

[6]

Harbor: https://www.cncf.io/projects/harbor/

[7]

Spegel: https://spegel.dev/

[8]

Kademlia 分布式哈希表的 Go 实现: https://github.com/libp2p/go-libp2p-kad-dht

[9]

Containerd 注册表镜像: https://github.com/containerd/containerd/blob/main/docs/hosts.md#cri

[10]

AKS: https://azure.microsoft.com/en-us/products/kubernetes-service

[11]

Minikube: https://minikube.sigs.k8s.io/docs/

[12]

EKS: https://aws.amazon.com/eks/

[13]

K3S: https://k3s.io/

[14]

RKE2: https://docs.rke2.io/

[15]

Kind: https://kind.sigs.k8s.io/

[16]

Talos: https://www.talos.dev/

[17]

VKE: https://www.volcengine.com/product/vke

[18]

GKE: https://cloud.google.com/kubernetes-engine

[19]

DigitalOcean: https://www.digitalocean.com/products/kubernetes

[20]

K3s 的内置注册表镜像: https://docs.k3s.io/installation/registry-mirror

[21]

1 分钟快速搭建指南: https://atbug.com/setup-k3s-cluster-with-k3sup-in-one-minute/

[22]

k3s-cluster-automation: https://github.com/addozhang/k3s-cluster-automation


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/179714
 
94 次点击