将一个内部 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 分两层配置:
- Registry 服务端配置:让内部 docker-registry 具备回源缓存能力。
- 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.io、ghcr.io、registry.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:latest、nginx: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>。