作者:崔力强
在之前使用云效进行镜像构建时,你可能会遇到如下问题:
- 只能对一个 Registy 进行 login,如果 FROM 的 image 和 push 的 image registry 不相同,则无法实现。
- 镜像构建缓存基于 oss 实现,有 5G 的大小限制,如果缓存过大,就无法使用。且每次全量上传缓存,耗时较长。为了解决上述问题,云效流水线对镜像构建能力做了如下增强:
- 提供了镜像 Registry 的 Login 和镜像构建并推送两类步骤,以提高灵活性。比如,支持 FROM Image 和构建出来的目标 Image 来自不同的私有镜像仓库的场景。
- 基于 buildx 提供标准、透明且灵活的的镜像缓存管理机制。如果是私有构建机器,可以选择本地缓存;如果是在公共构建集群,则可以采用远端的 Registry 作为缓存存储。
- 基于 buildx 提供了多架构镜像构建能力,简单的添加一个参数,就可以同时构建 amd64 和 arm64 两种 CPU 架构的镜像。两个镜像共享同一个镜像地址,在不同架构的机器上拉取镜像时候,会自动拉取对应架构的镜像。
需要注意的是,为了使用上述新能力,需要在构建任务的构建环境中选择“指定容器构建”。如果是私有构建机器的话,需要选择在“默认 VM 环境”上构建。
使用了这两种配置之后,就可以选择到上面这些能力增强。
并且可以在步骤运行的日志中看到这些步骤所对应的代码库地址:
查看源码,就可以了解该步骤的实现细节。
镜像 Registry Login 与镜像构建的解耦
假设有这个场景,你有两个阿里云账号,A 和 B。
A 账号下有一个镜像作为镜像构建的基础镜像,是私有镜像,地址如下,其中包含了 Java 程序运行的基础软件。
registry.cn-beijing.aliyuncs.com/hub-mirrors/openjdk:8-jdk-alpine
B 账号下有一个镜像作为应用镜像,也是私有镜像,地址如下:
registry.cn-hangzhou.aliyuncs.com/yunxiao-demo/app1
该镜像的 Dockerfile 如下,来自代码库:<https://atomgit.com/flow-example/spring-boot/blob/master/Dockerfile5>
# 基础镜像来自A账号,构建出来的镜像 registry.cn-hangzhou.aliyuncs.com/yunxiao-demo/app1 会推送到B账号
FROM registry.cn-beijing.aliyuncs.com/hub-mirrors/openjdk:8-jdk-alpine
# 下面这行COPY是为了验证缓存是否生效用的
COPY deploy.sh deploy.sh
# 下面这行COPY,因为每次构建出来的jar包都不相同,因此不会走缓存
COPY target/application.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
在这个镜像构建步骤前,需要有一个 Java 构建的步骤来产生这个 target/application.jar 文件。
在老版的构建步骤则存在问题。在原有的镜像构建步骤中,docker login 和 docker build 是在同一个步骤中的进行 的,如下图所示:
login 和 build 两个动作耦合起来了,一个 build 只能搭配一个 login。考虑上述的场景,FROM 镜像和构建目标镜像来自不同的账号,且都是私有镜像,则无法实现。
使用新的步骤,就可以实现了,如下图的配置所示:
运行的日志如下,两个登录步骤都会在镜像构建步骤中生效。
使用 Registry 作为镜像缓存存储
在上面的例子中,日志的最后部分输出:
这表明构建缓存上传到我的 Registry 了,也就是:
registry.cn-hangzhou.aliyuncs.com/yunxiao-demo/app1:flow-docker-build-cache
这是镜像构建的默认配置,让我们回过头来再看下构建配置,进行确认:
这里选择了“远端缓存”,默认使用目标镜像,tag 是 flow-docker-build-cache。在下一次运行镜像构建时,就会重用这个缓存,然后基于这个缓存进行构建。让我们看下下一次的构建日志:
可以看到,构建尝试从 registry.cn-hangzhou.aliyuncs.com/yunxiao-demo/app1:flow-docker-build-cache 中拉取缓存,并且未修改的层会直接使用缓存。
在这个例子中,只有两个步骤,第一个步骤耗时不长,使用缓存与否并没有带来时间上的差异,且拉取缓存本身也是需要花时间的。因此需要结合拉取缓存的时间与缓存节省时间进行综合考量。
此外还可以通过使用 VPC Registry 地址来加速缓存的拉取和上传。由于云效在大陆地区只有北京的构建集群,因此如果你的目标镜像是在北京的,则可以使用 VPC 地址来替换公网地址来达到加速的效果。
对于上面的例子,目标镜像是在杭州的,在北京的云效公共构建集群上无法通过 VPC 地址来访问杭州的 Registry,该怎么办呢?
也是有办法的,修改构建配置,让缓存不要存储到默认的杭州目标 Registry 中,而是配存储到北京镜像 Registry 的 VPC 镜像地址。这里我们使用上面提到的北京的基础镜像地址作为缓存存储的目标,如下所示:
在远端缓存中填写地址如下:registry-vpc.cn-beijing.aliyuncs.com/hub-mirrors/openjdk:app1-flow-cache
表明在构建过程中产生的缓存会被存储到到这个镜像的特定的 tag,app1-flow-cache 上。表明这个 tag 是给 app1 这个应用的缓存使用的。
然后还需要做两个调整,使得构建过程中镜像拉取和镜像缓存存储都使用这个 VPC 地址,从而加快构建速度。
1)账号的 A 的 login 地址也要改成这个 VPC 的地址
2)Dockerfile 中的 FROM 也要改成这个 VPC 的 registry 地址
再次观察日志,可以看到构建过程会从 vpc 地址上传和下载缓存:
使用本机作为缓存存储
如果你使用私有构建机的话,则推荐使用“本地缓存”的选项。docker 构建会直接本地的 docker deamon 来进行构建,因此会使用本地缓存。
配置如下所示:
查看日志会发现,可以看到命令中没有指定特别的缓存方式,所以使用的默认值就是本地缓存。运行两次之后可以看到被缓存的层。
构建多架构镜像
一个镜像包含了 CPU 架构的属性,在 amd64 芯片的机器上构建产出的镜像在 arm64 的机器上将无法启动。如果你的镜像需要同时在 amd64 和 arm64 等不同的芯片架构上使用,那么可以轻松的在云效上构建出来多架构的镜像。
云效基于 buildx 提供多架构构建能力,只需要添加一个 --platform 的参数即可。基于上面的示例,我们只需修改配置如下:
添加“更多构建参数”--platform linux/amd64,linux/arm64
查看日志,可以看到每个构建步骤都会同时构建 amd64 和 arm64 两个版本:
在这次构建中,会产生一个镜像地址:registry.cn-hangzhou.aliyuncs.com/yunxiao-demo/app1:2024-09-19-12-24-56,但实际包含的是两个镜像。当从 arm64 的机器上进行拉取,则拉取的就是 arm64 版本的镜像,当从 amd64 的机器上进行拉取,则拉取的就是 amd64 版本的镜像。
总结
云效镜像构建能力的增强,得益于几个方面:
- 基于源构建的方式(<https://atomgit.com/flow-step)构建步骤,以便用户可以了解实现细节。如果有定制化的需求,可以fork这个代码库,修改后发布到自己的企业中来使用(https://help.aliyun.com/zh/yunxiao/user-guide/use-flow-cli-to-customize-development-steps)>
- 使用更细力度的原子能力的组合来覆盖更多的构建场景,同时提供任务模板,来降低配置的复杂度。
- 拥抱最新的构建技术和标准,提供更加灵活的构建参数配置能力,并大幅提高了构建缓存的可用性和上传下载的速度(<https://github.com/docker/buildx)>
欢迎点击此处,前往云效流水线 Flow(flow\.aliyun.com)进行体验!