使用官方 Registry v2 搭建 Docker Mirror 私有镜像仓库
在国内网络环境下,直接从 Docker Hub 拉取镜像经常遇到超时或被限流的问题。搭建一个本地 Docker Mirror 代理仓库可以有效解决这个痛点。本文介绍如何使用官方 Registry v2 搭建私有镜像代理仓库。
为什么要搭 Docker Mirror
1. Docker Hub 公网限速
Docker Hub 对匿名用户的拉取限速为 100 次/6 小时,免费账户为 200 次/6 小时。在 CI/CD 流水线或多人团队环境下,这个限额很容易触达。通过自建 Mirror 代理,所有节点的拉取请求汇聚到 Mirror 节点,Mirror 对上游只产生一次请求,有效规避限额。
2. 缓存加速 Build
在 CI/CD 构建中,每次 docker build 都需要拉取基础镜像。即使代码没有变化,基础镜像也会被反复从公网拉取,浪费时间。Mirror 缓存了基础镜像后:
- 首次构建:从上游拉取并缓存
- 后续构建:直接从 Mirror 拉取,速度提升 5-10 倍
- 多节点并行构建:共享同一个 Mirror,只消耗一次外网流量
3. 离线可用与稳定性
| 特性 | 说明 |
|---|---|
| 离线可用 | 已缓存的镜像在内网环境下随时可用,不依赖外网连接 |
| 稳定可控 | 不受 Docker Hub 宕机、限速、DNS 污染等公网问题影响 |
| 节省带宽 | 多个节点共享缓存,外网出口带宽消耗大幅降低 |
架构说明
Docker Client → Caddy (HTTPS) → Registry v2 (Mirror) → DaoCloud 上游 → Docker Hub
↓
本地缓存(命中则直接返回)
整个链路的请求流程:
- Docker Client 向
docker pull配置的 Mirror 地址发起拉取请求 - 请求经 Caddy HTTPS 反向代理转发到 Registry v2
- Registry 检查本地缓存,命中则直接返回
- 缓存未命中时,Registry 向 DaoCloud 上游代理转发请求
- DaoCloud 再从 Docker Hub 获取镜像数据并逐层返回
- Registry 将收到的每一层数据写入本地缓存
环境准备
- Docker 20.10+:确保 Docker Engine 版本支持
registry-mirrors配置 - 域名:一个已解析到服务器 IP 的域名,用于 Caddy 自动申请 HTTPS 证书(如
mirror.example.com) - SSD 存储:建议至少 100GB 以上,镜像层文件较大且频繁读写,SSD 能显著提升缓存命中后的响应速度
- 开放端口:只需开放 443 端口(HTTPS),Registry 本身通过 Docker 内部网络通信,不暴露端口到宿主机
部署 Registry v2 Mirror
配置文件
创建 config.yml:
# Registry 配置文件版本,当前为 0.1
version: 0.1
# 日志配置
log:
# 日志级别:debug / info / warn / error / fatal,默认 info
level: info
# 日志格式:json / text / logfmt,默认 logfmt
formatter: logfmt
# 附加字段,会添加到每条日志中,默认无
fields:
service: registry
environment: production
# 存储配置
storage:
# 存储驱动类型,默认 filesystem
filesystem:
# 镜像层数据的存储根目录,默认 /var/lib/registry
rootdirectory: /var/lib/registry
# 并发文件系统操作的线程数,默认 100
maxthreads: 100
# Blob 描述符缓存,用于加速 manifest 请求
cache:
blobdescriptor: inmemory
# 启用删除 API,允许通过 DELETE 接口删除 blob,默认 false
delete:
enabled: true
# 存储维护任务配置
maintenance:
# 上传清理,自动清理未完成的上传文件
uploadpurging:
# 是否启用上传清理,默认 true
enabled: true
# 未完成上传文件的保留时间,超过此时间将被清理,默认 168h(7 天)
age: 168h
# 清理检查间隔,默认 24h
interval: 24h
# 是否只预览不实际删除,默认 false
dryrun: false
# 只读模式开关,设为 true 时拒绝所有写入操作(push、delete),拉取正常
# 可用于 GC 时临时开启,防止并发写入,默认 false
readonly:
enabled: false
# HTTP 服务器配置
http:
# 监听地址和端口,默认 :5000
addr: :5000
# 监听的网络接口,默认 localhost(仅本地访问)
host: 0.0.0.0
# HTTP 响应头设置
headers:
# 防止浏览器 MIME 类型嗅探,安全最佳实践
X-Content-Type-Options: [nosniff]
# CORS 允许的来源,设为 * 允许所有来源
Access-Control-Allow-Origin: ['*']
# CORS 允许的 HTTP 方法
Access-Control-Allow-Methods:
- HEAD
- GET
- OPTIONS
# CORS 允许的请求头
Access-Control-Allow-Headers:
- Authorization
- Accept
# 上游代理配置
proxy:
# 上游镜像源地址(必填)
remoteurl: https://docker.m.daocloud.io
# 上游认证用户名(可选,公共源不需要)
# username:
# 上游认证密码(可选,公共源不需要)
# password:
# 健康检查配置
health:
storagedriver:
# 是否启用存储健康检查(默认:true)
enabled: true
# 检查间隔(默认:10s)
interval: 10s
# 连续失败多少次后标记不健康(默认:3)
threshold: 3
关键配置项说明
| 配置项 | 默认值 | 说明 |
|---|---|---|
storage.filesystem.rootdirectory | /var/lib/registry | 镜像存储路径 |
storage.cache.blobdescriptor | inmemory | Blob 元数据缓存方式,可选 redis |
storage.delete.enabled | false | 必须设为 true,否则无法执行 GC |
storage.maintenance.readonly.enabled | false | 只读模式,GC 时临时开启 |
proxy.remoteurl | - | 上游镜像源地址 |
health.storagedriver.enabled | true | 存储驱动健康检查 |
上游源选择:DaoCloud
| 特性 | 说明 |
|---|---|
| 稳定性 | DaoCloud 是国内主流容器厂商,镜像站长期维护 |
| 完整性 | 同步 Docker Hub 全量镜像 |
| 速度 | 国内 CDN 加速 |
| 免费 | 公共服务,无需注册 |
其他可选的上游源:
# DaoCloud
remoteurl: https://docker.m.daocloud.io
# 阿里云
remoteurl: https://registry.cn-hangzhou.aliyuncs.com
# 腾讯云
remoteurl: https://mirror.ccs.tencentyun.com
可以部署多个 Mirror 实例使用不同的上游源,互为备份。
缓存类型
Registry v2 的 proxy 模式使用以下缓存机制:
1. Blob 缓存(层缓存)
镜像的每一层(layer)都会被缓存到本地存储:
/var/lib/registry/
├── docker/
│ └── registry/
│ └── v2/
│ ├── blobs/
│ │ └── sha256/ # 镜像层数据
│ └── repositories/ # 仓库元数据
2. Manifest 缓存(清单缓存)
镜像的 manifest(包含层关系、tag 等信息)也会被缓存。
3. BlobDescriptor 缓存(元数据缓存)
配置 storage.cache.blobdescriptor:
# 内存缓存(默认,适合中小规模)
storage:
cache:
blobdescriptor: inmemory
# Redis 缓存(适合大规模部署)
storage:
cache:
blobdescriptor: redis
redis:
addr: redis:6379
password: yourpassword
db: 0
ringlimit: 100
缓存策略
| 缓存类型 | 说明 | 建议场景 |
|---|---|---|
inmemory | 元数据存内存,blob 存磁盘 | 镜像数量 < 10000 |
redis | 元数据存 Redis,blob 存磁盘 | 镜像数量 > 10000,多实例 |
缓存未命中
- Registry 检查本地存储 → 未找到
- 转发请求到上游(DaoCloud)
- 上游返回数据 → Registry 同时缓存到本地并返回给客户端
- 后续请求直接从本地返回
GC(垃圾回收)
随着时间推移,Registry 中会积累大量不再使用的镜像层数据(例如旧版本镜像被更新替换)。GC 用于清理这些无用数据,释放磁盘空间。
GC 原理
Registry 使用 content-addressable 存储,每个 blob 通过 SHA256 哈希标识。GC 的核心逻辑:
- 标记所有被当前 manifest 引用的 blob
- 删除未被引用的 blob(垃圾数据)
manifest v2 → 引用 blob A, B, C
manifest v1 → 引用 blob A, B, D(旧版本)
GC 后:blob D 被清除(只被旧 manifest 引用)
在线 GC(read-only 模式)
Registry v2 支持 只读模式(read-only mode),GC 前切换为只读,避免并发写入导致数据不一致:
- 将 Registry 切换为只读模式(拒绝写入,拉取正常)
- 执行 GC 清理未引用的 blob
- 恢复读写模式
第一步:预览(dry-run)
# 安全操作,不影响服务,建议先执行确认
docker exec registry-mirror bin/registry garbage-collect \
/etc/docker/registry/config.yml \
--dry-run
输出示例:
ubuntu for sha256:abc123...
nginx for sha256:def456...
第二步:切换只读 + 执行 GC + 恢复
注意:不能通过环境变量
REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED=true设置只读模式,会触发 panic(Registry 的已知 bug,环境变量覆盖 maintenance 子配置时 map key 类型不匹配)。 正确做法是通过docker exec修改容器内的 config.yml,再用docker restart重载配置。
# 1. 开启只读模式 + 重启生效
docker exec registry-mirror sed -i "/readonly:/,/enabled:/ s/enabled: false/enabled: true/" /etc/docker/registry/config.yml
docker restart registry-mirror
# 2. 等待容器就绪后执行 GC
sleep 5
docker exec registry-mirror bin/registry garbage-collect /etc/docker/registry/config.yml
# 3. 恢复读写模式 + 重启生效
docker exec registry-mirror sed -i "/readonly:/,/enabled:/ s/enabled: true/enabled: false/" /etc/docker/registry/config.yml
docker restart registry-mirror
定期 GC 建议
建议通过 cron 定期执行 GC:
# 每月 1 号凌晨 3 点执行 GC
0 3 1 * * root docker exec registry-mirror sed -i "/readonly:/,/enabled:/ s/enabled: false/enabled: true/" /etc/docker/registry/config.yml && docker restart registry-mirror && sleep 5 && docker exec registry-mirror bin/registry garbage-collect /etc/docker/registry/config.yml && docker exec registry-mirror sed -i "/readonly:/,/enabled:/ s/enabled: true/enabled: false/" /etc/docker/registry/config.yml && docker restart registry-mirror
GC 与删除 API 的区别
| 方式 | 作用 | 用途 |
|---|---|---|
DELETE /v2/<name>/manifests/<digest> | 删除指定 manifest | 删除特定镜像 tag |
garbage-collect | 清理未引用的 blob | 回收磁盘空间 |
两者通常配合使用:先通过 DELETE API 删除不需要的 manifest,再执行 GC 释放空间。
Caddy Server — HTTPS 反向代理
Docker 客户端配置 registry-mirrors 要求使用 HTTPS。使用 Caddy 的核心优势是 零配置自动 HTTPS,自动申请和续期 Let’s Encrypt 证书。
为什么选 Caddy
| 特性 | Caddy | Nginx |
|---|---|---|
| HTTPS 证书 | 自动申请续期 | 需配合 certbot |
| 配置复杂度 | 极简 | 较复杂 |
| HTTP/3 | 原生支持 | 需额外编译模块 |
| 适合场景 | 中小规模、快速部署 | 大规模、复杂路由 |
Caddyfile 配置
创建 Caddyfile:
mirror.example.com {
reverse_proxy registry:5000
# 上传大镜像需要放宽限制
request_body {
max_size 10GB
}
# Docker 客户端需要的 header
header /v2/ Docker-Distribution-Api-Version "registry/2.0"
# 访问日志
log {
output file /data/caddy/access.log
format console
}
# 健康检查端点不记日志
@health path /v2/
log @health {
output discard
}
}
确保域名
mirror.example.com的 DNS A 记录已指向服务器公网 IP,Caddy 才能自动申请 Let’s Encrypt 证书。
完整 docker-compose.yml(Caddy + Registry)
version: '3.8'
services:
caddy:
image: caddy:2-alpine
container_name: registry-caddy
restart: always
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_data:/data
- ./caddy_config:/config
depends_on:
registry:
condition: service_healthy
registry:
image: registry:2
container_name: registry-mirror
restart: always
# 不暴露端口,仅通过 Caddy 访问
expose:
- "5000"
volumes:
- ./data:/var/lib/registry
- ./config.yml:/etc/docker/registry/config.yml
environment:
- REGISTRY_STORAGE_DELETE_ENABLED=true
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"]
interval: 30s
timeout: 10s
retries: 3
启动服务
# 启动
docker compose up -d
# 查看 Caddy 日志,确认证书申请成功
docker logs -f registry-caddy
看到类似输出表示 HTTPS 证书已自动获取:
{"level":"info","msg":"certificate obtained successfully","identifier":"mirror.example.com"}
验证
# 测试 HTTPS 访问
curl https://mirror.example.com/v2/
# 应返回空 JSON:{}
Docker 客户端配置
{
"registry-mirrors": [
"https://mirror.example.com"
],
"insecure-registries": []
}
使用 Caddy 提供 HTTPS 后,不需要 配置
insecure-registries,安全性更好。
Caddy 数据持久化
./caddy_data/ # TLS 证书(自动申请、自动续期)
./caddy_config/ # Caddy 配置缓存
证书续期由 Caddy 自动处理,无需人工干预。
日常运维
查看存储占用
# 查看缓存目录大小
du -sh /data/registry/
# 查看各仓库占用
du -sh /data/registry/docker/registry/v2/repositories/*
查看日志
docker logs -f --tail 100 registry-mirror
docker logs -f --tail 100 registry-caddy
健康检查
# 检查服务状态
curl https://mirror.example.com/v2/
# 检查上游连通性
curl https://mirror.example.com/v2/_catalog
完整目录结构
docker-mirror/
├── docker-compose.yml # Caddy + Registry
├── Caddyfile # Caddy HTTPS 反向代理配置
├── config.yml # Registry 配置
├── data/ # Registry 镜像缓存
├── caddy_data/ # Caddy TLS 证书
└── caddy_config/ # Caddy 配置缓存
总结
| 要点 | 说明 |
|---|---|
| 核心组件 | 官方 Registry v2 + proxy 模式 |
| HTTPS 代理 | Caddy(零配置自动 HTTPS、证书自动续期) |
| 上游源 | DaoCloud(稳定、完整、免费) |
| 缓存加速 | Blob 磁盘缓存 + 元数据内存/Redis 缓存,加速 CI/CD Build |
| GC | read-only 模式 + docker exec,防止并发写入 |
| 运维要点 | 监控磁盘用量、定期 GC、关注上游可用性 |
对于内网 Docker 环境,搭建一个 Mirror 代理仓库是性价比极高的基础设施投入,一次部署长期受益。