Harbor 迁移指南:从 2.6.4 到 2.12

目录

迁移说明

本指南描述了如何将 Harbor 从版本 2.6.4 升级到版本 2.12。考虑到版本差距较大和升级的稳定性,我们采用数据迁移的方法进行升级。此方法的优点包括:

  1. 避免了多个中间版本升级的复杂性
  2. 在最小修改的情况下重用现有的数据库、Redis 和存储

I. 数据备份

数据库备份

export INSTANCE_NAME=<harbor 实例名称> INSTANCE_NAMESPACE=<harbor 实例命名空间>

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 数据通常较大,不建议从 Pod 中复制。您需要根据对应的存储类型定位存储目录。

您可以通过以下方式从 harbor 实例的 yaml 中获取 Registry 配置:

export INSTANCE_NAME=<harbor 实例名称> INSTANCE_NAMESPACE=<harbor 实例命名空间>

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

主机路径存储

如果原始实例使用主机路径存储,配置如下:

persistence:
  hostPath:
    registry:
      host:
        nodeName: <节点名称>
        path: <registry 存储路径>

进入节点上的目录以备份数据。

PVC 或存储类

如果原始实例使用存储类,则 PVC 的名称固定为:<实例名称>-harbor-registry

如果原始实例使用 PVC 存储,配置如下:

helmValues:
  persistence:
    persistentVolumeClaim:
      registry:
        existingClaim: <registry pvc 名称>

您可以使用以下命令查看 PVC 的具体信息:

export INSTANCE_NAMESPACE=<harbor 实例命名空间>

kubectl -n ${INSTANCE_NAMESPACE} get pvc <registry pvc 名称> -o yaml

您需要参考 PVC 存储类的文档来寻找数据目录。

II. 停止旧的 Harbor 服务

首先,从 Operator Hub 页面卸载 harbor 操作员,然后按以下命令缩减旧的 harbor 服务:

export INSTANCE_NAME=<harbor 实例名称> INSTANCE_NAMESPACE=<harbor 实例命名空间>

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

注意:请勿停止数据库组件,因为我们需要对其进行迁移

III. 数据库迁移

首先,通过以下命令获取实例的 yaml 配置:

export INSTANCE_NAME=<harbor 实例名称> INSTANCE_NAMESPACE=<harbor 实例命名空间>

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

数据库配置如下:

database:
  type: external
  external:
    existingSecret: <数据库密码秘密>
    host: <数据库主机>
    port: <数据库端口>
    username: <数据库用户名>

然后创建一个任务来执行数据库迁移:

export POSTGRESQL_HOST=<数据库主机> POSTGRESQL_PORT=<数据库端口> POSTGRESQL_USERNAME=<数据库用户名> POSTGRESQL_PASSWORD=<数据库密码> POSTGRESQL_SSLMODE="require"
export INSTANCE_NAMESPACE=<harbor 实例命名空间>

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: docker-mirrors.alauda.cn/mgle/standalone-db-migrator:v2.12.0
        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]: 正在将数据迁移到最新的模式...
2024-12-23T02:43:05Z [INFO] [/cmd/standalone-db-migrator/main.go:54]: 数据库信息: postgres://harbor@test-6-database:5432/registry?sslmode=require
2024-12-23T02:43:05Z [INFO] [/common/dao/base.go:67]: 注册数据库: type-PostgreSQL host-test-6-database port-5432 database-registry sslmode-"require"
2024-12-23T02:43:05Z [INFO] [/common/dao/base.go:72]: 注册数据库完成
2024-12-23T02:43:05Z [INFO] [/common/dao/pgsql.go:135]: 正在为 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]: 迁移完成。数据库中的数据模式现在是最新的。

IV. 部署新的 Harbor 服务

新实例需要挂载原始实例的存储,因此需要在与原始实例相同的命名空间中。

获取原始实例配置

我们需要从原始实例获取配置并将其转换为新实例的配置,如下所示:

export INSTANCE_NAME=<harbor 实例名称> INSTANCE_NAMESPACE=<harbor 实例命名空间>

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

转换数据库配置

