私有镜像仓库搭建

私有镜像仓库搭建

写在前面

前面所有文章默认你从 Docker Hub 拉镜像,但生产环境绝不应该用 Docker Hub 做内部分发——网速慢、敏感镜像外泄风险、没有审计。本篇会从最简单的 Docker Registry 入手,逐步升级到 Harbor,最后讲我们迁移 Harbor 的真实经历。

一、为什么需要私有仓库

1.1 直接用 Docker Hub 不行吗?

维度 Docker Hub 自建私有仓库
速度 受公网带宽影响,国内更慢 内网拉取,几秒就到
安全 内网业务镜像推到公网 完全在内网,不外泄
审计 没有 完整的拉取/推送日志
配额 限速(匿名 100次/6h,免费账户 200次/6h) 无限
高可用 依赖 Docker 自己保证

我们公司迁移的第一动力是速度——从 Docker Hub 拉一个 1GB 镜像要 20 分钟,从 Harbor 拉只要 30 秒。

1.2 选型对比

方案 优点 缺点 适合
Docker Registry 简单、官方 无 UI、无鉴权 个人 / 测试
Harbor 功能完整、企业级 资源占用大 公司生产
Quay 安全扫描强 收费 大企业
Nexus/Artifactory 通用制品管理 重 已有这些工具

Harbor 是绝大多数公司的最优解,本篇重点讲它。

二、最简方案:Docker Registry

先用 5 分钟起一个 Registry 感受一下:

# compose.yaml
services:
  registry:
    image: registry:2
    restart: unless-stopped
    ports:
      - "5000:5000"
    volumes:
      - registry-data:/var/lib/registry

volumes:
  registry-data:
docker compose up -d

测试:

# 打个 tag
docker tag nginx:alpine localhost:5000/my-nginx:v1

# 推送
docker push localhost:5000/my-nginx:v1

# 拉取
docker pull localhost:5000/my-nginx:v1

# 浏览 API
curl http://localhost:5000/v2/_catalog
# {"repositories":["my-nginx"]}

没有鉴权,任何人都能 push/pull——只能在内网用,或者加上一层网络隔离。

2.1 加 TLS

services:
  registry:
    image: registry:2
    ports:
      - "5000:5000"
    volumes:
      - registry-data:/var/lib/registry
      - ./certs:/certs:ro
    environment:
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
      REGISTRY_HTTP_TLS_KEY: /certs/domain.key

或者放在 Nginx 后面(生产推荐)。

2.2 加基础认证

# 创建密码文件
mkdir -p auth
docker run --rm --entrypoint htpasswd \
  httpd:2 -Bbn admin yourpassword > auth/htpasswd
services:
  registry:
    image: registry:2
    ports:
      - "5000:5000"
    volumes:
      - registry-data:/var/lib/registry
      - ./auth:/auth:ro
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm

登录:

docker login localhost:5000
# Username: admin
# Password: ******

三、生产方案:Harbor

3.1 核心特性

Harbor 在 Registry 基础上加了:

3.2 部署方式

方式 1:Harbor 官方 compose(单机)

# 下载离线安装包(自带所有依赖镜像)
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzf harbor-offline-installer-v2.10.0.tgz
cd harbor

# 编辑配置
cp harbor.yml.tmpl harbor.yml
vi harbor.yml

harbor.yml 关键配置:

hostname: harbor.company.com
http:
  port: 80
https:
  port: 443
  certificate: /etc/harbor/certs/harbor.crt
  private_key: /etc/harbor/certs/harbor.key
harbor_admin_password: Harbor12345  # 首次登录后改掉
database:
  password: root123
data_volume: /data/harbor
trivy:
  ignore_unfixed: true
  skip_update: true
  offline_scan: false

安装:

sudo ./install.sh

方式 2:K8s Helm(集群)

helm repo add harbor https://helm.goharbor.io
helm install harbor harbor/harbor \
  --namespace harbor \
  --create-namespace \
  --set expose.ingress.hosts.core=harbor.company.com

3.3 首次使用

# 配置 Docker 信任私有仓库(自签名证书)
mkdir -p /etc/docker/certs.d/harbor.company.com
cp harbor.crt /etc/docker/certs.d/harbor.company.com/ca.crt

# 重启 Docker
systemctl restart docker

# 登录
docker login harbor.company.com
# Username: admin
# Password: Harbor12345

# 创建项目(在 Web UI 里更直观)
# Project -> New Project -> "myproject" (私有)

推送镜像:

docker tag myapp:v1.0 harbor.company.com/myproject/myapp:v1.0
docker push harbor.company.com/myproject/myapp:v1.0

3.4 RBAC 配置

Harbor 的权限模型:

实战中我们的分配:

3.5 镜像复制(跨 Harbor 同步)

比如我们公司在多个数据中心,需要同步镜像:

# 在目标 Harbor 上:
# Registries -> New Endpoint -> 源 Harbor 信息
# Replication -> New Replication Rule:
#   - Source: source-harbor
#   - Destination: target-harbor
#   - Trigger: 手动 / 定时 / 事件触发

实战坑:触发器用 “Event Based” 经常漏触发,推荐用 Scheduled(比如每小时同步一次)。

3.6 镜像签名

镜像签名能防止中间人篡改镜像。Harbor 用 Cosign(Sigstore 项目):

# 在 CI 里
cosign sign --key cosign.key harbor.company.com/myproject/myapp:v1.0

# 验证
cosign verify --key cosign.pub harbor.company.com/myproject/myapp:v1.0

生产中我们后来用 keyless 模式(绑定 OIDC 身份,不需要私钥):

