内部Docker Registry 配置 Pull-Through Cache节省带宽

将一个内部 Docker Distribution Registry 配置成 Docker Hub 的 pull-through cache,让集群节点优先从内部地址拉取镜像。内部 docker-registry 本地没有对应镜像时,会从 Docker Hub 回源拉取并缓存,后续请求直接命中本地缓存。

复制代码
内部 docker-registry: registry.internal:5000
上游 Docker Hub: https://registry-1.docker.io

主要配置

Pull-through cache 分两层配置:

  1. Registry 服务端配置:让内部 docker-registry 具备回源缓存能力。
  2. Kubernetes 节点配置:让 containerd/Docker 把 Docker Hub 镜像请求转发到内部 docker-registry。

Kubernetes 本身不会读取 registry 的配置文件。Pod 拉镜像时最终由每个节点上的容器运行时处理,所以所有需要拉镜像的节点都要配置 mirror。

Registry 侧配置

Docker Distribution registry 通常通过配置文件启动。二进制部署常见命令类似:

bash 复制代码
/usr/bin/registry serve /etc/registry/registry_config.yml

容器部署时通常把配置挂载到:

bash 复制代码
/etc/docker/registry/config.yml

无论哪种部署方式,核心都是修改 registry 的配置文件。

备份配置

bash 复制代码
cp /etc/registry/registry_config.yml /etc/registry/registry_config.yml.bak.$(date +%F-%H%M%S)

如果你的 registry 容器使用的是 /etc/docker/registry/config.yml,备份对应文件。

启用 pull-through cache

在配置中增加或修改 proxy.remoteurl

yaml 复制代码
proxy:
  remoteurl: https://registry-1.docker.io
  ttl: 168h

完整示例:

yaml 复制代码
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
http:
  addr: :5000
  debug:
    addr: :5001
    prometheus:
      enabled: true
      path: /metrics
  headers:
    X-Content-Type-Options: [nosniff]
proxy:
  remoteurl: https://registry-1.docker.io
  ttl: 168h
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
auth:
  htpasswd:
    realm: "Registry Realm"
    path: /etc/registry/registry_htpasswd

如果上游 Docker Hub 需要认证,增加账号配置:

yaml 复制代码
proxy:
  remoteurl: https://registry-1.docker.io
  username: <dockerhub-username>
  password: <dockerhub-password-or-token>
  ttl: 168h

说明:

  • remoteurl 是启用 pull-through cache 的关键字段。
  • ttl: 168h 表示缓存保留 7 天;过期后可被清理,再次拉取会重新回源。
  • storage.delete.enabled: true 建议保留,否则过期内容可能无法清理。
  • 一个 registry mirror 只能代理一个上游。https://registry-1.docker.io 只代理 Docker Hub,不能同时代理 quay.ioghcr.ioregistry.k8s.io
  • 如果要代理多个上游,建议分别部署多个 registry mirror。

重启 registry

systemd 二进制部署:

bash 复制代码
systemctl restart registry
systemctl status registry -l
journalctl -u registry -n 100 --no-pager

Docker 容器部署:

bash 复制代码
docker restart <registry-container-name>
docker logs --tail=100 <registry-container-name>

containerd/nerdctl 容器部署:

bash 复制代码
nerdctl restart <registry-container-name>
nerdctl logs --tail=100 <registry-container-name>

Kubernetes 部署:

bash 复制代码
kubectl -n <namespace> rollout restart deployment/<registry-deployment>
# 或 StatefulSet:
kubectl -n <namespace> rollout restart statefulset/<registry-statefulset>

Registry 侧验证

确认端口:

bash 复制代码
ss -ltnp | grep ':5000'

确认 API 可访问:

bash 复制代码
curl http://127.0.0.1:5000/v2/

如果 registry 开启了 htpasswd:

bash 复制代码
curl -u admin:<password> http://127.0.0.1:5000/v2/

测试拉取 Docker Hub 镜像:

bash 复制代码
docker pull <内部docker-registry地址>/library/busybox:latest

或:

bash 复制代码
nerdctl pull <内部docker-registry地址>/library/busybox:latest

或:

bash 复制代码
crictl pull --creds admin:<password> <内部docker-registry地址>/library/busybox:latest

或者可以用skneo或者crane等镜像仓库工具来验证。

Kubernetes 节点侧配置

Kubernetes Pod 默认写 busybox:latestnginx:latest 这类 Docker Hub 镜像名时,实际拉取由节点上的 containerd/Docker 完成。要让 Pod 自动走内部缓存,需要配置每个节点的容器运行时。

containerd hosts.toml 配置

适用于使用 /etc/containerd/certs.d 的 containerd 版本。

确认 /etc/containerd/config.toml 中启用了 config_path

toml 复制代码
[plugins."io.containerd.grpc.v1.cri".registry]
  config_path = "/etc/containerd/certs.d"

创建 Docker Hub 配置目录:

bash 复制代码
mkdir -p /etc/containerd/certs.d/docker.io

写入 mirror 配置:

bash 复制代码
cat >/etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://registry-1.docker.io"

[host."http://<内部docker-registry地址>"]
  capabilities = ["pull", "resolve"]
  skip_verify = true
EOF

如果 registry 是 HTTPS 且证书可信:

toml 复制代码
server = "https://registry-1.docker.io"

[host."https://<内部docker-registry地址>"]
  capabilities = ["pull", "resolve"]

如果 registry 使用自签证书:

toml 复制代码
server = "https://registry-1.docker.io"

[host."https://<内部docker-registry地址>"]
  capabilities = ["pull", "resolve"]
  skip_verify = true

如果 registry 开启了 htpasswd,可以在 hosts.toml 中配置 Basic Authorization header:

bash 复制代码
AUTH=$(printf 'admin:<password>' | base64 -w0)

cat >/etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://registry-1.docker.io"

[host."http://<内部docker-registry地址>"]
  capabilities = ["pull", "resolve"]
  skip_verify = true

[host."http://<内部docker-registry地址>".header]
  authorization = "Basic ${AUTH}"
EOF

重启 containerd:

bash 复制代码
systemctl restart containerd

验证:

bash 复制代码
crictl pull busybox:latest
crictl images | grep busybox

Docker 节点配置

如果节点使用 Docker,在 /etc/docker/daemon.json 中配置:

json 复制代码
{
  "insecure-registries": ["<内部docker-registry地址>"],
  "registry-mirrors": ["http://<内部docker-registry地址>"]
}

如果 registry 使用 HTTPS 且证书可信,可以写:

json 复制代码
{
  "registry-mirrors": ["https://<内部docker-registry地址>"]
}

重启 Docker:

bash 复制代码
systemctl restart docker

验证:

bash 复制代码
docker pull busybox:latest

curl 测试

确认 registry API 可访问:

bash 复制代码
curl -i http://<内部docker-registry地址>/v2/

如果内部 docker-registry 开启了认证,未带账号访问返回 401 Unauthorized 是正常的。可以带账号再测一次:

bash 复制代码
curl -i -u admin:<password> http://<内部docker-registry地址>/v2/

测试通过内部 docker-registry 访问 Docker Hub 镜像 manifest:

bash 复制代码
curl -I -u admin:<password> \
  -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
  http://<内部docker-registry地址>/v2/library/busybox/manifests/latest

如果没有开启认证,去掉命令里的 -u admin:<password>