前言

公司软件和镜像仓是在天翼云上,8 台机器共用 20M 带宽,速度非常之慢,全国各地的客户都用这个镜像仓库,k8s 滚动更新用的是组里大哥写的容器管理平台(大部分用这个)和我写的无网更新小助手(少数断网的学校用的这个)。大哥写的容器管理平台做了15分钟的超时,也就是说15分钟没有完成整个更新流程(调度、下载、启动、探针 Ready)就会报异常,所以遇到同时更新的时候,经常被实施被拉去看更新异常原因,一去看异常基本上都是15分钟镜像没下载出来,实施不会看异常原因,也不会等镜像下载到本地了之后重新走更新流程(镜像拉取策略是 IfNotPresent,超时了等下载完成再重新更新就行,但是停了之后重新走流程已经下载的进度会丢),只会找运维看原因,每次让他们等镜像下完了再重新滚动更新也不懂,很无奈。

前段时间,隔壁组的大佬提出来可以用免费的镜像仓来加速,推荐了阿里、华为、天翼云的容器镜像加速服务(都不要钱),正好最近在做国产化适配,给容器上多架构镜像,就一起做了,解决下这个问题

思路

公司目前的 CI 是用 Jenkins +Shell 脚本做的,有一说一,用过 Github Action 和 drone 的插件之后感觉这玩意儿很垃圾,但是由于以下几个原因还确实不好换

  • 项目太多了迁移难度很大
  • 公司的代码仓是 GitLab ,里面大量用到了子组和群组,这玩意儿在各个 CI 里面支持都很拉,之前看过 drone 的 issues 里面说了原因,“子组和群组” 这个玩意儿是 GitLab 独有的,其它 git 仓库都没有,所以不认为做这个 feature 有普适性价值(提一点个人看法,子组和群组这玩意儿我认为是不应该使用的,应该用组织 Organization 来区分项目,很多 git 生态组件都不支持这玩意儿,我的 git 用的是 SourceGit ,也是不支持,为了能在公司仓用这个,我去改了他校验地址的代码自己构建了一个)
  • 代码仓库在云上,但是构建是在本地(本地服务器的性能比较高,云上的机器比较贵配置买的不是很好),导致 webhook 调不动,所以很多 CI 用不起来(Jenkins 是每分钟拉 git 比对有更新没来确定要不要触发构建)

多架构镜像要求 Harbor 版本要高才能支持,没记错的话好像是 2.5.2 的,公司的 harbor 仓是 1.9 的,所以需要升级,反正都升级了,就直接升级到 2.10 的最新版好了

镜像加速的话,利用 Harbor 的复制功能(事件启动),把 Jenkins 构建出来的镜像推上去就行,因为云上的带宽低,所以在本地搭建了一个 Harbor ,先从 Jenkins 推到这个推送仓,再从这个推送仓推到公司云上的仓库、阿里和华为的镜像加速仓库上去

同时还要做的,就是把公司仓上面的 200G+ 的历史构建镜像推到阿里和华为的仓库里面

实操

Harbor 仓升级

非常简单,只需要严格按照官方文档干就行,比如我想要升级到最新版本,就直接去看最新版本的文档,这里给个 2.10 版本的例子:Harbor docs | Upgrade Harbor and Migrate Data (goharbor.io)

里面写到,要升级到 v2.10 ,你得先升到 v2.8.0 之后的版本,以此类推,一点点看下去,直到找到包含当前版本 1.9 的就行,然后从最下面的版本按照文档一点点升级就行,不难

image-20240505150500904

Jenkins 多架构镜像构建

主要就是把 docker buildx 跑起来

这块稍有一点点麻烦,首先,你最好去把机器的内核升级到 5.x ,uname -a 可以看当前内核版本

image-20240505151212964

docker 版本的话,19.03+ 就可以,现在随便抓个发行版装 docker 应该都不会是问题

启用 docker 的实验性特性,修改以下位置的文件,并 systemctl daemon-reload && systemctl restart docker

docker 服务端

/etc/docker/daemon.json

{
"experimental": true
}

docker 客户端

~/.docker/config.json

{
"experimental": "enabled"
}

docker 改完之后,用下面的命令查看下 buildx 能用了不

docker buildx version

docker version

image-20240505152447373

还需要让系统支持多架构构建,这块主要是用 qemu 做的,这块你需要去看两个 repo ,不同机器可能需要改改里面参数

repo:

命令(先去看 repo,不建议直接复制)

docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker run --privileged --rm tonistiigi/binfmt --install all

都弄好之后,创建应该 buildx 的 builder,非 https 或自签证书的,需要提前做一个配置文件,后面如果修改,需要删掉重新来做,认证的话,docker login 就行,他会从 login 里面拿认证数据(其实就是从 ~/.docker/config.json 里面的 auth 拿的)

docker buildx create --name luckykeeper --use  --driver docker-container --config /root/buildkitd.toml

/root/buildkitd.toml 示例如下

[registry."harbor.test.com:10000"]
http = true
insecure = true

创建好之后,可以用 docker buildx ls 来查看,下面第二个就是我们创建的,可以看到 platform 很全

