Docker 私有仓库 Registry
私有镜像仓库是企业级容器化部署的重要组成部分。本章将介绍如何搭建和管理 Docker 私有仓库。
什么是镜像仓库?
镜像仓库(Registry)是存储和分发 Docker 镜像的服务。Docker Hub 是最大的公共仓库,但在企业环境中,通常需要搭建私有仓库:
私有仓库的优势
| 优势 | 说明 |
|---|---|
| 访问控制 | 管理谁可以推送和拉取镜像 |
| 网络效率 | 本地网络传输更快 |
| 安全合规 | 敏感镜像不暴露在公网 |
| 成本控制 | 避免公共仓库的带宽费用 |
| 镜像管理 | 自定义标签和版本策略 |
仓库架构
┌─────────────────────────────────────────────────────────┐
│ Docker Registry │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ API │ │ Auth │ │ Storage │ │
│ │ 接口 │ │ 认证 │ │ 存储 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 存储后端 │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ 文件系统 │ │ S3 │ │ Azure │ │
│ │ │ │ │ │ Blob │ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────┘
快速部署私有仓库
使用 Docker 部署
最简单的方式是使用官方 Registry 镜像:
# 启动仓库容器
docker run -d \
--name registry \
-p 5000:5000 \
-v /data/registry:/var/lib/registry \
--restart=always \
registry:2
# 查看运行状态
docker ps | grep registry
验证仓库
# 查看仓库状态
curl http://localhost:5000/v2/_catalog
# 输出
{"repositories":[]}
推送镜像到私有仓库
# 标记镜像
docker tag nginx:latest localhost:5000/nginx:latest
# 推送镜像
docker push localhost:5000/nginx:latest
# 查看仓库内容
curl http://localhost:5000/v2/_catalog
# {"repositories":["nginx"]}
# 查看镜像标签
curl http://localhost:5000/v2/nginx/tags/list
# {"name":"nginx","tags":["latest"]}
从私有仓库拉取
# 删除本地镜像
docker rmi localhost:5000/nginx:latest
# 从私有仓库拉取
docker pull localhost:5000/nginx:latest
使用 Docker Compose 部署
基础配置
# compose.yaml
services:
registry:
image: registry:2
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
volumes:
- ./data:/var/lib/registry
restart: always
带认证的配置
services:
registry:
image: registry:2
ports:
- "5000:5000"
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
volumes:
- ./data:/var/lib/registry
- ./auth:/auth
restart: always
创建认证文件:
# 创建目录
mkdir -p auth
# 创建用户密码文件
docker run --rm --entrypoint htpasswd httpd:2 -Bbn admin password > auth/htpasswd
# 查看文件
cat auth/htpasswd
# admin:$2y$05$...
登录和推送:
# 登录私有仓库
docker login localhost:5000
# Username: admin
# Password: password
# 推送镜像
docker push localhost:5000/nginx:latest
# 登出
docker logout localhost:5000
配置 HTTPS
生产环境必须使用 HTTPS 加密传输。
使用自签名证书
# 创建证书目录
mkdir -p certs
# 生成自签名证书
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout certs/domain.key \
-x509 -days 365 \
-out certs/domain.crt \
-subj "/CN=myregistry.local"
# 证书信息
# CN: 仓库域名
# days: 有效期
Docker Compose 配置:
services:
registry:
image: registry:2
ports:
- "443:443"
environment:
REGISTRY_HTTP_ADDR: 0.0.0.0:443
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
volumes:
- ./data:/var/lib/registry
- ./certs:/certs
restart: always
客户端配置
信任自签名证书:
# Linux
sudo mkdir -p /etc/docker/certs.d/myregistry.local
sudo cp certs/domain.crt /etc/docker/certs.d/myregistry.local/ca.crt
# 重启 Docker
sudo systemctl restart docker
# macOS (Docker Desktop)
# 将证书添加到系统钥匙串
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain \
certs/domain.crt
# Windows
# 将证书添加到 "受信任的根证书颁发机构"
certutil -addstore "Root" domain.crt
使用域名访问:
# 添加 hosts 记录
echo "127.0.0.1 myregistry.local" | sudo tee -a /etc/hosts
# 登录
docker login myregistry.local
# 推送镜像
docker tag nginx:latest myregistry.local/nginx:latest
docker push myregistry.local/nginx:latest
使用 Let's Encrypt 证书
services:
registry:
image: registry:2
ports:
- "443:443"
environment:
REGISTRY_HTTP_ADDR: 0.0.0.0:443
REGISTRY_HTTP_TLS_CERTIFICATE: /etc/letsencrypt/live/registry.example.com/fullchain.pem
REGISTRY_HTTP_TLS_KEY: /etc/letsencrypt/live/registry.example.com/privkey.pem
volumes:
- ./data:/var/lib/registry
- /etc/letsencrypt:/etc/letsencrypt:ro
restart: always
Registry 配置详解
配置文件方式
创建 config.yml 文件:
version: 0.1
log:
level: info
formatter: text
fields:
service: registry
storage:
filesystem:
rootdirectory: /var/lib/registry
maxthreads: 100
delete:
enabled: true
cache:
blobdescriptor: inmemory
http:
addr: 0.0.0.0:5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization', 'Accept']
auth:
htpasswd:
realm: Registry Realm
path: /auth/htpasswd
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
使用配置文件:
services:
registry:
image: registry:2
ports:
- "5000:5000"
volumes:
- ./data:/var/lib/registry
- ./auth:/auth
- ./config.yml:/etc/docker/registry/config.yml
restart: always
存储配置
本地文件系统:
storage:
filesystem:
rootdirectory: /var/lib/registry
AWS S3:
storage:
s3:
accesskey: AWS_ACCESS_KEY
secretkey: AWS_SECRET_KEY
region: us-west-1
bucket: my-registry-bucket
encrypt: true
secure: true
Azure Blob Storage:
storage:
azure:
accountname: myaccount
accountkey: mykey
container: mycontainer
Google Cloud Storage:
storage:
gcs:
bucket: my-bucket
keyfile: /path/to/keyfile.json
清理策略
配置镜像删除:
storage:
delete:
enabled: true
删除镜像:
# 获取镜像 digest
curl -u admin:password \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://myregistry.local/v2/nginx/manifests/latest
# 删除镜像(需要 digest)
curl -u admin:password -X DELETE \
https://myregistry.local/v2/nginx/manifests/sha256:xxx
# 运行垃圾回收
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
高级认证配置
使用 Docker Registry Token Auth
更安全的认证方式是通过独立的认证服务器:
# 认证服务器配置示例
auth:
token:
realm: https://auth.example.com/auth
service: registry.example.com
issuer: Auth Server
rootcertbundle: /certs/auth.crt
使用 OAuth
auth:
token:
realm: https://auth.example.com/oauth/token
service: registry.example.com
issuer: Auth Server
autoredirect: true
仓库管理工具
Registry Web UI
使用 Docker Registry UI:
services:
registry:
image: registry:2
ports:
- "5000:5000"
volumes:
- ./data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- "8080:80"
environment:
- REGISTRY_TITLE=My Private Registry
- REGISTRY_URL=http://registry:5000
- DELETE_IMAGES=true
depends_on:
- registry
访问 http://localhost:8080 即可管理镜像。
Portus
Portus 是一个功能完整的私有仓库管理平台:
services:
portus:
image: opensuse/portus:latest
ports:
- "3000:3000"
environment:
- PORTUS_MACHINE_FQDN_VALUE=portus.example.com
- PORTUS_SECRET_KEY_BASE=secret
- PORTUS_PASSWORD=secret
depends_on:
- db
db:
image: library/mariadb:10.0.23
environment:
MYSQL_ROOT_PASSWORD: portus
registry:
image: library/registry:2
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
REGISTRY_AUTH_TOKEN_REALM: http://portus.example.com:3000/v2/token
REGISTRY_AUTH_TOKEN_SERVICE: registry.example.com
REGISTRY_AUTH_TOKEN_ISSUER: portus.example.com
volumes:
- ./data:/var/lib/registry
Harbor
Harbor 是企业级的镜像仓库解决方案:
# 下载 Harbor
wget https://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-offline-installer-v2.8.0.tgz
# 解压
tar xzf harbor-offline-installer-v2.8.0.tgz
# 配置
cd harbor
cp harbor.yml.tmpl harbor.yml
# 编辑 harbor.yml 配置域名、密码等
# 安装
./install.sh
Harbor 功能特性:
- 基于角色的访问控制
- 漏洞扫描(Trivy)
- 镜像签名(Notary)
- 垃圾回收
- 复制策略
- 审计日志
- 图形化管理界面
Registry API
常用 API 端点
| 端点 | 方法 | 说明 |
|---|---|---|
/v2/ | GET | 检查 API 版本 |
/v2/_catalog | GET | 列出所有仓库 |
/v2/<name>/tags/list | GET | 列出镜像标签 |
/v2/<name>/manifests/<ref> | GET | 获取镜像清单 |
/v2/<name>/manifests/<ref> | DELETE | 删除镜像 |
/v2/<name>/blobs/<digest> | GET | 获取镜像层 |
API 示例
# 检查 API
curl -u admin:password https://myregistry.local/v2/
# 列出所有仓库
curl -u admin:password https://myregistry.local/v2/_catalog
# 分页列出仓库
curl -u admin:password "https://myregistry.local/v2/_catalog?n=10&last=nginx"
# 列出镜像标签
curl -u admin:password https://myregistry.local/v2/nginx/tags/list
# 获取镜像清单
curl -u admin:password \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://myregistry.local/v2/nginx/manifests/latest
# 获取镜像 digest
DIGEST=$(curl -u admin:password \
-I -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://myregistry.local/v2/nginx/manifests/latest 2>/dev/null \
| grep Docker-Content-Digest | awk '{print $2}' | tr -d '\r')
# 删除镜像
curl -u admin:password -X DELETE \
https://myregistry.local/v2/nginx/manifests/$DIGEST
镜像复制与同步
Registry 同步
# 从 Docker Hub 拉取并推送到私有仓库
docker pull nginx:latest
docker tag nginx:latest myregistry.local/library/nginx:latest
docker push myregistry.local/library/nginx:latest
使用 skopeo 同步
Skopeo 是一个强大的镜像操作工具:
# 安装
brew install skopeo # macOS
# 复制镜像
skopeo copy docker://nginx:latest docker://myregistry.local/nginx:latest
# 列出镜像标签
skopeo list-tags docker://myregistry.local/nginx
# 检查镜像
skopeo inspect docker://myregistry.local/nginx:latest
# 同步整个仓库
skopeo sync --src docker --dest docker \
docker://docker.io/library \
docker://myregistry.local/library
Harbor 复制策略
在 Harbor 中可以配置自动复制规则:
- 进入 Harbor Web 界面
- 选择 "复制管理" -> "新建规则"
- 配置源仓库和目标仓库
- 设置触发模式(手动/定时/事件驱动)
备份与恢复
备份仓库数据
# 备份数据目录
tar czf registry-backup-$(date +%Y%m%d).tar.gz /data/registry
# 备份配置和认证文件
tar czf registry-config-backup.tar.gz ./auth ./certs ./config.yml
使用 Docker 备份
# 创建备份容器
docker run --rm \
-v registry-data:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/registry-backup.tar.gz /data
恢复仓库
# 停止仓库
docker stop registry
# 恢复数据
tar xzf registry-backup.tar.gz -C /
# 启动仓库
docker start registry
生产环境最佳实践
高可用配置
services:
registry-1:
image: registry:2
environment:
REGISTRY_STORAGE: s3
REGISTRY_STORAGE_S3_ACCESSKEY: ${AWS_ACCESS_KEY}
REGISTRY_STORAGE_S3_SECRETKEY: ${AWS_SECRET_KEY}
REGISTRY_STORAGE_S3_REGION: us-east-1
REGISTRY_STORAGE_S3_BUCKET: my-registry
deploy:
replicas: 2
nginx:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- registry-1
监控配置
services:
registry:
image: registry:2
environment:
REGISTRY_HTTP_DEBUG_ADDR: 0.0.0.0:5001
REGISTRY_HTTP_DEBUG_PROMETHEUS_ENABLED: "true"
REGISTRY_HTTP_DEBUG_PROMETHEUS_PATH: /metrics
ports:
- "5000:5000"
- "5001:5001"
Prometheus 抓取配置:
scrape_configs:
- job_name: 'docker-registry'
static_configs:
- targets: ['registry:5001']
日志配置
log:
level: info
formatter: json
fields:
service: registry
environment: production
hooks:
- type: syslog
levels: [error, fatal]
config:
address: syslog.example.com:514
tag: registry
常见问题
1. 推送镜像时权限被拒绝
# 错误:denied: requested access to the resource is denied
# 解决方案:先登录
docker login myregistry.local
2. HTTPS 证书问题
# 错误:x509: certificate signed by unknown authority
# 解决方案:配置信任证书
sudo mkdir -p /etc/docker/certs.d/myregistry.local
sudo cp ca.crt /etc/docker/certs.d/myregistry.local/
# 或临时禁用验证(不推荐)
docker --insecure-registry myregistry.local push myregistry.local/nginx
3. 存储空间不足
# 清理未使用的层
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
# 配置定时清理
# crontab -e
0 2 * * * docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
小结
本章我们学习了:
- 私有仓库部署:Docker 和 Docker Compose 方式
- 认证配置:htpasswd 基础认证
- HTTPS 配置:自签名证书和 Let's Encrypt
- 存储配置:本地文件系统和云存储
- 管理工具:Registry UI、Portus、Harbor
- API 使用:常用 API 端点和示例
- 镜像同步:skopeo 工具使用
- 备份恢复:数据备份策略
练习
- 使用 Docker Compose 部署一个带认证的私有仓库
- 配置 HTTPS 并测试推送拉取
- 使用 Registry API 列出仓库中的镜像
- 使用 skopeo 从 Docker Hub 同步镜像到私有仓库
- 配置镜像垃圾回收定时任务