当前位置: 首页 > 科技观察

DockerRegistry迁移到Harbor

时间:2023-03-14 21:44:10 科技观察

RegistryDockerDistribution的四种方式DockerDistribution是第一个实现打包、发布、存储和镜像分发的工具,起到dockerregistry的作用。(目前Distribution已捐赠给CNCF)。其中,DockerDistribution中的spec规范后来成为OCIdistribution-spec规范。可以认为DockerDistribution实现了大部分的OCI镜像分发规范,两者在很大程度上是兼容的。OCI的指导思想是先行业实践,再将这些实践总结成技术规范。因此,虽然OCI的distribution-spec规范还没有正式发布(目前版本是v1.0.0-rc1),但是基于DockerDistributiondocker的镜像仓库已经成为被广泛采用的方案,dockerregistryhttpapiv2已经成为事实上的标准。HarborHarbor也使用DockerDistribution(dockerregistry)作为后端镜像存储服务。在harbor2.0之前的版本中,大部分镜像相关的功能都是由DockerDistribution处理的。images、OCI等产品的元数据由harbor组件从docker组件中获取。登记处;harbor2.0版本之后,图片等OCI产品相关的元数据由harbor自己维护,当这些产品被PUSH时,元数据被写入harbor数据库。正因为如此,Harbor不再只是一个存储和管理镜像的服务,而是一个云原生的仓库服务,可以存储和管理各种符合OCI规范的工件,如HelmChart、CNAB、OPABundle等。DockerRegistry到Harbor回到本文主题:如何将dockerregistry中的镜像迁移到harbor?假设内网环境有两台机器,一台机器上运行dockerregistry,假设域名为registry.k8s.li。另一台机器正在运行harbor,假设域名是harbor.k8s.li。现在有5,000个镜像存储在dockerregistry中。Harbor刚刚部署完毕,里面还没有镜像。在没有磁盘和网络限制的情况下,如何高效地将dockerregistry中的镜像迁移到harbor?获取Registry中所有镜像的列表首先,我们需要在迁移前获取dockerregistry中的镜像列表,这样才能保证迁移后镜像不丢失。按照《 深入浅出容器镜像的一生》一文中提到的registry存储目录结构。在registry存储目录中,每个镜像的tag由current/index文件指向该tag镜像的manifests文件,所以我们可以通过遍历registry存储目录中的current/index文件,得到所有镜像的tag,以获取注册表中所有镜像的列表。注意这种方式只能获取带标签的图片,其他不带标签的图片无法获取。镜像列表可以通过以下命令在registry存储目录中获取:Harbor创建项目对于一个新部署的harbor,上面只会有一个默认的library项目,需要在dockerregistry中手动创建相应的项目在港口。dockerregistry中的镜像项目是registry存储目录中repositories下的目录名。拿到镜像列表并在harbor上完成相应项目的创建后,我们就可以进行正式的迁移工作了。根据不同的场景,可以采用以下解决方案:方案一:dockerretag方案一可能是大多数人第一个想到的方法,也是最简单粗暴的方法。就是把一台机器上dockerpull下的dockerregistry里面的镜像全部使用,然后dockerretag,再dockerpush到harbor。如果你看过我之前写的《深入浅出容器镜像的一生》和?,并且在日常生活中使用过skopeo,你肯定会认为这不是一个聪明的解决方案,因为dockerpull–>dockertag–>dockerpull会解压镜像层。对于只是将图像从一个注册表复制到另一个注册表,这些码头工人在此过程中做了很多无用的工作。详细原理可以参考上面两篇文章,这里不再赘述。为了追求高效率,我们没有使用dockerretag方案。再来看看解决方案二:解决方案二:skopeo在《镜像搬运工 skopeo 初体验》一文中介绍过可以使用skopeocopy直接将原始blob从一个registry复制到另一个registry,这里期间不会涉及镜像层解压操作.从性能和耗时上来说,比使用docker要高效很多。使用skopeocopy和使用skopeosyncdocker和skopeo本质上都是通过registry的HTTPAPI下载和上传镜像。在这个过程中,仍然有大量的HTTP请求。如果使用HTTPS,也涉及到HTTPS。在加解密的过程中,这期间有很多无用的工作。那么有没有更好的办法呢?方案三:迁移存储目录文章开头提到了harbor的后端镜像存储也是使用dockerregistry。对于registry,只要是使用DockerDistributionV2,其后端存储目录结构是完全一样的。那为什么不直接打包复制registry存放目录,解压到harborregistry存放目录呢?这样一来,就可以保证所有的镜像都迁移完,一个也不会掉队。对于harbor1.x版本,直接将dockerregistry的存放目录迁移到harbor的registry存放目录,然后删除harbor的redis数据(因为harbor的redis缓存了镜像的元数据信息),重启harbor。重启harbor后,harbor会调用后端registry提取镜像的元数据信息,存储到redis中。这样就完成了迁移的工作。备份docker注册机上的registry存放目录备份完成后,将docker.tarscp到harbor机器上,然后恢复harbor机器上的registry存放目录。迁移后可能会遇到镜像无法推送到harbor的问题。因为dockerregistry容器中registry存储目录的属主和所属组为root,而harborregistry容器中registry存储目录的属主和所属组为10000:10000,两者的权限不一样,这会导致harbor无法推送镜像。因此,迁移完成后,需要修改harborregistry目录的属主和所属组。解决方案4对于harbor2.x,因为harbor加强了Artifact的元数据管理能力,即向harbor推送或同步时元数据要写到harbor自己的数据库中。从harbor的角度来看,只要数据库中没有这个Artifact的manifest信息或者没有这个layer层的信息,harbor就会认为这个Artifact或者layer不存在,返回404错误。按照方案三直接解压dockerregistry存放目录到harborregistry存放目录的方法是行不通的。因为镜像是解压到registry存储的,虽然harbor的registry容器看起来是有镜像的,但是因为harbor数据库中没有镜像,所以harbor会认为没有镜像。所以现在看来??只能通过第二种方案,使用skopeo将图片一一推送到harbour。但是对于一些特定的场景,不能像方案二那样有dockerregistryHTTP服务,只能有dockerregistry压缩包。如何将dockerregistry存储目录下的镜像迁移到harbor2.0??中提到,skopeo支持的图片格式如下:需要注意的是,这几类图片的名称对应着图片的存在方式,以及图片的层次方式。不同的处理方式是不一样的。同样的,比如docker://存放在registry上;docker-daemon:存放在本地dockerpull;再比如docker-archive,它是通过dockersave得到的镜像;而dir:是保存在form中的镜像文件夹。同一个镜像有这几种存在方式,就像水有气态、液态、固态一样。可以这样理解,它们表达的是同一个镜像,只是存在方式不同而已。由于图片存放在registry存储目录,直接从文件系统以dir的形式读取图片理论上比方案二要好。虽然skopeo支持dir格式的图片,但是skopeo目前不支持直接使用registry存储directories,所以还是得想办法把dockerregistry存储目录下的每一个image都转成skopeodir格式。skopeodir那么先看看skopeodir长什么样子?为了测试方案的可行性,首先使用skopeo命令从dockerhub中拉取一个镜像并保存到dir。命令如下:使用tree命令查看alpine文件夹的目录结构,如下:从文件名和大小以及文件自省可以判断manifest文件对应的是镜像的manifests文件图像;ASCII文本类型的文件是镜像的imageconfig文件,包含了镜像的元数据信息。而另一个gzip压缩后的数据文件并不是经过gzip压缩过的image层。查看manifest文件的内容也再次印证了这个结论:image的config字段对应e50c909a8df2,文件类型为image.v1+json文本文件。图片的layer字段对应4c0d98bf9879,文件类型为.tar.gzipgzip压缩文件。从注册表存储目录中获取镜像,接下来就来到本文比较精彩的部分。如何从注册表存储中“捕获”图像并将其转换为skopeo支持的目录格式。首先获取镜像的manifests文件,从中可以获取镜像的所有blob文件。例如对于registry存储目录下的library/alpine:latestimage,在registry中存储如下:1.通过repositories/library/alpine/_manifests/tags/latest/获取alpineimagelasts的tagcurrent/linkfilemanifests文件的sha256值,然后去blobs中根据sha256值找到镜像的manifests文件;2、根据current/link文件中的sha256值在blobs目录下找到对应的文件,blobs目录下对应的manifests文件为blobs/sha256/39/39eda93d15866957feaee28f8fc5adb545276a64147445c64992ef69804dbf01/data;3、使用正则匹配过滤掉manifests文件中所有的sha256值,而这些sha256值对应的是imagemanifest文件和blobs目录下的images;4.layer可以得到blobs目录下镜像的所有layer和imageconfig文件,然后将这些文件组装成一个dir格式的image。这里使用cp从registry存储目录复制镜像。过程如下:最终图片格式如下:对比上面skopeo拷贝的dir文件夹,除了一个无关紧要的版本文件外,其他都一模一样。5、再次优化,将第4步的cp操作改为硬链接操作,可以大大减少磁盘的IO操作。注意:硬链接文件不能跨分区,所以必须和注册表存放目录在同一个分区。然后使用skopeocopy或者skopeosync把钓到的镜像推送到harbor使用skopeocopy使用skopeosync需要注意的是skopeosync的方法是同步项目级别,镜像的名字和tag对应的名字目录。其实修改skopeo的源码应该也能无缝支持注册表存放目录。对比总结以上方案:方案一:上手成本低,适用于大量镜像,无需安装skopeo,缺点是性能较差;方案二:适用于两个注册中心之间镜像的同步复制,比如将dockerhub中的一些公共镜像复制到公司内网的镜像仓库中。方案三:适用于镜像仓库之间的迁移。性能是所有方案中最好的。需要注意的是,如果目标镜像仓库是harbor2.x,则不能使用该方法。方案四:是方案三的折衷版本,为了适配Harbor2.0,需要将镜像重新推送到Harbor,所以性能比方案三差。