cosign sign --keyless harbor.company.com/myproject/myapp:v1.0

Kubernetes 配合 kyverno / conftest 在部署时验证签名。

四、垃圾回收(GC)实战

4.1 镜像为什么会膨胀?

每次 CI 构建都会生成新的镜像并 push,但旧的镜像不会自动删除。时间一长,Harbor 磁盘爆炸。

我们曾经遇到:30 天没清理的 Harbor 占了 4TB 磁盘,大多是已经被覆盖的中间层。

4.2 手动 GC

Harbor 的 GC 不会自动跑,需要手动触发:

# 进入 Harbor 容器
docker exec -it harbor-core bash

# 触发 GC
cd /etc/core
python -c "
import os
os.environ['HARBOR_ADMIN_PASSWORD'] = 'xxx'
import core
core.gc_api.gc()
"

# 或者通过 API
curl -X POST "https://harbor.company.com/api/v2.0/system/gc/schedule" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"schedule":{"type":"Hourly","cron":"0 0 * * * *"}}'

4.3 实战:Harbor 迁移到 2.10 时 GC 的坑

我们从 Harbor 2.6 升到 2.10 时,GC 行为变了——2.10 之前的 GC 不会真正删除 blob,只是标记。需要手工做一次 full GC:

# 1. 触发 GC
curl -X POST "https://harbor.company.com/api/v2.0/system/gc" -H "..."

# 2. 等 GC 完成(看 Web UI / Harbor.log)
# 3. 进入 registry 容器,手工清理标记的 blob
docker exec -it registry bash
cd /etc/registry
# 触发 blob 清理
registry garbage-collect /etc/registry/config.yml

# 4. 真的删除磁盘文件
registry garbage-collect --delete-untagged /etc/registry/config.yml

清理了 3.2TB 的孤儿 blob,磁盘从 92% 降到 41%。

4.4 自动清理策略

我们后来在每个项目里配置 保留策略:

# Tag Retention Rule
rules:
  - name: "保留最近 10 个"
    tag_selectors:
      - kind: latest  # 或者正则
    action: retain
    params:
      most_recent: 10
  - name: "保留带 prod 标签的"
    tag_selectors:
      - decoration: matches
        pattern: "prod-.*"
    action: retain

定时清理的效果:Harbor 长期稳定在 1.2TB 左右。

五、备份与恢复

5.1 备份什么?

Harbor 的数据分两部分:

  1. 数据库(PostgreSQL):存元数据(项目、用户、tag 信息)
  2. 镜像数据(Registry):存在 /data/harbor/registry/ 目录

5.2 备份脚本

#!/bin/bash
# backup-harbor.sh

set -euo pipefail

BACKUP_DIR=/backup/harbor/$(date +%F)
HARBOR_DIR=/data/harbor
PG_CONTAINER=harbor-db

mkdir -p $BACKUP_DIR

# 1. 备份数据库
docker exec $PG_CONTAINER pg_dump -U postgres harbor > $BACKUP_DIR/harbor.sql

# 2. 备份镜像数据
tar czf $BACKUP_DIR/registry.tar.gz -C $HARBOR_DIR registry/

# 3. 备份配置(可选,但建议)
cp $HARBOR_DIR/harbor.yml $BACKUP_DIR/

# 4. 上传到远程
aws s3 cp $BACKUP_DIR s3://my-backups/harbor/$(date +%F)/ --recursive

echo "Backup done: $BACKUP_DIR"

定时任务:

# 每天凌晨 3 点备份
0 3 * * * /opt/scripts/backup-harbor.sh

5.3 恢复

# 1. 部署一个空的 Harbor
./install.sh

# 2. 恢复数据库
docker exec -i harbor-db psql -U postgres harbor < backup/harbor.sql

# 3. 恢复镜像数据
tar xzf registry.tar.gz -C /data/harbor/

# 4. 重启 Harbor
docker compose down && docker compose up -d

六、性能优化

6.1 启用 P2P 分发

Harbor 支持 P2P 镜像分发(基于 Kraken),拉取大镜像时速度提升明显:

# harbor.yml
distribution:
  policy:
    - name: my-p2p-policy
      params:
        enabled: true

但这个我们没启用——绝大多数镜像小于 500MB,瓶颈不在分发,在网络。

6.2 镜像分层缓存(客户端)

Docker 24+ 自带 Registry Pull Through Cache,可以缓存上游镜像:

# docker daemon 配置
{
  "registry-mirrors": ["https://harbor.company.com"]
}

这样 docker pull nginx:alpine 第一次从 Harbor 代理 Docker Hub,第二次直接从 Harbor 缓存拉。

我们后来把所有员工机器都配置了 Harbor mirror,Docker Hub 流量降了 80%。

6.3 资源调优

Harbor 默认配置偏保守。生产中我们调大了:

# harbor.yml
proxy:
  http:
    - component: core
      timeout: 60s
  https:
    - component: core
      timeout: 60s

# 数据库连接池
database:
  max_idle_conns: 50
  max_open_conns: 200

七、本篇小结

主题 关键点
Registry 基础 简单、无 UI、适合测试
Harbor 部署 官方 compose / K8s Helm
RBAC 项目级权限,符合最小权限原则
镜像复制 多数据中心同步
镜像签名 Cosign,防止中间人篡改
GC 必须定期跑,避免磁盘爆炸
备份 数据库 + 镜像数据都要备份

下一篇讲 CI/CD 集成实战,会用 GitHub Actions 完整跑一遍构建→测试→推送→部署的流水线。

参考资料