在DevOps和容器化技术快速发展的今天,Harbor作为企业级Docker镜像仓库,承担着镜像存储、分发、漏洞扫描等核心职责。但随着Harbor版本迭代不断加速,如何安全完成组件升级、避免数据丢失,成为每个运维工程师和开发者必须面对的现实问题。本文将围绕Harbor镜像仓库的组件升级策略与升级过程中的数据迁移方案展开,从基础概念到实际操作,带你走完整个升级流程。
一、Harbor镜像仓库的基本认知
Harbor是一个开源的容器镜像仓库项目,最初由VMware公司开发并捐赠给CNCF基金会,目前已是CNCF毕业项目。你可以把它理解为一个"镜像超市"——开发者把构建好的Docker镜像推送到这个超市,其他开发者或服务从这个超市拉取镜像来部署运行。Harbor负责管理这些镜像,确保它们安全、可用、可追溯。
Harbor并不是一个简单的单体应用,它由多个组件协同工作。要理解升级策略,首先需要搞清楚它的内部架构。
1.1 Harbor的核心组件构成
Harbor内部包含以下关键组件:
- Harbor Core:核心Web服务,处理API请求、用户认证、权限控制
- Registry:Docker Registry组件,负责镜像的实际存储与读写
- Registry Controller:控制Registry的行为,处理镜像上传下载逻辑
- Jobservice:后台任务调度服务,处理镜像复制、扫描等异步任务
- Log Collector:日志收集服务,将各组件日志汇聚到数据库
- PostgreSQL数据库:存储Harbor的元数据,包括用户信息、镜像元数据、权限配置等
- Redis:缓存服务,存储会话信息和队列数据
- Nginx:反向代理,统一流量入口
- Portal:前端UI界面
- Token Service:Docker Registry的认证令牌服务
- Chart Museum:Helm Chart仓库服务
这些组件之间通过Docker网络通信,数据库和Redis为各组件提供持久化存储和缓存。理解这个架构有助于我们明白为什么升级需要谨慎——每个组件的版本必须相互兼容,数据必须平滑迁移,任何一环出错都可能导致整个服务不可用。
二、升级策略的核心逻辑
2.1 两种升级场景
Harbor的升级策略主要分为两种情况。
第一种是小版本升级,比如从1.10.0升级到1.10.4。小版本通常只修复Bug和安全漏洞,架构不会有大的变化,数据库结构一般不会变动,升级过程相对简单,可以直接覆盖更新或使用docker-compose方式滚动更新。
第二种是大版本升级,比如从1.9.0升级到2.0.0。大版本可能涉及架构调整、数据库表结构变化、API不兼容等。这种情况下必须先做数据库迁移,再更新应用代码,整个过程更复杂,风险也更高。
2.2 为什么必须逐级升级
Harbor官方文档明确要求升级必须遵循"逐级升级"原则。假设当前使用的是Harbor 1.5.2,目标版本是2.3.0,不能一次性跳过中间版本直接升级,而应该按以下路径逐步推进:
1.5.2 → 1.9.0 → 1.10.0 → 2.0.0 → 2.1.0 → 2.2.0 → 2.3.0
原因很直观:每个大版本可能包含数据库迁移脚本,跳过某个版本可能导致迁移脚本缺失,数据库升级失败。数据库迁移是一个链式过程,假设1.9.0到1.10.0之间有一个迁移脚本将某字段从varchar改为text类型。如果跳过1.9.0直接升级到2.0.0,2.0.0的迁移脚本可能假设该字段已经是text类型,但实际还是varchar类型,导致迁移脚本报错。逐级升级确保每一步的数据库变更都被正确执行,不会出现跳跃式变更导致的数据不一致。
2.3 升级路径选择
根据实际业务情况,可以选择不同的升级路径:
- 完整迁移路径:适用于数据量不大、停机时间可以接受的情况,先备份数据,再全新部署新版本,最后导入数据
- 就地升级路径:适用于数据量大、停机时间有限制的情况,在原位逐步更新组件,数据库迁移在原地执行
- 灰度升级路径:先升级一台实例验证,确认无误后再推进到生产环境
三、升级前的准备工作
3.1 环境与依赖检查
升级前必须确认当前Harbor实例的运行状态。首先检查所有容器是否正常运行:
# ==========================================
# 技术栈:Docker Compose + Shell Script
# 功能:检查Harbor所有容器的运行状态
# ==========================================
# 查看当前系统中所有运行中的Harbor容器
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep harbor
# 输出示例:
# NAMES STATUS IMAGE
# harbor-harbor-core Up 3 hours goharbor/harbor-core:v2.3.0
# harbor-harbor-registry Up 3 hours goharbor/registry:v2.3.0
# harbor-harbor-jobservice Up 3 hours goharbor/harbor-jobservice:v2.3.0
# harbor-harbor-db Up 3 hours postgres:11.5-alpine
接下来查看当前Harbor的具体版本号:
# ==========================================
# 功能:查看Harbor的版本信息
# ==========================================
# 进入core容器查看版本文件
docker exec -it harbor-harbor-core cat /harbor/version
# 也可以通过Harbor API获取版本信息
curl -s -k -u "admin:Harbor12345" \
"https://your-harbor-address/api/v2.0/systeminfo" | python3 -m json.tool
# 输出示例:
# {
# "harbor_version": "2.3.0",
# "storage_provider": "filesystem",
# "admission_controller": "notary",
# "registry_storage": 5995258956800,
# "registry_storage_used": 4294967296
# }
3.2 数据目录结构确认
Harbor默认的数据目录通常在/data/harbor下,升级前需要了解数据目录的完整结构:
# ==========================================
# 功能:检查Harbor数据目录结构
# ==========================================
# 查看所有子目录
ls -la /data/harbor/
# 检查各个关键子目录是否存在
echo "=== 检查核心数据目录 ==="
for dir in registry database redis cache logs; do
if [ -d "/data/harbor/$dir" ]; then
echo "[OK] $dir 目录存在"
du -sh "/data/harbor/$dir"
else
echo "[MISSING] $dir 目录不存在"
fi
done
# 输出示例:
# === 检查核心数据目录 ===
# [OK] registry 目录存在
# 4.2G /data/harbor/registry
# [OK] database 目录存在
# 512M /data/harbor/database
# [OK] redis 目录存在
# 128M /data/harbor/redis
# [OK] cache 目录存在
# 256M /data/harbor/cache
# [OK] logs 目录存在
# 768M /data/harbor/logs
3.3 全面数据备份
备份是升级前最重要的一步,没有之一。Harbor的数据主要存储在数据库、镜像存储目录、配置文件中。下面展示一个完整的备份脚本:
# ==========================================
# 技术栈:Docker Compose + Shell Script
# 功能:Harbor升级前的完整数据备份脚本
# ==========================================
#!/bin/bash
# --- 配置变量 ---
BACKUP_DIR="/data/harbor/backup" # 备份存储路径
TIMESTAMP=$(date +%Y%m%d_%H%M%S) # 时间戳,防止覆盖
HARBOR_CORE_CONTAINER="harbor-harbor-core"
HARBOR_DB_CONTAINER="harbor-harbor-db"
HARBOR_REGISTRY_CONTAINER="harbor-harbor-registry"
DATABASE_NAME="harbor" # PostgreSQL数据库名
DATABASE_USER="postgres" # PostgreSQL用户名
DATABASE_HOST="127.0.0.1" # 数据库地址
# --- 创建备份目录 ---
mkdir -p "$BACKUP_DIR"
mkdir -p "$BACKUP_DIR/database" # 数据库备份目录
mkdir -p "$BACKUP_DIR/config" # 配置文件备份目录
mkdir -p "$BACKUP_DIR/metadata" # 元数据备份目录
echo "========================================"
echo "开始Harbor数据备份,时间戳:$TIMESTAMP"
echo "========================================"
# --- 步骤1:数据库备份 ---
echo "[1/4] 正在备份PostgreSQL数据库..."
docker exec "$HARBOR_DB_CONTAINER" \
pg_dump -h "$DATABASE_HOST" \
-U "$DATABASE_USER" \
-d "$DATABASE_NAME" \
> "$BACKUP_DIR/database/harbor_db_backup_${TIMESTAMP}.sql"
# 验证备份文件是否生成
if [ -f "$BACKUP_DIR/database/harbor_db_backup_${TIMESTAMP}.sql" ]; then
BACKUP_SIZE=$(du -sh "$BACKUP_DIR/database/harbor_db_backup_${TIMESTAMP}.sql" | awk '{print $1}')
echo " [OK] 数据库备份完成,文件大小:$BACKUP_SIZE"
else
echo " [FAIL] 数据库备份失败,请检查容器状态"
exit 1
fi
# --- 步骤2:配置文件备份 ---
echo "[2/4] 正在备份配置文件..."
# 备份Harbor配置文件
docker cp "$HARBOR_CORE_CONTAINER:/harbor/": "$BACKUP_DIR/config/"
# 备份docker-compose文件
cp /data/harbor/docker-compose.yml "$BACKUP_DIR/config/docker-compose_${TIMESTAMP}.yml"
cp /data/harbor/harbor.yml "$BACKUP_DIR/config/harbor_${TIMESTAMP}.yml"
echo " [OK] 配置文件备份完成"
# --- 步骤3:元数据备份 ---
echo "[3/4] 正在备份镜像元数据..."
# 导出所有项目信息
curl -s -k -u "admin:Harbor12345" \
"https://your-harbor-address/api/v2.0/projects" \
> "$BACKUP_DIR/metadata/projects_backup_${TIMESTAMP}.json"
# 导出所有镜像信息(需要遍历项目)
for PROJECT in $(curl -s -k -u "admin:Harbor12345" \
"https://your-harbor-address/api/v2.0/projects" \
| python3 -c "import sys,json; [print(p['name']) for p in json.load(sys.stdin)]"); do
curl -s -k -u "admin:Harbor12345" \
"https://your-harbor-address/api/v2.0/projects/${PROJECT}/repositories" \
> "$BACKUP_DIR/metadata/repos_${PROJECT}_${TIMESTAMP}.json"
done
echo " [OK] 元数据备份完成"
# --- 步骤4:生成备份清单 ---
echo "[4/4] 正在生成备份清单..."
echo "Harbor备份清单 - $TIMESTAMP" > "$BACKUP_DIR/backup_manifest_${TIMESTAMP}.txt"
echo "========================================" >> "$BACKUP_DIR/backup_manifest_${TIMESTAMP}.txt"
find "$BACKUP_DIR" -type f -exec ls -lh {} \; >> "$BACKUP_DIR/backup_manifest_${TIMESTAMP}.txt"
echo "========================================" >> "$BACKUP_DIR/backup_manifest_${TIMESTAMP}.txt"
echo "备份文件校验和:" >> "$BACKUP_DIR/backup_manifest_${TIMESTAMP}.txt"
find "$BACKUP_DIR" -type f -exec md5sum {} \; >> "$BACKUP_DIR/backup_manifest_${TIMESTAMP}.txt"
echo "========================================"
echo "备份全部完成!"
echo "备份目录:$BACKUP_DIR"
echo "========================================"
四、数据迁移方案详解
4.1 数据库迁移流程
数据库迁移是Harbor升级中最关键的环节。Harbor使用内置的install.sh脚本自动执行数据库迁移,其底层依赖Go代码中的migration包。迁移脚本位于Harbor仓库的src/migration目录下,每个版本对应一组migration文件。
迁移的基本原理是:通过一个版本跟踪表记录当前数据库已经执行到哪个版本,升级时从当前版本开始,依次执行后续所有版本的migration脚本,直到达到目标版本。
下面演示如何手动触发数据库迁移:
# ==========================================
# 技术栈:Docker Compose + Shell Script
# 功能:手动执行Harbor数据库迁移
# ==========================================
#!/bin/bash
# --- 配置 ---
TARGET_VERSION="2.3.0" # 目标升级版本
BACKUP_DIR="/data/harbor/backup"
DB_CONTAINER="harbor-harbor-db"
CORE_CONTAINER="harbor-harbor-core"
echo "=== 开始数据库迁移流程 ==="
echo "目标版本:$TARGET_VERSION"
echo ""
# --- 第一步:停止Harbor服务 ---
echo "[1/5] 停止Harbor服务..."
cd /data/harbor
docker-compose down
# 确认所有容器已停止
if docker ps | grep -q harbor; then
echo " [WARN] 仍有Harbor容器在运行,强制停止..."
docker ps | grep harbor | awk '{print $1}' | xargs docker stop
fi
echo " [OK] 所有Harbor容器已停止"
# --- 第二步:检查迁移前置条件 ---
echo "[2/5] 检查迁移前置条件..."
# 确认备份文件存在
BACKUP_FILE=$(ls -t "$BACKUP_DIR/database/" | head -1)
if [ -z "$BACKUP_FILE" ]; then
echo " [FAIL] 未找到数据库备份文件,请先执行备份!"
exit 1
fi
echo " [OK] 使用备份文件:$BACKUP_FILE"
# --- 第三步:部署新版本组件 ---
echo "[3/5] 部署新版本Harbor组件..."
# 下载新版本安装脚本
wget -q -O install_harbor.sh \
"https://github.com/goharbor/harbor/releases/download/v${TARGET_VERSION}/harbor-online-install-${TARGET_VERSION}.tgz"
tar -xzf harbor-online-install-${TARGET_VERSION}.tgz
mv harbor "${TARGET_VERSION}_deploy"
cd "${TARGET_VERSION}_deploy"
# 复制配置文件
cp /data/harbor/harbor.yml ./harbor.yml
# 执行安装(此时会自动执行数据库迁移)
echo " 执行安装脚本,数据库迁移将自动触发..."
./install.sh --with-notary
echo " [OK] 新版本部署完成"
# --- 第四步:验证数据库迁移结果 ---
echo "[4/5] 验证数据库迁移..."
docker exec "$DB_CONTAINER" \
psql -U postgres -d harbor -c "SELECT version FROM migrations ORDER BY version DESC LIMIT 1;"
echo " [OK] 数据库迁移验证完成"
# --- 第五步:启动服务并验证 ---
echo "[5/5] 启动Harbor服务..."
cd /data/harbor
docker-compose up -d
# 等待服务启动
echo " 等待30秒让服务完全启动..."
sleep 30
# 验证服务状态
curl -s -k -u "admin:Harbor12345" \
"https://your-harbor-address/api/v2.0/ping"
if [ $? -eq 0 ]; then
echo " [OK] Harbor服务启动成功!"
else
echo " [FAIL] Harbor服务启动失败,请检查日志!"
docker logs harbor-harbor-core
fi
echo "=== 数据库迁移流程完成 ==="
4.2 镜像数据迁移
镜像数据通常存储在文件系统(默认)或对象存储(如MinIO、S3)中。对于使用本地文件系统存储的场景,镜像数据直接保存在/data/harbor/registry/storage目录下,升级过程中不需要额外迁移,只需确保目录挂载路径不变即可。
对于使用MinIO等对象存储的场景,迁移过程需要特别注意:
# ==========================================
# 技术栈:Docker Compose + Shell Script
# 功能:MinIO对象存储场景下的镜像数据迁移
# ==========================================
#!/bin/bash
# --- 配置 ---
OLD_STORAGE="/data/harbor/registry/storage" # 旧存储路径
NEW_STORAGE="/data/harbor/new-registry/storage" # 新存储路径
MINIO_ENDPOINT="http://minio-service:9000"
MINIO_ACCESS_KEY="minio_access_key"
MINIO_SECRET_KEY="minio_secret_key"
MINIO_BUCKET="harbor-registry"
echo "=== MinIO镜像数据迁移 ==="
# --- 步骤1:同步现有数据 ---
echo "[1/4] 同步现有镜像数据到MinIO..."
# 安装mc工具(MinIO客户端)
wget -q https://dl.min.io/client/mc/release/linux-amd64/mc -O /usr/local/bin/mc
chmod +x /usr/local/bin/mc
# 配置MinIO alias
mc alias set harbor-minio \
"$MINIO_ENDPOINT" \
"$MINIO_ACCESS_KEY" \
"$MINIO_SECRET_KEY"
# 创建bucket(如果不存在)
mc mb --ignore-existing "harbor-minio/$MINIO_BUCKET"
# 递归复制本地存储到MinIO
echo " 正在复制镜像数据,这可能需要一些时间..."
mc cp -r "$OLD_STORAGE/" "harbor-minio/$MINIO_BUCKET/"
echo " [OK] 镜像数据同步完成"
# --- 步骤2:验证数据完整性 ---
echo "[2/4] 验证数据完整性..."
# 统计本地文件数量
LOCAL_COUNT=$(find "$OLD_STORAGE" -type f | wc -l)
echo " 本地文件数量:$LOCAL_COUNT"
# 统计MinIO文件数量
MINIO_COUNT=$(mc ls -r --json "harbor-minio/$MINIO_BUCKET/" | python3 -c "
import sys, json
count = 0
for line in sys.stdin:
try:
data = json.loads(line)
if not data.get('dir', False):
count += 1
except:
pass
print(count)
")
echo " MinIO文件数量:$MINIO_COUNT"
if [ "$LOCAL_COUNT" -eq "$MINIO_COUNT" ]; then
echo " [OK] 数据完整性验证通过"
else
echo " [WARN] 文件数量不匹配,请检查!"
fi
# --- 步骤3:更新Harbor配置指向新存储 ---
echo "[3/4] 更新Harbor配置..."
# 修改harbor.yml中的存储配置
sed -i "s|storage_provider: filesystem|storage_provider: minio|" harbor.yml
sed -i "s|rootpath: storage|rootpath: /harbor-registry|" harbor.yml
# --- 步骤4:重启Harbor服务 ---
echo "[4/4] 重启Harbor服务..."
docker-compose down
docker-compose up -d
echo " [OK] 服务已重启,请验证镜像访问是否正常"
4.3 数据库迁移回滚方案
万一升级失败,需要能够快速回滚。回滚的核心思路是:用升级前备份的数据库恢复,重新部署旧版本组件。
# ==========================================
# 技术栈:Docker Compose + Shell Script
# 功能:Harbor升级失败后的数据回滚脚本
# ==========================================
#!/bin/bash
# --- 配置 ---
BACKUP_DIR="/data/harbor/backup"
DB_CONTAINER="harbor-harbor-db"
OLD_VERSION_DIR="/data/harbor/harbor_2.2.0_deploy" # 旧版本部署目录
DATABASE_NAME="harbor"
DATABASE_USER="postgres"
echo "=== Harbor数据回滚开始 ==="
# --- 步骤1:停止所有服务 ---
echo "[1/5] 停止所有Harbor服务..."
cd /data/harbor
docker-compose down
# --- 步骤2:恢复数据库 ---
echo "[2/5] 恢复数据库备份..."
# 找到最新的备份文件
BACKUP_FILE=$(ls -t "$BACKUP_DIR/database/" | head -1)
if [ -z "$BACKUP_FILE" ]; then
echo " [FAIL] 未找到数据库备份文件!"
exit 1
fi
# 删除当前数据库并恢复
docker exec "$DB_CONTAINER" \
dropdb -h 127.0.0.1 -U "$DATABASE_USER" --if-exists "$DATABASE_NAME"
docker exec "$DB_CONTAINER" \
createdb -h 127.0.0.1 -U "$DATABASE_USER" "$DATABASE_NAME"
# 将备份文件导入
docker cp "$BACKUP_DIR/database/$BACKUP_FILE" "$DB_CONTAINER:/tmp/"
docker exec "$DB_CONTAINER" \
psql -h 127.0.0.1 -U "$DATABASE_USER" -d "$DATABASE_NAME" \
-f "/tmp/$BACKUP_FILE"
echo " [OK] 数据库恢复完成"
# --- 步骤3:重新部署旧版本 ---
echo "[3/5] 重新部署旧版本Harbor..."
cd "$OLD_VERSION_DIR"
cp /data/harbor/harbor.yml ./harbor.yml
./install.sh --with-notary
echo " [OK] 旧版本部署完成"
# --- 步骤4:恢复配置文件 ---
echo "[4/5] 恢复配置文件..."
cp "$BACKUP_DIR/config/docker-compose_*.yml" /data/harbor/docker-compose.yml
cp "$BACKUP_DIR/config/harbor_*.yml" /data/harbor/harbor.yml
# --- 步骤5:启动服务 ---
echo "[5/5] 启动Harbor服务..."
cd /data/harbor
docker-compose up -d
sleep 30
curl -s -k -u "admin:Harbor12345" \
"https://your-harbor-address/api/v2.0/ping"
if [ $? -eq 0 ]; then
echo " [OK] 回滚成功!Harbor已恢复到旧版本"
else
echo " [FAIL] 回滚后服务异常,请检查日志!"
fi
echo "=== Harbor数据回滚完成 ==="
五、关联技术深入解析
5.1 Docker Registry存储驱动
Harbor底层使用Docker Registry作为镜像存储引擎,而Docker Registry支持多种存储驱动:本地文件系统、S3、Azure Blob、GCS、MinIO等。理解存储驱动对数据迁移至关重要。
# ==========================================
# 技术栈:Docker Compose
# 功能:Harbor中不同存储驱动的配置文件对比
# ==========================================
# 配置文件位置:/data/harbor/harbor.yml
# 以下展示不同存储驱动的完整配置示例
# --- 方式1:本地文件系统存储(默认) ---
# 适合中小规模部署,数据存储在服务器本地磁盘
registry:
storage:
filesystem:
rootdirectory: /storage # 数据存储根目录
cache:
blobdescriptor: inmemory # 使用内存缓存blob描述符
blobdescriptor: v2 # blob描述符版本
# --- 方式2:MinIO对象存储 ---
# 适合中等规模部署,数据存储在MinIO集群中
registry:
storage:
minio:
accesskey: your_access_key # MinIO访问密钥
secretkey: your_secret_key # MinIO秘密密钥
bucket: harbor-registry # 存储bucket名称
endpoint: minio-service:9000 # MinIO服务地址
secure: false # 是否使用HTTPS
region: us-east-1 # 区域配置
rootdirectory: /registry # 根目录路径
cache:
blobdescriptor: redis # 使用Redis缓存
redis:
addr: redis-service:6379 # Redis地址
db: 2 # Redis数据库索引
# --- 方式3:AWS S3对象存储 ---
# 适合大规模生产环境,数据存储在AWS S3中
registry:
storage:
s3:
region: us-east-1 # S3区域
bucket: harbor-registry-bucket # S3 bucket名称
accesskey: AKIAIOSFODNN7EXAMPLE # S3访问密钥
secretkey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY # S3秘密密钥
encrypt: true # 是否加密存储
secure: true # 是否使用HTTPS
v4auth: true # 使用V4认证
rootdirectory: /registry # 根目录路径
5.2 Harbor的数据库Migration机制
Harbor使用Go语言编写了数据库迁移框架,位于src/server/migration包中。每个版本对应一组migration文件,命名规则为[版本号]_[描述].go。迁移框架通过版本跟踪表确保每个migration只被执行一次。
// ==========================================
// 技术栈:Go语言(Harbor源码级参考)
// 功能:理解Harbor数据库迁移机制的核心逻辑
// ==========================================
package migration
import (
"database/sql"
"fmt"
"sort"
)
// Migration 表示一个数据库迁移步骤
type Migration struct {
Version int // 迁移版本号,按升序排列
Name string // 迁移名称描述
Up func(db *sql.DB) error // 执行升级的函数
Down func(db *sql.DB) error // 执行回滚的函数(可选)
}
// MigrationManager 管理所有迁移步骤
type MigrationManager struct {
db *sql.DB
migrations []*Migration
currentVer int // 当前已执行的最高版本号
}
// NewMigrationManager 创建迁移管理器实例
func NewMigrationManager(db *sql.DB) *MigrationManager {
return &MigrationManager{
db: db,
migrations: make([]*Migration, 0),
}
}
// Add 注册一个迁移步骤
func (m *MigrationManager) Add(version int, name string, up func(*sql.DB) error, down func(*sql.DB) error) {
m.migrations = append(m.migrations, &Migration{
Version: version,
Name: name,
Up: up,
Down: down,
})
}
// Migrate 执行所有待执行的迁移
func (m *MigrationManager) Migrate() error {
// 按版本号升序排序
sort.Slice(m.migrations, func(i, j int) bool {
return m.migrations[i].Version < m.migrations[j].Version
})
// 查询当前数据库已执行的最高版本
err := m.db.QueryRow("SELECT MAX(version) FROM migrations").Scan(&m.currentVer)
if err != nil && err != sql.ErrNoRows {
return fmt.Errorf("查询当前版本失败: %v", err)
}
// 创建版本跟踪表(如果不存在)
_, err = m.db.Exec(`CREATE TABLE IF NOT EXISTS migrations (
version INTEGER PRIMARY KEY,
name VARCHAR(255),
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`)
if err != nil {
return fmt.Errorf("创建版本跟踪表失败: %v", err)
}
// 遍历所有迁移,执行版本号大于当前版本的所有迁移
for _, migration := range m.migrations {
if migration.Version <= m.currentVer {
// 跳过已执行的迁移
continue
}
fmt.Printf("执行迁移: v%d - %s\n", migration.Version, migration.Name)
// 开启事务,确保迁移的原子性
tx, err := m.db.Begin()
if err != nil {
return fmt.Errorf("开启事务失败: %v", err)
}
// 执行升级操作
if err := migration.Up(m.db); err != nil {
tx.Rollback()
return fmt.Errorf("迁移v%d执行失败: %v", migration.Version, err)
}
// 记录版本
_, err = m.db.Exec(
"INSERT INTO migrations (version, name) VALUES (?, ?)",
migration.Version, migration.Name,
)
if err != nil {
tx.Rollback()
return fmt.Errorf("记录版本失败: %v", err)
}
// 提交事务
if err := tx.Commit(); err != nil {
return fmt.Errorf("提交事务失败: %v", err)
}
m.currentVer = migration.Version
}
fmt.Printf("迁移完成,当前版本: v%d\n", m.currentVer)
return nil
}
// Rollback 回滚到指定版本
func (m *MigrationManager) Rollback(targetVersion int) error {
// 按版本号降序排序
sort.Slice(m.migrations, func(i, j int) bool {
return m.migrations[i].Version > m.migrations[j].Version
})
// 回滚所有高于目标版本的迁移
for _, migration := range m.migrations {
if migration.Version <= targetVersion || migration.Version > m.currentVer {
continue
}
if migration.Down == nil {
return fmt.Errorf("迁移v%d没有提供回滚方法", migration.Version)
}
fmt.Printf("回滚迁移: v%d - %s\n", migration.Version, migration.Name)
if err := migration.Down(m.db); err != nil {
return fmt.Errorf("回滚v%d失败: %v", migration.Version, err)
}
_, _ = m.db.Exec("DELETE FROM migrations WHERE version = ?", migration.Version)
m.currentVer = targetVersion
}
return nil
}
5.3 Harbor API与自动化升级
Harbor提供了丰富的RESTful API,可以用来查询系统状态、管理项目和镜像。在升级过程中,利用这些API可以实现自动化验证和监控。
# ==========================================
# 技术栈:Shell Script + curl + Python3
# 功能:使用Harbor API进行升级后的自动化验证
# ==========================================
#!/bin/bash
HARBOR_URL="https://your-harbor-address"
ADMIN_USER="admin"
ADMIN_PASS="Harbor12345"
echo "=== Harbor升级后自动化验证 ==="
# --- 验证1:服务健康检查 ---
echo "[1/6] 检查服务健康状态..."
PING_RESULT=$(curl -s -k -u "${ADMIN_USER}:${ADMIN_PASS}" \
"${HARBOR_URL}/api/v2.0/ping")
if [ "$PING_RESULT" = "pong" ]; then
echo " [PASS] 服务健康检查通过"
else
echo " [FAIL] 服务健康检查失败"
exit 1
fi
# --- 验证2:版本信息确认 ---
echo "[2/6] 确认Harbor版本..."
SYSTEM_INFO=$(curl -s -k -u "${ADMIN_USER}:${ADMIN_PASS}" \
"${HARBOR_URL}/api/v2.0/systeminfo")
VERSION=$(echo "$SYSTEM_INFO" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(data.get('harbor_version', 'unknown'))
")
echo " 当前版本:$VERSION"
# --- 验证3:项目列表检查 ---
echo "[3/6] 检查项目数据..."
PROJECTS=$(curl -s -k -u "${ADMIN_USER}:${ADMIN_PASS}" \
"${HARBOR_URL}/api/v2.0/projects?page_size=100")
PROJECT_COUNT=$(echo "$PROJECTS" | python3 -c "
import sys, json
projects = json.load(sys.stdin)
print(len(projects))
")
echo " 项目数量:$PROJECT_COUNT"
# --- 验证4:镜像访问测试 ---
echo "[4/6] 测试镜像访问..."
FIRST_PROJECT=$(echo "$PROJECTS" | python3 -c "
import sys, json
projects = json.load(sys.stdin)
if len(projects) > 0:
print(projects[0]['name'])
else:
print('library')
")
REPOS=$(curl -s -k -u "${ADMIN_USER}:${ADMIN_PASS}" \
"${HARBOR_URL}/api/v2.0/projects/${FIRST_PROJECT}/repositories")
REPO_COUNT=$(echo "$REPOS" | python3 -c "
import sys, json
repos = json.load(sys.stdin)
print(len(repos))
")
echo " 项目 '$FIRST_PROJECT' 下的仓库数量:$REPO_COUNT"
# --- 验证5:用户登录测试 ---
echo "[5/6] 测试用户登录..."
LOGIN_RESULT=$(curl -s -k -c /tmp/harbor_cookies -X POST \
"${HARBOR_URL}/api/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${ADMIN_USER}\",\"password\":\"${ADMIN_PASS}\"}")
# 使用token访问API
TOKEN=$(echo "$LOGIN_RESULT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(data.get('token', ''))
" 2>/dev/null)
if [ -n "$TOKEN" ]; then
echo " [PASS] 用户登录验证通过"
else
echo " [FAIL] 用户登录验证失败"
fi
# --- 验证6:存储状态检查 ---
echo "[6/6] 检查存储状态..."
STORAGE_INFO=$(curl -s -k -u "${ADMIN_USER}:${ADMIN_PASS}" \
"${HARBOR_URL}/api/v2.0/systeminfo")
STORAGE_USED=$(echo "$STORAGE_INFO" | python3 -c "
import sys, json
data = json.load(sys.stdin)
used = data.get('registry_storage_used', 0)
total = data.get('registry_storage', 0)
print(f'已用: {used/1024/1024/1024:.2f}GB / 总计: {total/1024/1024/1024:.2f}GB')
")
echo " 存储使用:$STORAGE_USED"
echo ""
echo "=== 所有验证完成 ==="
六、应用场景分析
6.1 中小型企业的内部镜像仓库
很多中小型企业在内部搭建Harbor作为私有镜像仓库,管理开发团队构建的Docker镜像。这类场景的特点是:数据量不大(通常几十到几百GB),团队人数有限(几十人),对停机时间有一定容忍度但不希望完全中断。
升级策略建议:选择工作日下午的非高峰时段执行升级,提前通知开发团队暂停镜像推送操作。采用就地升级方式,先完整备份数据,然后逐级升级。升级过程中开发团队可以继续从仓库拉取镜像(只读模式),但暂停推送操作直到升级完成。
6.2 多集群镜像分发场景
大型企业中,Harbor通常作为多集群的镜像分发中心,向多个Kubernetes集群推送镜像。这类场景的特点是:数据量大(TB级别),对可用性要求极高,停机时间几乎为零。
升级策略建议:采用灰度升级策略。先在一台备用的Harbor实例上执行升级,验证通过后,再通过流量切换将生产流量迁移到新版本实例。数据库采用主从架构,升级期间可以在从库上执行迁移,验证无误后再提升为主库。
6.3 CI/CD流水线中的镜像仓库
在DevOps流水线中,Harbor作为持续集成和持续部署的关键组件,每次构建完成后自动推送镜像到Harbor。这类场景的特点是:频繁写入,对推送延迟敏感,依赖Harbor的自动化扫描和签名功能。
升级策略建议:在CI/CD系统维护窗口期间执行升级,提前配置流水线重试机制。升级完成后验证所有项目的镜像访问权限和扫描策略是否正确迁移。
七、技术优缺点
7.1 Harbor组件升级的优缺点
优点方面:
Harbor的组件化架构设计使得升级时可以逐个替换组件,不需要一次性停掉整个服务。官方提供的install.sh脚本内置了自动迁移机制,能大幅降低手动操作出错的风险。Harbor使用数据库版本跟踪表管理迁移,确保了迁移的幂等性和可追溯性。此外,Harbor社区活跃,新版本通常能在较短时间内修复升级过程中发现的问题。
缺点方面:
数据库迁移过程不可逆——一旦migration执行完成,很难回到之前的状态,必须依赖备份回滚。大版本升级可能需要经历多个中间版本,整个过程耗时较长。Harbor的组件之间版本耦合度高,不能单独升级某个组件而保持其他组件不变。升级过程中如果网络不稳定或服务器断电,可能导致数据不一致。
7.2 数据迁移方案的优缺点
优点方面:
基于pg_dump的数据库备份方案简单可靠,适用于任何规模的部署。MinIO等对象存储的迁移可以通过mc工具批量同步,效率较高。Harbor的API接口完善,支持自动化验证脚本,能及时发现升级后数据异常。备份脚本可以同时覆盖数据库、配置和元数据三个维度,确保数据完整性。
缺点方面:
pg_dump备份对于大型数据库(数百GB级别)效率较低,恢复时间可能很长。MinIO迁移过程中需要确保网络带宽充足,否则同步耗时可能数小时。数据库迁移过程中服务完全不可用,无法做到零停机。如果备份文件损坏或存储介质故障,可能导致数据永久丢失。
八、注意事项
8.1 升级窗口规划
升级操作应在业务低峰期执行。对于内部开发团队使用的Harbor,建议选择周末或非工作日;对于生产环境使用的Harbor,建议选择凌晨2点到6点之间的时间窗口。升级前必须获得所有相关团队的通知确认,避免升级过程中有人正在进行镜像推送操作。
8.2 备份验证
备份完成后必须验证备份文件的有效性。不能只是检查文件是否存在,还要验证文件是否完整、可恢复。建议在升级前进行一次完整的备份恢复测试,确认备份能正常工作。如果备份恢复测试失败,必须重新备份后再执行升级。
8.3 网络与存储容量
升级过程中,数据库备份和镜像数据同步会消耗大量网络带宽和磁盘空间。升级前需要确认目标服务器的网络带宽足够(建议至少1Gbps),磁盘空间充足(至少预留备份数据的1.5倍空间)。对于跨机房升级场景,还需要考虑网络延迟和带宽限制。
8.4 监控与告警
升级过程中必须开启完善的监控告警。建议使用Prometheus和Grafana监控Harbor的各项指标:API响应时间、数据库连接数、镜像推送拉取延迟、存储使用率等。配置告警规则,当指标异常时立即通知运维人员。升级完成后至少观察30分钟,确认系统运行稳定。
8.5 升级文档记录
每次升级都必须详细记录升级过程,包括:升级前后的版本号、备份文件位置、执行的命令、遇到的问题及解决方案、验证结果等。这些记录对于后续排查问题和审计都有重要价值。建议将升级文档存放在团队的知识库中,形成标准操作流程。
九、文章总结
Harbor镜像仓库的组件升级看似简单,实际上涉及数据库迁移、镜像数据同步、配置文件更新、服务重启验证等多个环节。任何一个环节出错都可能导致升级失败甚至数据丢失。本文从Harbor的基本架构出发,详细讲解了升级策略的选择、升级前的准备工作、数据迁移的具体方案、升级失败后的回滚策略,以及升级过程中的注意事项。
核心要点可以归纳为以下几点:
第一,升级必须遵循逐级升级原则,不能跳过中间版本,否则数据库迁移脚本可能缺失。
第二,备份是一切的前提,没有备份就不要开始升级。备份必须覆盖数据库、配置文件和元数据三个维度。
第三,数据迁移是升级中最关键的环节,数据库迁移不可逆,必须做好回滚准备。
第四,升级完成后必须进行完整的验证,包括服务健康检查、版本确认、数据完整性验证、用户登录测试等。
第五,整个过程必须做好详细记录,形成可复用的标准操作流程。
最后提醒各位开发者,Harbor升级不是一锤子买卖,而是一个持续的过程。随着Harbor版本不断更新,定期升级是保持镜像仓库安全性和功能性的必要条件。建议在团队内部建立升级评估机制,每季度评估一次是否需要升级,及时跟进版本更新。
评论
围绕“Harbor镜像仓库的组件升级策略与升级过程中数据迁移方案”参与讨论