Harbor 迁移指南:从 2.6.4 升级到 2.12

迁移说明

本指南介绍如何将 Harbor 从版本 2.6.4 升级到版本 2.12。鉴于版本跨度较大且升级稳定性考虑,我们采用数据迁移的方式进行升级。该方式的优势包括:

  1. 避免多版本中间升级的复杂性
  2. 复用 registry 存储数据,加快升级速度

整体迁移流程如下:

  1. 将 Helm Chart 数据迁移到 registry 存储。
  2. 备份 PostgreSQL 数据库并恢复到 PostgreSQL 14 实例。
  3. 停止旧的 Harbor 实例。
  4. 使用恢复后的 PostgreSQL 数据库和旧 Harbor 实例的原始 registry 存储部署新的 Harbor 实例。

迁移 Helm Chart 数据

Harbor 支持两种不同方式存储 Helm Chart 数据:

  1. 通过 OCI API 直接存储在 Harbor registry 存储中。
  2. 通过 Chartmuseum 的 API 存储在 Harbor 托管的 Chartmuseum 后端。

从 Harbor 2.6 开始,Chartmuseum 已被废弃,并在 Harbor 2.8 中移除。因此,如果旧实例使用的是 Chartmuseum,需要将 Chart 数据迁移到 registry 存储。

需求

迁移 Helm Chart 数据

该工具会复制 Helm Chart,但不会从 Chartmuseum 中删除它们。

将 Chart 数据迁移到 registry 存储:

export HARBOR_URL=<harbor url>
export HARBOR_USER=<harbor username>
export HARBOR_PASSWORD=<harbor password>
# 如果 Harbor 是 http,添加 -plain-http
# 如果 Harbor 是自签名 https,添加 -insecure
# 更多参数请运行 `podman run -ti --rm alaudadockerhub/chartmuseum2oci:v1.1.0-g41baf1e --help`
podman run -ti --rm alaudadockerhub/chartmuseum2oci:v1.1.0-g41baf1e --url $HARBOR_URL --username $HARBOR_USER --password $HARBOR_PASSWORD

预期输出:

2023/06/28 16:17:32 416 Helm charts to migrate from Chartmuseum to OCI
   100% |████████████████████████████████████████████████████████████████████████████████████████████| (416/416, 20 it/min)
2023/06/28 16:38:14 416 Helm charts successfully migrated from Chartmuseum to OCI

如果只想迁移部分数据到 registry 存储,可以根据文档按项目过滤数据

关于如何使用 OCI Helm Chart,请参考以下文档:

INFO

自 Harbor 2.8 起不再支持 Chartmuseum,相关支持已停止。

如果仍需使用 Chartmuseum 管理的 Chart 仓库,请联系我们获取支持。

迁移时长

迁移过程主要涉及数据库备份与恢复,迁移时间与数据库大小成正比,而非容器镜像占用的 Registry 存储空间大小。

基于对 7.6GB 数据库的测试,迁移通常耗时约 18 分钟(使用读 IOPS 6000、写 IOPS 2000 的存储性能)。

备份

执行以下命令,使用 pg_dump 备份旧实例的 pg 数据库:

export INSTANCE_NAME=<harbor instance name> INSTANCE_NAMESPACE=<harbor instance namespace>

kubectl -n ${INSTANCE_NAMESPACE} exec -it ${INSTANCE_NAME}-database-0 -- bash

# 执行备份命令
pg_dump -U postgres -d registry > /tmp/harbor_database.dump

# 将备份文件复制到本地
kubectl -n ${INSTANCE_NAMESPACE} cp ${INSTANCE_NAME}-database-0:/tmp/harbor_database.dump ./harbor_database.dump
Registry 数据备份为可选项

Registry 数据通常非常庞大,备份需要额外存储空间且耗时较长,难以实施。此外,Harbor 实例升级通常不会改变 registry 存储结构,因此新实例可直接使用旧实例的存储。

数据库迁移

从 Harbor 2.6 升级到 2.12 时,数据库版本也发生变化,由 PostgreSQL 12 升级到 PostgreSQL 14。因此,需要先将旧实例的数据库备份导入到新数据库(PostgreSQL 14)中,然后使用 Harbor 提供的数据迁移工具,将数据库结构和数据迁移为兼容 Harbor 2.12 的结构。

需求

准备一个运行 PostgreSQL 14 版本的新实例。将 Harbor 数据库备份恢复到该实例后,执行数据库结构迁移。

