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

迁移说明

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

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

整体迁移流程如下:

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

迁移 Chart 数据

Harbor 支持两种不同方式存储 Helm chart 数据:1. 通过 OCI API 直接存储在 Harbor registry 存储中;2. 通过 chartmuseum 的 API 存储在 Harbor 托管的 chartmuseum 后端。

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

需求

迁移 Chart 数据

该工具会复制 Helm charts,但不会从 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 charts,请参考以下文档:

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 实例信息,然后创建任务执行数据库迁移:

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

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

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. 新实例需连接已迁移的 PG 数据库(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 名称:<old instance name>-harbor-registry

新实例直接使用旧 PVC。

# 新实例配置
helmValues:
  persistence:
    enabled: true
    persistentVolumeClaim:
      registry:
        existingClaim: <old instance name>-harbor-registry

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

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

转换访问方式配置

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

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

  externalURL: http://<node port ip>:<node port number>

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

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

  externalURL: http://<domain name>
# 新实例 https 配置
helmValues:
  expose:
    type: ingress
    tls:
      enabled: true
      certSource: "secret"
      secret:
        secretName: <tls certificate secret>
    ingress:
      hosts:
        core: <domain name>

  externalURL: https://<domain name>

验证

  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 实例。