image-20240505153042258

最后,构建和推送示例如下,应该没有问题了

docker buildx build --platform linux/amd64,linux/arm64 -t harbor.test.com:10000/cloud/xxx:v1 . --push

docker buildx build --platform linux/amd64,linux/arm64 -t harbor.test.com:10000/cloud/xxx:v1 . --push

Jenkins 那边把 docker build 和 docker push 换成 docker buildx build --push 就把构建和推送一并搞定了

回到 Harbor 仓库,查看镜像,可以看到已经是多架构的了

image-20240505153405158 image-20240505153343377

阿里、华为镜像仓库推送

都需要注册一个账号,阿里的个人服务支持 300 个的免费项目,支持多架构但是不保证,华为的暂时没有限制,明确支持多架构,速度都非常快,个人推荐用华为云的

阿里:https://cr.console.aliyun.com/

华为:https://console.huaweicloud.com/swr/

阿里

阿里的支持选择项目的默认类型,为了能够方便拉取,配置的是公开

image-20240505154001243

创建 AccessKey 之后,在 harbor 仓库复制里面配置好(仓库管理添加仓库,复制管理添加推送规则),就能自动推送

先添加仓库,提供者选阿里 ACR,URL 填上面图片的公网地址

image-20240505154201271

复制管理里面,仓库选择刚刚配置好的仓库,因为是推送所以是 Push-based,触发选择时间驱动,因为 harbor 仓库和阿里的命名空间不同,所以需要仓库扁平化替换1级

image-20240505154344196

华为

华为的项目默认是私有的,在项目比较多的情况下,建议去调 api 自动设置项目公开,比较少的情况也可以手动去针对每个项目去设置

image-20240505154627020

推送设置和阿里类似,不再赘述,但是需要注意 Harbor 不会检测华为云的 AccessKey 是否正确,只要推送失败了才能判断配置有没有问题,但是阿里的不需要真实推送就能检测

小插曲

华为云 SDK 使用时遇到的问题

不要用 Golang SDK 去操作 SWR ,会变得不幸,不咋维护而且调不通,翻 issues ,22 年的问题,一直没人管……

我拿官方给的 SWR API demo 跑了下,不通,这很逆天。多说一嘴,之前用阿里云的 Go SDK ,就很好用

image-20240505155009031

还好我除了 Golang 还会写点儿别的,Python 不大喜欢写,dart 没有 SDK,所以用 C# SDK 搞定了,打了个 AOT 的包扔服务器上面跑了

Harbor 复制的问题

新推镜像的问题解决了,接下来是历史镜像了

Harbor 代码里面写死了单个镜像复制从开始到结束的时间不能超过 30 分钟,如果超过 30 分钟,即使复制成功了也会被强制失败,所以最开始从公司仓复制历史镜像到本地老是失败,不限制带宽会影响到生产环境的升级,限制带宽又会超时,最后想到,镜像仓是在石家庄天翼云,内网是能跑满速的,我先把镜像推到石家庄天翼云,再把镜像从天翼云拉到本地,再从本地 Harbor 推送到阿里和华为就 ok 了嘛

天翼云的 SWR 也是免费的,但是 Harbor 不支持,从 UI 和 URL 来看,怎么看都是华为 SWR 套皮,在 Harbor 里面,用华为 SWR 去加也能成功,但是,API 不同!用不了!

这位比华为云的 Golang SDK 更是逆天!

image-20240505155855182

天翼云的官方文档说道 “Harbor 迁移到天翼云,用这个工具: AliyunContainerService/image-syncer: Docker image synchronization tool for Docker Registry V2 based services (github.com)

但是去翻下这个的 issues ,他不支持 Harbor,只支持标准的 api。跑了下也确实不行,很难不怀疑这文档是 AI 生成的,逆天!

image-20240505160156650

而且天翼云的这个 SWR 只能生成一天的限时 token ,不能生成长期的,所以长久用肯定是没有办法用了的,只能临时过渡下,为了解决推送的问题,我自己写了个同步工具,来解决这个问题

如果你遇到了同样的问题,可以去看这两个库,是这个工具的核心组件

mittwald/goharbor-client: Go Client for the Harbor container registry (github.com)

google/go-containerregistry: Go library and CLIs for working with container registries (github.com)

天翼云的 SWR 逆天在于,他的 API 间歇性和标准的 docker registry HTTP API 不通,我写的工具的逻辑是,先调用标准 API ,如果通,就直接复制 Artifacts ,如果不通,我就调 Harbor API ,找到项目所有的 tag ,然后手动 pull 下来镜像再 push 上去,Harbor 推天翼云的时候这样,反过来的时候就用本地保存的项目数据来比对

看下面的图片,可以看到,repo 进度是 265、268、269 的是走标准 API 复制 Artifacts 的,但是 266、267 调用标准 API 就拿不到 tag ,观察了下能拿到 tag 和 拿不到 tag 的项目,也没看出有什么区别,另外哪怕把项目设置成公开也是不能,一样拿不到 MANIFEST

image-20240505160927531