恢复数据库

将备份的数据库导入到新实例使用的数据库中。以下示例使用 psql 演示导入过程,具体导入方式请参考数据库厂商文档。

# 为新 PostgreSQL 实例创建新数据库。
# 示例中数据库名为 `registry`。
dropdb -U postgres registry
createdb -U postgres registry

# 旧实例使用 harbor 用户连接数据库,
# 但该用户可能在新实例数据库中不存在,
# 需将用户改为新 PostgreSQL 实例中存在的用户。
# 示例中使用 `postgres` 用户。
sed -i 's/OWNER TO harbor/OWNER TO postgres/g' /tmp/harbor_database.dump
psql -U postgres -d registry -f /tmp/harbor_database.dump
NOTE

由于数据库版本不一致,导入过程中可能出现关于不存在角色或函数的错误,可忽略。例如:

  • ERROR: role "admin" does not exist
  • ERROR: function metric_helpers.pg_stat_statements(boolean) does not exist
  • ERROR: schema "metric_helpers" already exists
  • ERROR: function "get_btree_bloat_approx" already exists with same argument types

数据库迁移

Harbor 2.6 的数据已导入新实例数据库。接下来,运行数据迁移任务,将数据库结构和数据迁移为兼容 Harbor 2.12 的结构。

设置新 PostgreSQL 实例信息,然后创建 Job 执行数据库迁移:

export POSTGRESQL_HOST=<database host>
export POSTGRESQL_PORT=<database port>
export POSTGRESQL_USERNAME=<database username>
export POSTGRESQL_PASSWORD=<database password>
# 如果旧实例使用 SSL,设置 SSL 模式为 `require`,否则设置为 `disable`。
export POSTGRESQL_SSLMODE="require"
export INSTANCE_NAMESPACE=<harbor instance namespace>
# 官方 migrator 镜像地址为 `https://hub.docker.com/r/mgle/standalone-db-migrator`,
# 可导入私有仓库并设置 MIGRATOR_IMAGE 环境变量使用。
export MIGRATOR_IMAGE=hub-mirrors.alauda.cn/mgle/standalone-db-migrator:v2.12.0

kubectl -n ${INSTANCE_NAMESPACE}  apply -f - << EOF
apiVersion: batch/v1
kind: Job
metadata:
  name: harbor-db-migrate
spec:
  backoffLimit: 5
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: harbor-migrate
        image: ${MIGRATOR_IMAGE}
        command: ["/harbor/migrate"]
        env:
        - name: POSTGRESQL_HOST
          value: "${POSTGRESQL_HOST}"
        - name: POSTGRESQL_PORT
          value: "${POSTGRESQL_PORT}"
        - name: POSTGRESQL_USERNAME
          value: "${POSTGRESQL_USERNAME}"
        - name: POSTGRESQL_PASSWORD
          value: "${POSTGRESQL_PASSWORD}"
        - name: POSTGRESQL_DATABASE
          value: "registry"
        - name: POSTGRESQL_SSLMODE
          value: "${POSTGRESQL_SSLMODE}"
EOF

任务完成后,可在 Job 日志中查看详细迁移进度和结果:

2024-12-23T02:43:05Z [INFO] [/cmd/standalone-db-migrator/main.go:53]: Migrating the data to latest schema...
2024-12-23T02:43:05Z [INFO] [/cmd/standalone-db-migrator/main.go:54]: DB info: postgres://harbor@test-6-database:5432/registry?sslmode=require
2024-12-23T02:43:05Z [INFO] [/common/dao/base.go:67]: Registering database: type-PostgreSQL host-test-6-database port-5432 database-registry sslmode-"require"
2024-12-23T02:43:05Z [INFO] [/common/dao/base.go:72]: Register database completed
2024-12-23T02:43:05Z [INFO] [/common/dao/pgsql.go:135]: Upgrading schema for pgsql ...
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 100/u 2.7.0_schema (184.508549ms)
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 110/u 2.8.0_schema (275.019668ms)
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 111/u 2.8.1_schema (286.508359ms)
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 120/u 2.9.0_schema (357.027979ms)
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 130/u 2.10.0_schema (378.349099ms)
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 140/u 2.11.0_schema (399.407785ms)
2024-12-23T02:43:05Z [INFO] [/go/pkg/mod/github.com/golang-migrate/migrate/v4@v4.18.1/migrate.go:765]: 150/u 2.12.0_schema (408.831695ms)
2024-12-23T02:43:05Z [INFO] [/cmd/standalone-db-migrator/main.go:63]: Migration done.  The data schema in DB is now update to date.