# 原始实例配置
database:
  type: external
  external:
    existingSecret: <数据库密码秘密>
    host: <数据库主机>
    port: <数据库端口>
    sslmode: <是否启用 SSL>
    username: <数据库用户名>
# 新实例配置
helmValues:
  database:
    type: external
    external:
      existingSecret: <数据库密码秘密>
      host: <数据库主机>
      port: <数据库端口>
      sslmode: <是否启用 SSL>
      username: <数据库用户名>

注意:数据库密码秘密键在旧实例和新实例之间是不同的。新实例需要使用 POSTGRES_PASSWORD 字段

data:
  POSTGRES_PASSWORD: <密码 base64>

转换 Redis 配置

# 原始实例配置
redis:
  type: external
  external:
    addr: rfr-harbor-test-5-redis:6379
    existingSecret: test-5-redis-password
# 新实例配置
helmValues:
  redis:
    type: external
    external:
      addr: <redis 主机>:<redis 端口>
      existingSecret: <redis 密码秘密>

注意:Redis 密码秘密键在旧实例和新实例之间是不同的。新实例需要使用 REDIS_PASSWORD 字段

data:
  REDIS_PASSWORD: <密码 base64>

转换主机路径存储配置

如果原始实例使用主机路径存储,配置如下:

# 原始实例配置
persistence:
  hostPath:
    jobservice:
      host:
        nodeName: <节点名称>
        path: <jobservice 存储路径>
    registry:
      host:
        nodeName: <节点名称>
        path: <registry 存储路径>
    trivy:
      host:
        nodeName: <节点名称>
        path: <trivy 存储路径>
# 新实例配置
helmValues:
  persistence:
    enabled: true
    hostPath:
      registry:
        path: <registry 存储路径>
      jobservice:
        path: <jobservice 存储路径>
      trivy:
        path: <trivy 存储路径>

  registry:
    nodeSelector:
      kubernetes.io/hostname: <节点名称>
  jobservice:
    nodeSelector:
      kubernetes.io/hostname: <节点名称>
  trivy:
    nodeSelector:
      kubernetes.io/hostname: <节点名称>

转换存储类存储配置

如果原始实例使用存储类,则 PVC 名称固定为:

  • jobservice: <实例名称>-harbor-jobservice
  • registry: <实例名称>-harbor-registry
  • trivy: data-<实例名称>-harbor-trivy-0
# 新实例配置
helmValues:
  persistence:
    enabled: true
    persistentVolumeClaim:
      registry:
        existingClaim: <实例名称>-harbor-jobservice
      jobservice:
        jobLog:
          existingClaim: <实例名称>-harbor-registry
      trivy:
        existingClaim: data-<实例名称>-harbor-trivy-0

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

# 原始实例配置
persistence:
  persistentVolumeClaim:
    registry:
      existingClaim: <registry pvc 名称>
    jobservice:
      existingClaim: <jobservice pvc 名称>
    trivy:
      existingClaim: <trivy pvc 名称>
# 新实例配置
helmValues:
  persistence:
    enabled: true
    persistentVolumeClaim:
      registry:
        existingClaim: <registry pvc 名称>
      jobservice:
        jobLog:
          existingClaim: <jobservice pvc 名称>
      trivy:
        existingClaim: <trivy pvc 名称>

转换 NodePort 配置

如果原始实例使用 NodePort,配置如下:

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

  externalURL: http://<节点端口 ip>:<节点端口号>

转换 Ingress 配置

如果原始实例使用域名,配置如下:

# 新实例 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 证书秘密>
    ingress:
      hosts:
        core: <域名>

  externalURL: https://<域名>

V. 验证

  1. 检查所有 Pod 状态:
export INSTANCE_NAME=<harbor 实例名称> INSTANCE_NAMESPACE=<harbor 实例命名空间>
kubectl get pods -n ${INSTANCE_NAMESPACE} -l release=${INSTANCE_NAME}
  1. 验证 Harbor 服务是否可访问:

    • 访问 Harbor Web UI,验证现有项目和镜像是否可见。
    • 测试 Docker 登录。
  2. 测试推送和拉取镜像。

  3. 在确认迁移正常后,您可以手动删除旧的 Harbor 实例。