跳到主要内容

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/_catalogGET列出所有仓库
/v2/<name>/tags/listGET列出镜像标签
/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 中可以配置自动复制规则:

  1. 进入 Harbor Web 界面
  2. 选择 "复制管理" -> "新建规则"
  3. 配置源仓库和目标仓库
  4. 设置触发模式(手动/定时/事件驱动)

备份与恢复

备份仓库数据

# 备份数据目录
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

小结

本章我们学习了:

  1. 私有仓库部署:Docker 和 Docker Compose 方式
  2. 认证配置:htpasswd 基础认证
  3. HTTPS 配置:自签名证书和 Let's Encrypt
  4. 存储配置:本地文件系统和云存储
  5. 管理工具:Registry UI、Portus、Harbor
  6. API 使用:常用 API 端点和示例
  7. 镜像同步:skopeo 工具使用
  8. 备份恢复:数据备份策略

练习

  1. 使用 Docker Compose 部署一个带认证的私有仓库
  2. 配置 HTTPS 并测试推送拉取
  3. 使用 Registry API 列出仓库中的镜像
  4. 使用 skopeo 从 Docker Hub 同步镜像到私有仓库
  5. 配置镜像垃圾回收定时任务