我们有一个 docker compose 部署的 harbor,随着时间的推移,存储的镜像越来越多,磁盘空间已经捉襟见肘。急需迁移到更大的存储空间去。harbor 支持众多的后端存储,所支持的后端存储类型和配置方法点击 Configuring a registry | CNCF DistributionHarbor docs | Configure the Harbor YML File 查看。

我这里使用的是minio(兼容s3)作为后端存储。

假设我的 harbor 的访问地址是:https://hub.kops.cc

配置方法

修改 harbor.yml 文件:

storage_service:
  s3:
    accesskey: minioadmin
    secretkey: minioadmin
    regionendpoint: http://127.0.0.1:9000
    bucket: harbor
    multipartcopythresholdsize: "5368709120"

执行 ./prepare 脚本使配置生效

./prepare

docker-compose down

docker-compose up -d

测试一下拉镜像,如果配置生效,这时候镜像是拉不下来的,因为我们存储空间改变了,新的存储空间没有这个镜像的数据。

harbor 原来是有 library/redis:5.0 这个镜像的,此时已经不能拉取。

docker pull harbor.kops.cc/library/redis:5.0   
Error response from daemon: manifest for harbor.kops.cc/library/redis:5.0 not found: manifest unknown: manifest unknown

迁移镜像数据

harbor后端的镜像仓库使用的是 CNCF Distribution,原来应该叫 Docker Registry。因此我们迁移有几种方案,你选一种合适的方案即可。

如果你执行了上面的步骤,已经修改了 harbor 的存储后端为s3,那么原来的镜像此时已经无法拉取。那么有没有办法能让镜像能拉取呢?自然是有的。我们临时部署一个 registry,并挂载 harbor 的 registry 存储目录过去,那么就可以拉取到原来的镜像了。

假设你的harbor数据存储在 /data/harbor,那么 harbor registry 的数据目录就是 /data/harbor/registry,将这个目录挂载到临时 registry 的 /var/lib/registry,用 docker 的话,命令如下:

docker run -d --name temp-registry -p 5000:5000 -v /data/harbor/registry:/var/lib/registry registry:2

测试一下,用临时registry拉取镜像看看:

docker pull localhost:5000/library/redis:5.0

docker pull localhost:5000/library/redis:5.0

5.0: Pulling from library/redis

Digest: sha256:6411e32d8386718b01ca27f6754847fd880c653c215467d0cd3d7876f213eb94

Status: Image is up to date for localhost:5000/library/redis:5.0

好,可以拉取。

这时候,你应该要清楚:

  • localhost:5000 能拉取到之前的镜像
  • hub.kops.cc 已经不能拉取镜像了,其他的数据都是还在的,harbor web也都能正常登录和查看

重新打标法(retag)

这个方案最原始,要求能访问原来的镜像(用上面提到的临时registry)。

迁移步骤如下:

  • 拉取镜像(localhost:5000)
  • 重新打标(docker tag localhost:5000/xxx hub.kops.cc/xxx)
  • 推送镜像(hub.kops.cc)

此步骤可以用一个 shell 脚本自动完成,需要装有 jq

#!/bin/bash

# 临时 Registry 地址
TEMP_REGISTRY_URL="http://localhost:5000"
# Harbor 地址
HARBOR_URL="http://hub.kops.cc"

# 获取所有镜像列表
images=$(curl -s "${TEMP_REGISTRY_URL}/v2/_catalog" | jq -r '.repositories[]')

# 遍历每个镜像
for image in $images; do
  echo "镜像: $image"

  # 获取每个镜像的 tag 列表
  tags=$(curl -s "${TEMP_REGISTRY_URL}/v2/${image}/tags/list" | jq -r '.tags[]?' )

  if [ -z "$tags" ]; then
    echo "  (无 tag)"
  else
    for tag in $tags; do
      old_tag=${TEMP_REGISTRY_URL##http*://}/${image}:${tag}
      new_tag=${HARBOR_URL##http*://}/${image}:${tag}
      docker pull ${old_tag}
      docker tag ${old_tag} ${new_tag}
      # 需要提前docker login
      docker push ${new_tag}
      docker rmi ${ols_tag} ${new_tag}
    done
  fi

  echo ""
done

skopeo copy 法

这个方法是重新打标法的升级版,重新打标需要pull/retag/push三个步骤,pull要接压缩,push要重新压缩,效率较低。

而 skopeo 在传输过程中,不会对镜像的层解包再封包,效率上提高了很多。

  • skopeo copy

    skopeo copy docker://localhost:5000/xxx docker://hub.kops.cc/xxx
    • 如果不是安全连接(https),有两个参数:--src-tls-verify=false --dest-tls-verify=false,分别标记源仓和目标仓是否是安全连接
    • 账号密码:--src-username --src-password --dest-username --dest-password,分别给源仓和目标仓配置用户名和密码

文件系统迁移法

我在想,能不能直接将 Harbor Registry 数据目录原封不动同步到 s3 去。我试了一下,确实存储的目录结构和本地文件系统是一模一样的。有了这个理论基础,那么就可以直接将整个目录的数据同步到 s3。

  • 首先,要先配置 ~/.s3cfg 配置文件,配置方法不会的可以找找其他资料,这里不过多赘述
  • 假设 Harbor Registry 的数据目录在 /data/harbor/registry
  • 假设桶名是 harbor
s3cmd sync /data/harbor/registry/docker s3://harbor/docker/

确保能看到这样的目录结构就说明是对的:

s3cmd ls s3://harbor/docker/registry/v2/
                  DIR  s3://harbor/docker/registry/v2/blobs/
                  DIR  s3://harbor/docker/registry/v2/repositories/

此法最简单高效,也不需要临时 Registry。在这种换存储系统的场景使用是最为合适的。只需要提前sync到s3,然后择机再做一次增量sync,然后再改配置重启harbor即可,整个停机窗口也可以控制在相当短的时间。

  • 增量 sync 加上 --delete 参数

标签: none

添加新评论