[TOC]
0x00 基础介绍 描述: 作为公司内部 PaaS toB 产品的打包发布人员,容器镜像对我们打工人而言就像是工地上的砖头 🧱,而我的一部分工作就是将这些砖头在各个仓库之间搬来搬去,最终将这些砖头打包放在产品的安装包中,形成一个完整的 PaaS 产品安装包。
Q: 在 PaaS (平台即服务)中的大家常说的ToB与ToC到底是什么?
ToC 面向普通用户服务, 主要是让用户体验感好,解决用户使用方面的问题记录,并返回给前后端开发。 ToB 是面向企业用户服务, 产品可用、其中最关键是让Boss使用Happly!
Q: 假如有如下场景,我们从dockerhub公共仓库中下载一个GB以上的镜像,到本地的私有仓库中,我想通常你会这样做先docker pull 到本地,然后使用docker tag更改为私有仓库地址加上镜像名称版本,最后再使用docker push上传镜像到私有仓库中,以供其它内网机器拉取并使用。虽然该方法是可行,但是如果有多个大于GB以上的镜像需要上传到私有仓库,每次都要先解压layer到本地,然后再压缩layer上传到私有仓库中,你能想象此过程花费的时间有多久吗? 对于我们运维工程师来说时间就是金钱,所以需想尽一切方法来节约时间成本,那有没有一种办法可以直接将 registry 上的 blob 复制到另一个 registry,中间过程不涉及对镜像 layer 的解压缩,这岂不美哉。
解决方案当然是存在的,如果你不想使用docker进行images镜像拉取上传,我们完成可以使用skope工具来完全替代 docker-cli 来搬运镜像,skopeo是一个命令行实用程序,可对容器映像和映像存储库执行各种操作。
什么是Skopeo? skopeo 使用 API V2 Registry,例如 Docker Registry、Atomic Registry、私有Registry、本地目录和本地 OCI 镜像目录。skopeo 不需要运行守护进程,它可以执行的操作包括:
通过各种存储机制复制镜像,例如,可以在不需要特权的情况下将镜像从一个Registry复制到另一个Registry
检测远程镜像并查看其属性,包括其图层,无需将镜像拉到本地
从镜像库中删除镜像
当存储库需要时,skopeo 可以传递适当的凭据和证书进行身份验证
镜像存储特点 根据 Robin 大佬在 《镜像仓库中镜像存储的原理解析》文章里得出的结论:
通过 Registry API 获得的两个镜像仓库中相同镜像的 manifest 信息完全相同。
两个镜像仓库中相同镜像的 manifest 信息的存储路径和内容完全相同。
两个镜像仓库中相同镜像的 blob 信息的存储路径和内容完全相同
项目信息 Github 官方地址: https://github.com/containers/skopeo Gitee mirror: https://gitee.com/mirrors/skopeo
0x01 安装编译 描述: Skopeo 官方安装&编译方式参考文档: https://github.com/containers/skopeo/blob/main/install.md
本节安装实践环境将在Ubuntu 20.04 LTS 以及 docker 20.10.12 中进行实践源码编译以及 apt 仓库源下载安装实践。
1.源码编译(静态) 描述: 要构建 skopeo 二进制文件您至少需要 Go 1.12 版本以上, 其次构建 skopeo 有两种方法,即在容器中
或者在本地环境中构建(安装环境较为复杂), 此处为了方便演示将采用容器方式进行编译构建。
[TOC]
0x00 基础介绍 描述: 作为公司内部 PaaS toB 产品的打包发布人员,容器镜像对我们打工人而言就像是工地上的砖头 🧱,而我的一部分工作就是将这些砖头在各个仓库之间搬来搬去,最终将这些砖头打包放在产品的安装包中,形成一个完整的 PaaS 产品安装包。
Q: 在 PaaS (平台即服务)中的大家常说的ToB与ToC到底是什么?
ToC 面向普通用户服务, 主要是让用户体验感好,解决用户使用方面的问题记录,并返回给前后端开发。 ToB 是面向企业用户服务, 产品可用、其中最关键是让Boss使用Happly!
Q: 假如有如下场景,我们从dockerhub公共仓库中下载一个GB以上的镜像,到本地的私有仓库中,我想通常你会这样做先docker pull 到本地,然后使用docker tag更改为私有仓库地址加上镜像名称版本,最后再使用docker push上传镜像到私有仓库中,以供其它内网机器拉取并使用。虽然该方法是可行,但是如果有多个大于GB以上的镜像需要上传到私有仓库,每次都要先解压layer到本地,然后再压缩layer上传到私有仓库中,你能想象此过程花费的时间有多久吗? 对于我们运维工程师来说时间就是金钱,所以需想尽一切方法来节约时间成本,那有没有一种办法可以直接将 registry 上的 blob 复制到另一个 registry,中间过程不涉及对镜像 layer 的解压缩,这岂不美哉。
解决方案当然是存在的,如果你不想使用docker进行images镜像拉取上传,我们完成可以使用skope工具来完全替代 docker-cli 来搬运镜像,skopeo是一个命令行实用程序,可对容器映像和映像存储库执行各种操作。
什么是Skopeo? skopeo 使用 API V2 Registry,例如 Docker Registry、Atomic Registry、私有Registry、本地目录和本地 OCI 镜像目录。skopeo 不需要运行守护进程,它可以执行的操作包括:
通过各种存储机制复制镜像,例如,可以在不需要特权的情况下将镜像从一个Registry复制到另一个Registry
检测远程镜像并查看其属性,包括其图层,无需将镜像拉到本地
从镜像库中删除镜像
当存储库需要时,skopeo 可以传递适当的凭据和证书进行身份验证
镜像存储特点 根据 Robin 大佬在 《镜像仓库中镜像存储的原理解析》文章里得出的结论:
通过 Registry API 获得的两个镜像仓库中相同镜像的 manifest 信息完全相同。
两个镜像仓库中相同镜像的 manifest 信息的存储路径和内容完全相同。
两个镜像仓库中相同镜像的 blob 信息的存储路径和内容完全相同
项目信息 Github 官方地址: https://github.com/containers/skopeo Gitee mirror: https://gitee.com/mirrors/skopeo
0x01 安装编译 描述: Skopeo 官方安装&编译方式参考文档: https://github.com/containers/skopeo/blob/main/install.md
本节安装实践环境将在Ubuntu 20.04 LTS 以及 docker 20.10.12 中进行实践源码编译以及 apt 仓库源下载安装实践。
1.源码编译(静态) 描述: 要构建 skopeo 二进制文件您至少需要 Go 1.12 版本以上, 其次构建 skopeo 有两种方法,即在容器中
或者在本地环境中构建(安装环境较为复杂), 此处为了方便演示将采用容器方式进行编译构建。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ git clone --depth=1 https://github.com/containers/skopeo.git $ cd skopeo $ sed -i 's#proxy.golang.org#https://goproxy.cn#g' skopeo/Makefile $ sudo apt-get install go-md2man $ whereis go-md2man $ BUILD_IMAGE="golang:latest" $ docker run --name skopeo-build -v $PWD :/src -v /usr/bin/go-md2man:/go/bin/go-md2man -w /src -e CGO_ENABLED=0 -e GOPROXY=https://goproxy.cn,direct ${BUILD_IMAGE} \ sh -c 'make BUILDTAGS=containers_image_openpgp GO_DYN_FLAGS=' $ cd ./bin $ ./skopeo --help
构建关键参数解析:
CGO_ENABLED=0 : 设置该环境变量, 禁用 CGO 会导致 Go 在可能的情况下更喜欢静态连接库,而不是动态链接到系统库 (解决可以在Ubuntu或者其它linux发行版中执行编译后二进制文件)。
GOPROXY=https://goproxy.cn,direct : Golong 依赖下载镜像站,加快go get依赖拉拉取。
BUILDTAGS=containers_image_openpgp : 设置该make参数消除了对libgpgme 及其配套库的依赖, Skopeo 的一些特性依赖于非 Go 库,例如 libgpgme 和 libdevmapper。
GO_DYN_FLAGS= : 清空该make参数 (否则会强制创建动态可执行文件)
2.分发包安装 描述: skopeo 可能已经打包在您的发行版中,此处以ubuntu 20.04为例进行安装。
1 2 3 4 5 6 7 8 9 10 11 sudo apt-get -y update sudo apt-get -y install skopeo . /etc/os-release echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID} / /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.listcurl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID} /Release.key | sudo apt-key add - sudo apt-get update sudo apt-get -y upgrade sudo apt-get -y install skopeo
3.容器安装运行 Skopeo 容器镜像可在 quay.io/skopeo/stable:latest 获得, 例如我们采用podman命令进行如下操作:1 podman run docker://quay.io/skopeo/stable:latest copy --help
0x02 快速上手 1.命令浅析 描述: skopen 是操作各种容器映像和容器映像仓库的工具,其使用方法及其可用命令如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ./skopeo --help Usage: skopeo [flags] skopeo [command ] Available Commands: copy delete help inspect list-tags login logout manifest-digest standalone-sign standalone-verify sync Flags: --command -timeout duration --debug --insecure-policy --override-arch ARCH --override-os OS --override-variant VARIANT --policy string --registries.d DIR --tmpdir string -h, --help help for skopeo -v, --version Version for Skopeo
2.Skopeo初体验 描述: 在使用体验skopeo之前,我们需要了解一哈 Skopeo 可以在那些图像和存储库类型上执行镜像操作(官网文档走一波):
Repository types
Describe
Example
containers-storage:docker-reference
适用于后端是 Podman, CRI-O, Buildah 的情况
containers-storage:
dir:path
适用于将manifest, layer tarballs 和 signatures 存储为单独文件的现有本地目录路径的情况
dir:/tmp/alpine:latest
docker://docker-reference
适用于Registry中实现”Docker Registry HTTP API V2”的镜像的情况
docker://harbor.weiyigeek.top/myblog:v2.8
docker-archive:path[:docker-reference]
适用于采用docker save
命令导出镜像以tar格式存储的文件的情况
docker-archive:alpine.tar
docker-daemon:docker-reference
适用于存储在 docker 守护进程内部存储中的图像的情况
docker-daemon:alpine:latest
oci:path:tag
适用于符合”Open Container Image Layout Specification”的目录中的图像标记
oci:alpine:latest
温馨提示: 同一个镜像存在的方式有可能不同,不同类型方式存储对镜像的 layer 处理的方式也不一样,。
测试环境说明 1 2 3 Docker 官方 hub 仓库 -> docker.io 私有 Harbor 仓库 -> harbor.weiyigeek.top 临时创建的本地仓库 -> 192.168.12.111:5000
说明: 上述仓库都是在Registry中支持Docker Registry HTTP API V2版本的。
Skopeo login/loout - 远程仓库 Auth 描述: 在使用 skopeo 前如果 src 或 dest 镜像是在 registry 仓库中的并且配置了非 public 的镜像需要相应的 auth 认证, 此时我们可以使用 docker login
或者 skopeo login
的方式登录到 registry 仓库,然后默认会在~/.docker
目录下生成 registry 登录配置文件 config.json ,该文件里保存了登录需要的验证信息,skopeo 拿到该验证信息才有权限往 registry push 镜像。
登陆认证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ skopeo login -u WeiyiGeek -p testpassword harbor.weiyigeek.top docker login -u WeiyiGeek docker.io docker login -u WeiyiGeek harbor.weiyigeek.top docker login -u anonymous -p anonymous 192.168.12.111:5000 cat ~/.docker/config.json { "auths" : { "192.168.12.111:5000" : { "auth" : "YW5v*******Q==" }, "harbor.weiyigeek.top" : { "auth" : "YWR*******LkA=" }, "https://index.docker.io/v1/" : { "auth" : "d2Vp**************kyZA==" } } }
注销认证 1 $ skopeo logout myregistrydomain.com:5000
温馨提示: 如果企业自建harbor仓库(一般都会设置自签证书)或者其它私有仓库配置证书,为了防止出错建议进行以下操作(正式环境请根据需要进行配置)。1 2 3 4 5 6 7 "insecure-registries" : ["harbor.weiyigeek.top" ,"192.168.12.111:5000" ] mkdir -vp /etc/docker/certs.d/harbor.weiyigeek.top cp -a /deployapp/harbor/harbor.pem /etc/docker/certs.d/harbor.weiyigeek.top/harbor.crt
温馨提示: 为了防止后续执行skopeo命令操作镜像时出错, 建议忽略policy策略和证书校验参数如下:1 2 3 --insecure-policy \ --src-tls-verify=false \ --dest-tls-verify=false \
Skopeo inspect - 检查存储库中的镜像 描述: skopeo 能够检查容器Registry上的存储库并获取图像层。检查命令获取存储库的清单,它能够向您显示有关整个存储库或标签的类似 docker inspect
的 json 输出。与 docker inspect 相比,此工具可帮助您在拉取存储库或标签之前收集有用的信息(使用磁盘空间), 检查命令可以向您显示给定存储库可用的标签、映像具有的标签、映像的创建日期和操作系统等。
支持传输的类型 : containers-storage, dir, docker, docker-archive, docker-daemon, oci, oci-archive, ostree, tarball
步骤 01.显示 busybox:latest 镜像的属性相关信息。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 skopeo inspect docker://docker.io/busybox:latest { "Name" : "docker.io/library/busybox" , "Digest" : "sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678" , "RepoTags" : [ "1" , "1-glibc" , "1-musl" , "1-ubuntu" , "1-uclibc" , "1.21-ubuntu" , "1.21.0-ubuntu" , ....... "unstable-uclibc" ], "Created" : "2021-12-30T19:19:41.006954958Z" , "DockerVersion" : "20.10.7" , "Labels" : null, "Architecture" : "amd64" , "Os" : "linux" , "Layers" : [ "sha256:5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa" ], "Env" : [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ] }
步骤 02.显示 busybox:latest 镜像的容器配置相关信息。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ skopeo inspect --config docker://docker.io/busybox:latest | jq { "created" : "2021-12-30T19:19:41.006954958Z" , "architecture" : "amd64" , "os" : "linux" , "config" : { "Env" : [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd" : [ "sh" ] }, "rootfs" : { "type" : "layers" , "diff_ids" : [ "sha256:01fd6df81c8ec7dd24bbbd72342671f41813f992999a3471b9d9cbc44ad88374" ] }, "history" : [ { "created" : "2021-12-30T19:19:40.833034683Z" , "created_by" : "/bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4cfde1f280177b458390ed5a6d1b54c6169522bc2c4d838e in / " }, { "created" : "2021-12-30T19:19:41.006954958Z" , "created_by" : "/bin/sh -c #(nop) CMD [\"sh\"]" , "empty_layer" : true } ] }
步骤 03.显示未经验证的图像 Digest(摘要)1 $ skopeo inspect --format "Name: {{.Name}} Digest: {{.Digest}}" docker://docker.io/busybox:latest
Skopeo copy - 仓库镜像拷贝 描述: skopeo 可以在各种存储机制之间复制容器镜像,支持包括容器仓库(The Quay, Docker Hub, OpenShift, GCR, ,Artifactory ...
)以及容器存储后端 (Podman, CRI-O, Docker
) 等、本地目录、本地 OCI-layout 目录。
例如,此处我从hub仓库复制busybox:latest
镜像到私有harbot仓库中,在从私有harbot仓库中拷贝到本地指定目录中。
步骤 01.从 regsitry A 到 registry B 复制 busybox:latest 镜像。1 2 3 4 5 6 skopeo copy --insecure-policy --src-tls-verify=false --dest-tls-verify=false --dest-authfile /root/.docker/config.json docker://docker.io/busybox:latest docker://harbor.weiyigeek.top/devops/busybox:latest
Tips: 由上述日志可以看到 skopeo 是直接从 registry 中 copy 镜像 layer 的 blob 文件,传输是镜像在 registry 中存储的原始格式。
步骤 02.从 registry B 复制 busybox:latest 镜像到本地 busybox:latest 目录中。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 skopeo copy --insecure-policy --src-tls-verify=false docker://harbor.weiyigeek.top/devops/busybox:latest dir:busybox:latest ls && tree busybox\:latest/ busybox:latest/ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa ├── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a ├── manifest.json └── version 0 directories, 4 files cat busybox\:latest/manifest.json { "schemaVersion" : 2, "mediaType" : "application/vnd.docker.distribution.manifest.v2+json" , "config" : { "mediaType" : "application/vnd.docker.container.image.v1+json" , "size" : 1456, "digest" : "sha256:beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a" }, "layers" : [ { "mediaType" : "application/vnd.docker.image.rootfs.diff.tar.gzip" , "size" : 772788, "digest" : "sha256:5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa" } ] } jq '.' busybox\:latest/beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a { "architecture" : "amd64" , "config" : { "Hostname" : "" , "Domainname" : "" , "User" : "" , "AttachStdin" : false , "AttachStdout" : false , "AttachStderr" : false , "Tty" : false , "OpenStdin" : false , "StdinOnce" : false , "Env" : [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd" : [ "sh" ], "Image" : "sha256:da658412c37aa24e561eb7e16c61bc82a9711340d8fb5cf1a8f39d8e96d7f723" , "Volumes" : null, "WorkingDir" : "" , "Entrypoint" : null, "OnBuild" : null, "Labels" : null }, ........ "history" : [ { "created" : "2021-12-30T19:19:40.833034683Z" , "created_by" : "/bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4cfde1f280177b458390ed5a6d1b54c6169522bc2c4d838e in / " }, { "created" : "2021-12-30T19:19:41.006954958Z" , "created_by" : "/bin/sh -c #(nop) CMD [\"sh\"]" , "empty_layer" : true } ], "os" : "linux" , "rootfs" : { "type" : "layers" , "diff_ids" : [ "sha256:01fd6df81c8ec7dd24bbbd72342671f41813f992999a3471b9d9cbc44ad88374" ] } }
步骤 03.将busybox:latest镜像从 registry B 复制到本地目录,并以 OCI 格式保存1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ skopeo copy --insecure-policy --src-tls-verify=false docker://harbor.weiyigeek.top/devops/busybox:latest oci:busybox-latest tree -h busybox-latest/
步骤 04.将 alpine:3.13.1
镜像从 docker 本地存储( /var/lib/docker/image) push 到 registry B中(实际上替代 docker push 功能)1 2 3 4 5 6 7 8 9 10 11 docker images alpine:3.13.1 skopeo copy --insecure-policy --dest-tls-verify=false --dest-authfile /root/.docker/config.json docker-daemon:alpine:3.13.1 docker://harbor.weiyigeek.top/devops/alpine:3.13.1
weiyigeek.top-harbor仓库中的alpine镜像
Skopeo sync - 镜像同步命令 描述: Skopeo sync可以在容器仓库和本地目录之间同步镜像,其功能类似于阿里云的 image-syncer (https://github.com/AliyunContainerService/image-syncer ) 工具, 实际上其比 image-syncer 更强大、灵活性更强一些,废话不多说实践为王。
skopeo sync 镜像同步文件示例:
步骤 01.将仓库中所有busybox镜像版本同步到本地目录。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ skopeo sync --insecure-policy --src-tls-verify=false --src docker --dest dir harbor.weiyigeek.top/devops/busybox /tmp $ tree -h /tmp/busybox:latest /tmp/busybox:latest ├── [755K] 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa ├── [1.4K] beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a ├── [ 527] manifest.json └── [ 33] version 0 directories, 4 files
步骤 02.从本地目录/tmp/
同步到 docker 的 hub 容器仓库中,此外我们可以通过浏览器看到 weiyigeek
用户下的 busybox
镜像 (https://hub.docker.com/u/weiyigeek)。 1 2 3 4 5 6 7 8 $ skopeo sync --insecure-policy --dest-tls-verify=false --src dir --dest docker /tmp weiyigeek
weiyigeek.top-hub-docker
步骤 03.从hub容器仓库中同步alpine-jenkins-jnlp:v2.285镜像到本地临时容器仓库中。1 2 3 4 5 6 $ skopeo sync --insecure-policy --src-tls-verify=false --dest-tls-verify=false --src docker --dest docker weiyigeek/alpine-jenkins-jnlp:v2.285 192.168.12.111:5000/
步骤 04.以配置文件方式进行同步, 首先我们需要准备一个需要同步的资源清单。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 cat <<'EOF' > skopeo-sync.yml registry.example.com: images: busybox: [] redis: - "1.0" - "2.0" - "sha256:111111" images-by-tag-regex: nginx: ^1\.13\.[12]-alpine-perl$ credentials: username: john password: this is a secret tls-verify: true cert-dir: /home/john/certs quay.io: tls-verify: false images: coreos/etcd: - latest EOF $ skopeo sync --src yaml --dest docker skopeo-sync.yml my-registry.local.lan/repo/
skopeo-sync.yml 文件中镜像匹配复制镜像说明:
registry.example.com/busybox : 所有版本的镜像.
registry.example.com/redis : 标记为“1.0”和“2.0”的图像以及带有摘要的图像”sha256:0000000000000000000000000000000011111111111111111111111111111111”.
registry.example.com/nginx : 图片标记为“1.13.1-alpine-perl”和“1.13.2-alpine-perl”.
quay.io/coreos/etcd : 拉取最新版本的镜像。
描述: 利用该命令我们可以列出 registry 上的某个镜像的所有 tag ,它是使用标准的 registry API 来获取镜像 tag。
简单示例:1 $ skopeo list-tags docker://harbor.weiyigeek.top/devops/busybox:latest
Skopeo delete - 删除仓库中镜像Tag 描述: 使用该命令我们可以删除镜像Tag,注意此处仅仅只是通过registry API 来删除镜像的 tag(即删除了 tag 对 manifests 文件的引用)并非真正将镜像删除掉,如果想要删除镜像的 layer 还是需要通过 registry GC 的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ skopeo delete docker://harbor.weiyigeek.top/devops/busybox:latest --debug curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X GET http://192.168.12.111:5000/v2/busybox/manifests/latest Docker-Content-Digest=$(curl -s --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X GET http://192.168.12.111:5000/v2/busybox/manifests/latest | grep "Docker-Content-Digest" | cut -d ' ' -f 2) curl -I -X DELETE http://192.168.12.111:5000/v2/busybox/manifests/${Docker-Content-Digest}
0x03 镜像同步最佳实践 本章节,主要参考我前同事木子博客(https://blog.k8s.li/skopeo.html)。
1.指定文本中镜像同步 假如,给你一个镜像列表 images-list.txt, 其格式如下, 我们可以直接采用shell脚本调用skopeo进行执行。1 2 3 4 5 6 7 8 cat <<'EOF' > images-list.txt kubesphere/kube-apiserver:v1.20.6 kubesphere/kube-scheduler:v1.20.6 kubesphere/kube-proxy:v1.20.6 kubesphere/kube-controller-manager:v1.20.6 kubesphere/kube-apiserver:v1.19.8 EOF
同步的shell脚本 skopeo-copy.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/bin/bash GREEN_COL="\\033[32;1m" RED_COL="\\033[1;31m" NORMAL_COL="\\033[0;39m" SOURCE_REGISTRY=$1 TARGET_REGISTRY=$2 : ${IMAGES_LIST_FILE:="images-list.txt"} : ${TARGET_REGISTRY:="hub.k8s.li"} : ${SOURCE_REGISTRY:="docker.io"} BLOBS_PATH="docker/registry/v2/blobs/sha256" REPO_PATH="docker/registry/v2/repositories" set -eo pipefailCURRENT_NUM=0 ALL_IMAGES="$(sed -n '/#/d;s/:/:/p' ${IMAGES_LIST_FILE} | sort -u) " TOTAL_NUMS=$(echo "${ALL_IMAGES} " | wc -l) skopeo_copy () { if skopeo copy --insecure-policy --src-tls-verify=false --dest-tls-verify=false \ --override-arch amd64 --override-os linux -q docker://$1 docker://$2 ; then echo -e "$GREEN_COL Progress: ${CURRENT_NUM} /${TOTAL_NUMS} sync $1 to $2 successful $NORMAL_COL " else echo -e "$RED_COL Progress: ${CURRENT_NUM} /${TOTAL_NUMS} sync $1 to $2 failed $NORMAL_COL " exit 2 fi } for image in ${ALL_IMAGES} ; do let CURRENT_NUM=${CURRENT_NUM} +1 skopeo_copy ${SOURCE_REGISTRY} /${image} ${TARGET_REGISTRY} /${image} done
执行命令和结果: 1 2 3 4 5 6 $ bash sync.sh docker.io localhost:5000 Progress: 1/143 sync docker.io/alpine:3.14 to localhost:5000/alpine:3.14 successful Progress: 2/143 sync docker.io/busybox:1.31.1 to localhost:5000/busybox:1.31.1 successful .... Progress: 142/143 sync docker.io/weaveworks/scope:1.13.0 to localhost:5000/weaveworks/scope:1.13.0 successful Progress: 143/143 sync docker.io/wordpress:4.8-apache to localhost:5000/wordpress:4.8-apache successful
2.使用 registry 存储特性同步 描述: 将镜像从 registry 中同步到本地目录,使用 registry 存储的特性,将本地目录中的镜像转换成 registry 存储的格式, 这样的好处就是可以去除一些 skopeo dir 中重复的 layers,减少镜像的总大小。
convert-images.sh1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #!/bin/bash set -eo pipefailGREEN_COL="\\033[32;1m" RED_COL="\\033[1;31m" NORMAL_COL="\\033[0;39m" SOURCE_REGISTRY=$1 TARGET_REGISTRY=$2 IMAGES_DIR=$2 : ${IMAGES_DIR:="images"} : ${IMAGES_LIST_FILE:="images-list.txt"} : ${SOURCE_REGISTRY:="docker.io"} : ${TARGET_REGISTRY:="hub.k8s.li"} BLOBS_PATH="docker/registry/v2/blobs/sha256" REPO_PATH="docker/registry/v2/repositories" CURRENT_NUM=0 ALL_IMAGES="$(sed -n '/#/d;s/:/:/p' ${IMAGES_LIST_FILE} | sort -u) " TOTAL_NUMS=$(echo "${ALL_IMAGES} " | wc -l) skopeo_sync () { if skopeo sync --insecure-policy --src-tls-verify=false --dest-tls-verify=false \ --override-arch amd64 --override-os linux --src docker --dest dir $1 $2 > /dev/null; then echo -e "$GREEN_COL Progress: ${CURRENT_NUM} /${TOTAL_NUMS} sync $1 to $2 successful $NORMAL_COL " else echo -e "$RED_COL Progress: ${CURRENT_NUM} /${TOTAL_NUMS} sync $1 to $2 failed $NORMAL_COL " exit 2 fi } convert_images () { rm -rf ${IMAGES_DIR} ; mkdir -p ${IMAGES_DIR} for image in ${ALL_IMAGES} ; do let CURRENT_NUM=${CURRENT_NUM} +1 image_name=${image%%:*} image_tag=${image##*:} image_repo=${image%%/*} skopeo_sync ${SOURCE_REGISTRY} /${image} ${IMAGES_DIR} /${image_repo} manifest="${IMAGES_DIR} /${image} /manifest.json" manifest_sha256=$(sha256sum ${manifest} | awk '{print $1}' ) mkdir -p ${BLOBS_PATH} /${manifest_sha256:0:2} /${manifest_sha256} ln -f ${manifest} ${BLOBS_PATH} /${manifest_sha256:0:2} /${manifest_sha256} /data mkdir -p ${REPO_PATH} /${image_name} /{_uploads,_layers,_manifests} mkdir -p ${REPO_PATH} /${image_name} /_manifests/revisions/sha256/${manifest_sha256} mkdir -p ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /{current,index/sha256} mkdir -p ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256} echo -n "sha256:${manifest_sha256} " > ${REPO_PATH} /${image_name} /_manifests/revisions/sha256/${manifest_sha256} /link echo -n "sha256:${manifest_sha256} " > ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /current/link echo -n "sha256:${manifest_sha256} " > ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256} /link for layer in $(sed '/v1Compatibility/d' ${manifest} | grep -Eo "\b[a-f0-9]{64}\b" ); do mkdir -p ${BLOBS_PATH} /${layer:0:2} /${layer} mkdir -p ${REPO_PATH} /${image_name} /_layers/sha256/${layer} echo -n "sha256:${layer} " > ${REPO_PATH} /${image_name} /_layers/sha256/${layer} /link ln -f ${IMAGES_DIR} /${image} /${layer} ${BLOBS_PATH} /${layer:0:2} /${layer} /data done done } convert_images
install.sh : 使用这个脚本将 registry 存储中的镜像转换成 skopeo dir 的方式,然后再将镜像同步到 registry 中。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/bin/bash REGISTRY_DOMAIN="harbor.k8s.li" REGISTRY_PATH="/var/lib/registry" cd ${REGISTRY_PATH} gen_skopeo_dir () { BLOB_DIR="docker/registry/v2/blobs/sha256" REPO_DIR="docker/registry/v2/repositories" SKOPEO_DIR="docker/skopeo" for image in $(find ${REPO_DIR} -type d -name "current" ); do name=$(echo ${image} | awk -F '/' '{print $5"/"$6":"$9}' ) link=$(cat ${image} /link | sed 's/sha256://' ) mfs="${BLOB_DIR} /${link:0:2} /${link} /data" mkdir -p "${SKOPEO_DIR} /${name} " ln ${mfs} ${SKOPEO_DIR} /${name} /manifest.json layers=$(grep -Eo "\b[a-f0-9]{64}\b" ${mfs} | sort -n | uniq) for layer in ${layers} ; do ln ${BLOB_DIR} /${layer:0:2} /${layer} /data ${SKOPEO_DIR} /${name} /${layer} done done } sync_image () { for project in $(ls ${SKOPEO_DIR} ); do skopeo sync --insecure-policy --src-tls-verify=false --dest-tls-verify=false \ --src dir --dest docker ${SKOPEO_DIR} /${project} ${REGISTRY_DOMAIN} /${project} done } gen_skopeo_dir
温馨提示: 此种方式是有些复杂对于大镜像的复制是推荐的, 而对于一些小镜像且显得多余。
3.从 registry 存储中 select 出镜像进行同步 描述: 先将镜像同步到一个 registry 中,再将镜像从 registry 存储中捞出来,该 registry 可以当作一个镜像存储的池子,我们使用 Linux 中硬链接的特性将镜像”复制”一份出来,然后再打一个 tar 包, 这样做的好处就是每次打包镜像的时候都能复用历史的镜像数据,而且性能极快。
步骤 01.先将镜像同步到一个固定的 registry 中。1 $ bash skopeo-copy.sh docker.io localhost:5000
步骤 02.使用该脚本将镜像从 registry 存储中捞出来1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #!/bin/bash set -eo pipefailIMAGES_LIST="$1 " REGISTRY_PATH="$2 " OUTPUT_DIR="$3 " BLOB_DIR="docker/registry/v2/blobs/sha256" REPO_DIR="docker/registry/v2/repositories" if [ -d ${OUTPUT_DIR} ];then rm -rf ${OUTPUT_DIR} ; fi mkdir -p ${OUTPUT_DIR} for image in $(find ${IMAGES_LIST} -type f -name "*.list" | xargs grep -Ev '^#|^/' | grep ':' ); do image_name=${image%%:*} image_tag=${image##*:} tag_link=${REGISTRY_PATH} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /current/link manifest_sha256=$(sed 's/sha256://' ${tag_link} ) manifest=${REGISTRY_PATH} /${BLOB_DIR} /${manifest_sha256:0:2} /${manifest_sha256} /data mkdir -p ${OUTPUT_DIR} /${BLOB_DIR} /${manifest_sha256:0:2} /${manifest_sha256} ln -f ${manifest} ${OUTPUT_DIR} /${BLOB_DIR} /${manifest_sha256:0:2} /${manifest_sha256} /data mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /{_uploads,_layers,_manifests} mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/revisions/sha256/${manifest_sha256} mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /{current,index/sha256} mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256} echo -n "sha256:${manifest_sha256} " > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /current/link echo -n "sha256:${manifest_sha256} " > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/revisions/sha256/${manifest_sha256} /link echo -n "sha256:${manifest_sha256} " > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256} /link for layer in $(sed '/v1Compatibility/d' ${manifest} | grep -Eo '\b[a-f0-9]{64}\b' | sort -u); do mkdir -p ${OUTPUT_DIR} /${BLOB_DIR} /${layer:0:2} /${layer} mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_layers/sha256/${layer} ln -f ${BLOB_DIR} /${layer:0:2} /${layer} /data ${OUTPUT_DIR} /${BLOB_DIR} /${layer:0:2} /${layer} /data echo -n "sha256:${layer} " > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_layers/sha256/${layer} /link done done