停止旧 Harbor 实例

从 Operator Hub 页面卸载 harbor operator,然后执行以下命令缩容旧 Harbor 实例:

export INSTANCE_NAME=<harbor instance name> INSTANCE_NAMESPACE=<harbor instance namespace>

kubectl -n ${INSTANCE_NAMESPACE} scale deployment -l release=${INSTANCE_NAME} --replicas=0
kubectl -n ${INSTANCE_NAMESPACE} scale statefulset -l release=${INSTANCE_NAME} --replicas=0
kubectl -n ${INSTANCE_NAMESPACE} delete service -l release=${INSTANCE_NAME}
kubectl -n ${INSTANCE_NAMESPACE} delete ingress -l release=${INSTANCE_NAME}

部署新 Harbor 实例

TIP

新实例需要挂载原实例的存储,因此需与原实例处于同一命名空间

请根据部署文档部署新实例,注意以下几点:

  1. 新实例需连接迁移后的 PostgreSQL 数据库(PostgreSQL 14)
  2. 不要复用旧 Harbor 实例的 Redis,需新建 Redis 实例
  3. registry 存储需使用旧实例的存储
  4. 访问方式需与旧实例保持一致,避免迁移完成后影响业务使用

以下章节介绍旧实例与新实例的配置迁移。

获取原实例配置

获取原实例配置并转换为新实例配置,命令如下:

export INSTANCE_NAME=<harbor instance name> INSTANCE_NAMESPACE=<harbor instance namespace>

helm -n ${INSTANCE_NAMESPACE} get values ${INSTANCE_NAME}

转换 Registry 存储配置

如果原实例使用 Host Path 存储部署,配置如下:

# 原实例配置
persistence:
  hostPath:
    registry:
      host:
        nodeName: <node name>
        path: <registry storage path>

迁移为新实例配置:

# 新实例配置
helmValues:
  persistence:
    enabled: true
    hostPath:
      registry:
        path: <registry storage path>

  registry:
    nodeSelector:
      kubernetes.io/hostname: <node name>

如果原实例使用存储类部署,PVC 名称固定:

  • registry PVC 名称:<旧实例名>-harbor-registry

新实例使用旧 PVC。

# 新实例配置
helmValues:
  persistence:
    enabled: true
    persistentVolumeClaim:
      registry:
        existingClaim: <旧实例名>-harbor-registry

如果原实例使用 PVC 部署,配置如下:

# 原实例配置
persistence:
  persistentVolumeClaim:
    registry:
      existingClaim: <registry pvc 名称>

新实例配置:

# 新实例配置
helmValues:
  persistence:
    enabled: true
    persistentVolumeClaim:
      registry:
        existingClaim: <registry pvc 名称>

转换访问方式配置

如果原实例使用 NodePort 部署,设置如下配置:

# 新实例配置
helmValues:
  expose:
    type: nodePort
    nodePort:
      name: harbor
      ports:
        http:
          port: 80
          nodePort: <node port 端口号>

  externalURL: http://<node port IP>:<node port 端口号>

如果原实例使用域名部署,设置如下配置:

# 新实例 http 配置
helmValues:
  expose:
    type: ingress
    tls:
      enabled: false
    ingress:
      hosts:
        core: <域名>

  externalURL: http://<域名>
# 新实例 https 配置
helmValues:
  expose:
    type: ingress
    tls:
      enabled: true
      certSource: "secret"
      secret:
        secretName: <tls 证书 secret>
    ingress:
      hosts:
        core: <域名>

  externalURL: https://<域名>

验证

  1. 检查所有 Pod 状态:

    export INSTANCE_NAME=<harbor instance name> INSTANCE_NAMESPACE=<harbor instance namespace>
    kubectl get pods -n ${INSTANCE_NAMESPACE} -l release=${INSTANCE_NAME}
  2. 验证 Harbor 服务可访问:

    • 访问 Harbor Web UI,确认已有项目和镜像可见
    • 测试 Podman 登录
  3. 测试镜像/Chart 的推送与拉取:

    • 测试镜像推送与拉取
    • 测试 OCI Chart 推送与拉取(可选)
  4. 确认迁移成功后,可在 DevOps Toolchain/Instances/ 页面手动删除旧 Harbor 实例。