Github Action并行构建多架构docker

利用docker作为rust的开发环境,可以便捷升级,引入依赖,避免对本地环境的污染。为了便捷,使用了github action来构建docker镜像,实现了多架构和快速访问国外网络

问题

在原本的构建逻辑中,使用了build-push-action来实现多架构构建,但是因为其原理是使用QUME来模拟arm进行串行构建,arm构建非常慢,在我构建四个架构的情况下运行时间超出了action的六小时限制

因此需要想办法实现并行构建

方案一

action提供了matrix,可以通过提供不同的变量实现并行

简单配置一下

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
dev:
runs-on: ubuntu-20.04
permissions: write-all
needs: [version]
if: needs.version.outputs.u == 'true'
strategy:
matrix:
platform: [linux/386, linux/amd64, linux/arm/v7, linux/arm64]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout Runtime
run: |
git clone https://gist.github.com/inkroom/501548078a930c6f3bd98ea257409648 runtime
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to the Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/${{ github.repository_owner }}/rust
${{ secrets.DOCKER_HUB_USERNAME }}/rust
tags: |
type=raw,value=${{ needs.version.outputs.ve }}
labels: |
org.opencontainers.image.description=rust开发环境-${{ needs.version.outputs.ve }}
org.opencontainers.image.title=rust-${{ needs.version.outputs.ve }}
- name: Build Docker image
uses: docker/build-push-action@v4
with:
context: runtime
file: runtime/Dockerfile.rust
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: ${{ matrix.platform }}
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
RUST_VERSION=${{ needs.version.outputs.ve }}


结果就成了每构建成功一个架构,docker hub里面对应tag就会被替换成新镜像,之前push的就会丢失

方案二

之前的方案之所以会这样,是因为分开构建的镜像不被认为属于同一个tag

所以尝试使用manifast实现镜像合并

将不同架构push到不同的tag,然后使用命令实现合并

新增一个combine任务

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
combine:
runs-on: ubuntu-20.04
permissions: write-all
needs: [version, dev]
# 直接push 会导致 新推送覆盖旧推送,所以只能分开推送到不同tag,最后才采取合并
if: needs.version.outputs.u == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Create Manifest
run: |
docker manifest create --insecure ghcr.io/${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ghcr.io/${{ github.repository_owner }}/rust:amd64 ghcr.io/${{ github.repository_owner }}/rust:386 ghcr.io/${{ github.repository_owner }}/rust:arm-v7 ghcr.io/${{ github.repository_owner }}/rust:arm64

docker manifest annotate ghcr.io/${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ghcr.io/${{ github.repository_owner }}/rust:amd64 --os linux --arch amd64
docker manifest annotate ghcr.io/${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ghcr.io/${{ github.repository_owner }}/rust:386 --os linux --arch 386
docker manifest annotate ghcr.io/${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ghcr.io/${{ github.repository_owner }}/rust:arm-v7 --os linux --arch arm --variant v7
docker manifest annotate ghcr.io/${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ghcr.io/${{ github.repository_owner }}/rust:arm64 --os linux --arch arm64
docker manifest push --insecure ghcr.io/${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }}


docker manifest create --insecure ${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ${{ github.repository_owner }}/rust:amd64 ${{ github.repository_owner }}/rust:386 ${{ github.repository_owner }}/rust:arm-v7 ${{ github.repository_owner }}/rust:arm64

docker manifest annotate ${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ${{ github.repository_owner }}/rust:amd64 --os linux --arch amd64
docker manifest annotate ${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ${{ github.repository_owner }}/rust:386 --os linux --arch 386
docker manifest annotate ${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ${{ github.repository_owner }}/rust:arm-v7 --os linux --arch arm --variant v7
docker manifest annotate ${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }} ${{ github.repository_owner }}/rust:arm64 --os linux --arch arm64

docker manifest push --insecure ${{ github.repository_owner }}/rust:${{ needs.version.outputs.ve }}


执行后出现以下错误

1
ghcr.io/inkroomtemp/rust:amd64 is a manifest list

猜测是因为构建出来的镜像已经是一个 manifest list,而非manifest,所以不能再套娃了

方案三

研究一番后,发现build-push-action在readme里提供了一个并行构建的样例


结果是不能push by digest

到处都找不到这个配置的文档,只能放弃

方案四

这个方案来自issues

思路是并行构建产物作为缓存,combine里引入缓存,完整执行一次普通构建,因为有缓存,所以速度很快,不会超出时间限制

有一点需要处理,每个matrix构建出的缓存都是一个整体,后面使用的时候需要进行一次合并,就是把缓存目录合并,同时把index.json进行合并


结果很不理想,构建依然没用上缓存

最终方案

研究半天,最终还是回到方案三

发现最开始的错误原因是我在初步构建的时候给了tag和image,这个和outputs里的配置冲突了,去掉就可以正常使用了

2024-04-03

使用中发现一个奇怪的问题:使用qemu模拟i386,dockerfile中使用命令arch返回的是x86_64,但是包管理器和命令dpkg --print-architecture能返回i386

没找到原因和解决方案,只能不构建这个架构


Github Action并行构建多架构docker
http://blog.inkroom.cn/2023/07/24/3GFZG3W.html
作者
inkbox
发布于
2023年7月24日
许可协议